Merge "Revert "Get fastboot working on OS X""
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..c6f6251
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,6 @@
+filegroup {
+ name: "android_filesystem_config_header",
+ srcs: ["include/private/android_filesystem_config.h"],
+}
+
+subdirs = ["*"]
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b3661e4..5b5eff4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -56,3 +56,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/lmkd_intermediates/import_includes)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libsysutils_intermediates/import_includes)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/grep $(PRODUCT_OUT)/system/bin/toolbox)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/gatekeeper.$(TARGET_DEVICE).so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.$(TARGET_DEVICE).so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/vendor)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
diff --git a/adb/.clang-format b/adb/.clang-format
index 0395c8e..fc4eb1b 100644
--- a/adb/.clang-format
+++ b/adb/.clang-format
@@ -2,6 +2,8 @@
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
+AccessModifierOffset: -2
+ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
IndentWidth: 4
diff --git a/adb/Android.mk b/adb/Android.mk
index e2d0bb1..b444afa 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -5,12 +5,6 @@
LOCAL_PATH:= $(call my-dir)
-ifeq ($(HOST_OS),windows)
- adb_host_clang := false # libc++ for mingw not ready yet.
-else
- adb_host_clang := true
-endif
-
adb_host_sanitize :=
adb_target_sanitize :=
@@ -20,17 +14,24 @@
-Wall -Wextra -Werror \
-Wno-unused-parameter \
-Wno-missing-field-initializers \
+ -Wvla \
-DADB_REVISION='"$(adb_version)"' \
+ADB_COMMON_linux_CFLAGS := \
+ -Wexit-time-destructors \
+
+ADB_COMMON_darwin_CFLAGS := \
+ -Wexit-time-destructors \
+
# Define windows.h and tchar.h Unicode preprocessor symbols so that
# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
# build if you accidentally pass char*. Fix by calling like:
-# CreateFileW(widen(utf8).c_str()).
+# std::wstring path_wide;
+# if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ }
+# CreateFileW(path_wide.c_str());
ADB_COMMON_windows_CFLAGS := \
-DUNICODE=1 -D_UNICODE=1 \
-ADB_COMMON_CFLAGS += $(ADB_COMMON_$(HOST_OS)_CFLAGS)
-
# libadb
# =========================================================
@@ -42,18 +43,27 @@
# get enough of adb in here that we no longer need minadb. https://b/17626262
LIBADB_SRC_FILES := \
adb.cpp \
- adb_auth.cpp \
adb_io.cpp \
adb_listeners.cpp \
+ adb_trace.cpp \
adb_utils.cpp \
+ fdevent.cpp \
sockets.cpp \
+ socket_spec.cpp \
+ sysdeps/errno.cpp \
transport.cpp \
transport_local.cpp \
transport_usb.cpp \
LIBADB_TEST_SRCS := \
adb_io_test.cpp \
+ adb_listeners_test.cpp \
adb_utils_test.cpp \
+ fdevent_test.cpp \
+ socket_spec_test.cpp \
+ socket_test.cpp \
+ sysdeps_test.cpp \
+ sysdeps/stat_test.cpp \
transport_test.cpp \
LIBADB_CFLAGS := \
@@ -61,23 +71,49 @@
-fvisibility=hidden \
LIBADB_linux_CFLAGS := \
- -std=c++14 \
+ $(ADB_COMMON_linux_CFLAGS) \
-LIBADB_CFLAGS += $(LIBADB_$(HOST_OS)_CFLAGS)
+LIBADB_darwin_CFLAGS := \
+ $(ADB_COMMON_darwin_CFLAGS) \
+
+LIBADB_windows_CFLAGS := \
+ $(ADB_COMMON_windows_CFLAGS) \
LIBADB_darwin_SRC_FILES := \
- fdevent.cpp \
- get_my_path_darwin.cpp \
- usb_osx.cpp \
+ sysdeps_unix.cpp \
+ client/usb_dispatch.cpp \
+ client/usb_libusb.cpp \
+ client/usb_osx.cpp \
LIBADB_linux_SRC_FILES := \
- fdevent.cpp \
- get_my_path_linux.cpp \
- usb_linux.cpp \
+ sysdeps_unix.cpp \
+ client/usb_dispatch.cpp \
+ client/usb_libusb.cpp \
+ client/usb_linux.cpp \
LIBADB_windows_SRC_FILES := \
sysdeps_win32.cpp \
- usb_windows.cpp \
+ sysdeps/win32/errno.cpp \
+ sysdeps/win32/stat.cpp \
+ client/usb_windows.cpp \
+
+LIBADB_TEST_windows_SRCS := \
+ sysdeps/win32/errno_test.cpp \
+ sysdeps_win32_test.cpp \
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_MODULE := libadbd_usb
+LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
+LOCAL_SRC_FILES := daemon/usb.cpp
+
+LOCAL_SANITIZE := $(adb_target_sanitize)
+
+# Even though we're building a static library (and thus there's no link step for
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
+
+include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CLANG := true
@@ -85,40 +121,44 @@
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
LOCAL_SRC_FILES := \
$(LIBADB_SRC_FILES) \
- adb_auth_client.cpp \
- fdevent.cpp \
+ adbd_auth.cpp \
jdwp_service.cpp \
- qemu_tracing.cpp \
- usb_linux_client.cpp \
LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_SHARED_LIBRARIES := libbase
# Even though we're building a static library (and thus there's no link step for
# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
LOCAL_MODULE := libadb
+LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
+LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
LOCAL_SRC_FILES := \
$(LIBADB_SRC_FILES) \
- $(LIBADB_$(HOST_OS)_SRC_FILES) \
adb_auth_host.cpp \
+LOCAL_SRC_FILES_darwin := $(LIBADB_darwin_SRC_FILES)
+LOCAL_SRC_FILES_linux := $(LIBADB_linux_SRC_FILES)
+LOCAL_SRC_FILES_windows := $(LIBADB_windows_SRC_FILES)
+
LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_SHARED_LIBRARIES := libbase
# Even though we're building a static library (and thus there's no link step for
# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_static libbase
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
+LOCAL_STATIC_LIBRARIES_linux := libusb
+LOCAL_STATIC_LIBRARIES_darwin := libusb
-ifeq ($(HOST_OS),windows)
- LOCAL_C_INCLUDES += development/host/windows/usb/api/
-endif
+LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
+LOCAL_MULTILIB := first
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -126,110 +166,140 @@
LOCAL_CLANG := true
LOCAL_MODULE := adbd_test
LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS)
+LOCAL_SRC_FILES := \
+ $(LIBADB_TEST_SRCS) \
+ $(LIBADB_TEST_linux_SRCS) \
+ shell_service.cpp \
+ shell_service_protocol.cpp \
+ shell_service_protocol_test.cpp \
+ shell_service_test.cpp \
+
LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_STATIC_LIBRARIES := libadbd
+LOCAL_STATIC_LIBRARIES := libadbd libcrypto_utils libcrypto libusb
LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
include $(BUILD_NATIVE_TEST)
+# libdiagnose_usb
+# =========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdiagnose_usb
+LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_CFLAGS := $(LIBADB_CFLAGS)
+LOCAL_SRC_FILES := diagnose_usb.cpp
+# Even though we're building a static library (and thus there's no link step for
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libbase
+include $(BUILD_HOST_STATIC_LIBRARY)
+
# adb_test
# =========================================================
include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
LOCAL_MODULE := adb_test
+LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
+LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
+LOCAL_SRC_FILES := \
+ $(LIBADB_TEST_SRCS) \
+ adb_client.cpp \
+ bugreport.cpp \
+ bugreport_test.cpp \
+ line_printer.cpp \
+ services.cpp \
+ shell_service_protocol.cpp \
+ shell_service_protocol_test.cpp \
+
+LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS)
+LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
+LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_SHARED_LIBRARIES := libbase
LOCAL_STATIC_LIBRARIES := \
libadb \
- libcrypto_static \
+ libcrypto_utils \
+ libcrypto \
libcutils \
+ libdiagnose_usb \
+ libgmock_host \
-ifeq ($(HOST_OS),linux)
- LOCAL_LDLIBS += -lrt -ldl -lpthread
-endif
+LOCAL_STATIC_LIBRARIES_linux := libusb
+LOCAL_STATIC_LIBRARIES_darwin := libusb
-ifeq ($(HOST_OS),darwin)
- LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
-endif
+# Set entrypoint to wmain from sysdeps_win32.cpp instead of main
+LOCAL_LDFLAGS_windows := -municode
+LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
+LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit -lobjc
+LOCAL_LDLIBS_windows := -lws2_32 -luserenv
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
-ifeq ($(HOST_OS),windows)
- LOCAL_LDLIBS += -lws2_32 -luserenv
- LOCAL_STATIC_LIBRARIES += AdbWinApi
-endif
+LOCAL_MULTILIB := first
include $(BUILD_HOST_NATIVE_TEST)
-# adb device tracker (used by ddms) test tool
-# =========================================================
-
-ifeq ($(HOST_OS),linux)
-include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
-LOCAL_MODULE := adb_device_tracker_test
-LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := test_track_devices.cpp
-LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_SHARED_LIBRARIES := liblog libbase
-LOCAL_STATIC_LIBRARIES := libadb libcrypto_static libcutils
-LOCAL_LDLIBS += -lrt -ldl -lpthread
-include $(BUILD_HOST_EXECUTABLE)
-endif
-
# adb host tool
# =========================================================
include $(CLEAR_VARS)
-ifeq ($(HOST_OS),linux)
- LOCAL_LDLIBS += -lrt -ldl -lpthread
- LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
-endif
+LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
-ifeq ($(HOST_OS),darwin)
- LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
- LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
-endif
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon -lobjc
-ifeq ($(HOST_OS),windows)
- # Use wmain instead of main
- LOCAL_LDFLAGS += -municode
- LOCAL_LDLIBS += -lws2_32 -lgdi32
- EXTRA_STATIC_LIBS := AdbWinApi
-endif
-
-LOCAL_CLANG := $(adb_host_clang)
+# Use wmain instead of main
+LOCAL_LDFLAGS_windows := -municode
+LOCAL_LDLIBS_windows := -lws2_32 -lgdi32
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
LOCAL_SRC_FILES := \
+ adb_client.cpp \
+ bugreport.cpp \
client/main.cpp \
console.cpp \
commandline.cpp \
- adb_client.cpp \
- services.cpp \
file_sync_client.cpp \
+ line_printer.cpp \
+ services.cpp \
+ shell_service_protocol.cpp \
LOCAL_CFLAGS += \
$(ADB_COMMON_CFLAGS) \
-D_GNU_SOURCE \
-DADB_HOST=1 \
+LOCAL_CFLAGS_windows := \
+ $(ADB_COMMON_windows_CFLAGS)
+
+LOCAL_CFLAGS_linux := \
+ $(ADB_COMMON_linux_CFLAGS) \
+
+LOCAL_CFLAGS_darwin := \
+ $(ADB_COMMON_darwin_CFLAGS) \
+ -Wno-sizeof-pointer-memaccess -Wno-unused-parameter \
+
LOCAL_MODULE := adb
LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_SANITIZE := $(adb_host_sanitize)
LOCAL_STATIC_LIBRARIES := \
libadb \
libbase \
- libcrypto_static \
- libcutils \
+ libcrypto_utils \
+ libcrypto \
+ libdiagnose_usb \
liblog \
- $(EXTRA_STATIC_LIBS) \
-# libc++ not available on windows yet
-ifneq ($(HOST_OS),windows)
- LOCAL_CXX_STL := libc++_static
-endif
+# Don't use libcutils on Windows.
+LOCAL_STATIC_LIBRARIES_darwin := libcutils
+LOCAL_STATIC_LIBRARIES_linux := libcutils
+
+LOCAL_STATIC_LIBRARIES_darwin += libusb
+LOCAL_STATIC_LIBRARIES_linux += libusb
+
+LOCAL_CXX_STL := libc++_static
# Don't add anything here, we don't want additional shared dependencies
# on the host adb tool, and shared libraries that link against libc++
@@ -238,12 +308,10 @@
include $(BUILD_HOST_EXECUTABLE)
-$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))
-
-ifeq ($(HOST_OS),windows)
-$(LOCAL_INSTALLED_MODULE): \
- $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll \
- $(HOST_OUT_EXECUTABLES)/AdbWinUsbApi.dll
+$(call dist-for-goals,dist_files sdk win_sdk,$(LOCAL_BUILT_MODULE))
+ifdef HOST_CROSS_OS
+# Archive adb.exe for win_sdk build.
+$(call dist-for-goals,win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
endif
@@ -261,9 +329,12 @@
framebuffer_service.cpp \
remount_service.cpp \
set_verity_enable_state_service.cpp \
+ shell_service.cpp \
+ shell_service_protocol.cpp \
LOCAL_CFLAGS := \
$(ADB_COMMON_CFLAGS) \
+ $(ADB_COMMON_linux_CFLAGS) \
-DADB_HOST=0 \
-D_GNU_SOURCE \
-Wno-deprecated-declarations \
@@ -280,18 +351,27 @@
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
-LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_SANITIZE := $(adb_target_sanitize)
+LOCAL_STRIP_MODULE := keep_symbols
LOCAL_STATIC_LIBRARIES := \
libadbd \
libbase \
+ libbootloader_message \
libfs_mgr \
- liblog \
- libmincrypt \
+ libfec \
+ libfec_rs \
libselinux \
- libext4_utils_static \
+ liblog \
+ libext4_utils \
+ libsquashfs_utils \
libcutils \
libbase \
+ libcrypto_utils \
+ libcrypto \
+ libminijail \
+ libdebuggerd_handler \
include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
index 63000f2..30c21f7 100644
--- a/adb/SERVICES.TXT
+++ b/adb/SERVICES.TXT
@@ -237,7 +237,7 @@
Note that there is no single-shot service to retrieve the list only once.
sync:
- This starts the file synchronisation service, used to implement "adb push"
+ This starts the file synchronization service, used to implement "adb push"
and "adb pull". Since this service is pretty complex, it will be detailed
in a companion document named SYNC.TXT
diff --git a/adb/SYNC.TXT b/adb/SYNC.TXT
index e74d217..06d7804 100644
--- a/adb/SYNC.TXT
+++ b/adb/SYNC.TXT
@@ -25,12 +25,9 @@
The following sync requests are accepted:
LIST - List the files in a folder
+RECV - Retrieve a file from device
SEND - Send a file to device
-RECV - Retreive a file from device
-
-Not yet documented:
STAT - Stat a file
-ULNK - Unlink (remove) a file. (Not currently supported)
For all of the sync request above the must be followed by length number of
bytes containing an utf-8 string with a remote filename.
@@ -40,7 +37,7 @@
respond with zero or more directory entries or "dents".
The directory entries will be returned in the following form
-1. A four-byte sync response id beeing "DENT"
+1. A four-byte sync response id "DENT"
2. A four-byte integer representing file mode.
3. A four-byte integer representing file size.
4. A four-byte integer representing last modified time.
@@ -60,13 +57,13 @@
adb push disk_image /some_block_device
to work.
-After this the actual file is sent in chunks. Each chucks has the following
+After this the actual file is sent in chunks. Each chunk has the following
format.
A sync request with id "DATA" and length equal to the chunk size. After
follows chunk size number of bytes. This is repeated until the file is
-transfered. Each chunk must not be larger than 64k.
+transferred. Each chunk must not be larger than 64k.
-When the file is tranfered a sync request "DONE" is sent, where length is set
+When the file is transferred a sync request "DONE" is sent, where length is set
to the last modified time for the file. The server responds to this last
request (but not to chuck requests) with an "OKAY" sync response (length can
be ignored).
@@ -77,8 +74,8 @@
the file that will be returned. Just as for the SEND sync request the file
received is split up into chunks. The sync response id is "DATA" and length is
the chuck size. After follows chunk size number of bytes. This is repeated
-until the file is transfered. Each chuck will not be larger than 64k.
+until the file is transferred. Each chuck will not be larger than 64k.
-When the file is transfered a sync resopnse "DONE" is retrieved where the
+When the file is transferred a sync response "DONE" is retrieved where the
length can be ignored.
diff --git a/adb/__init__.py b/adb/__init__.py
deleted file mode 100644
index 6b509c6..0000000
--- a/adb/__init__.py
+++ /dev/null
@@ -1,17 +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.
-#
-from __future__ import absolute_import
-from .device import * # pylint: disable=wildcard-import
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 29c9481..ece143c 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
#include "sysdeps.h"
#include "adb.h"
@@ -30,14 +30,19 @@
#include <sys/time.h>
#include <time.h>
+#include <chrono>
#include <string>
+#include <thread>
#include <vector>
-#include <unordered_map>
-#include <base/logging.h>
-#include <base/macros.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/errors.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/quick_exit.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "adb_auth.h"
#include "adb_io.h"
@@ -45,32 +50,13 @@
#include "adb_utils.h"
#include "transport.h"
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-
#if !ADB_HOST
-#include <cutils/properties.h>
#include <sys/capability.h>
#include <sys/mount.h>
+#include <android-base/properties.h>
+using namespace std::chrono_literals;
#endif
-ADB_MUTEX_DEFINE(D_lock);
-
-#if !ADB_HOST
-const char* adb_device_banner = "device";
-static android::base::LogdLogger gLogdLogger;
-#else
-const char* adb_device_banner = "host";
-#endif
-
-void AdbLogger(android::base::LogId id, android::base::LogSeverity severity,
- const char* tag, const char* file, unsigned int line,
- const char* message) {
- android::base::StderrLogger(id, severity, tag, file, line, message);
-#if !ADB_HOST
- gLogdLogger(id, severity, tag, file, line, message);
-#endif
-}
-
std::string adb_version() {
// Don't change the format of this --- it's parsed by ddmlib.
return android::base::StringPrintf("Android Debug Bridge version %d.%d.%d\n"
@@ -82,138 +68,46 @@
void fatal(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
- fprintf(stderr, "error: ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+
+#if ADB_HOST
+ fprintf(stderr, "error: %s\n", buf);
+#else
+ LOG(ERROR) << "error: " << buf;
+#endif
+
va_end(ap);
- exit(-1);
+ abort();
}
void fatal_errno(const char* fmt, ...) {
+ int err = errno;
va_list ap;
va_start(ap, fmt);
- fprintf(stderr, "error: %s: ", strerror(errno));
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- va_end(ap);
- exit(-1);
-}
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), fmt, ap);
-#if !ADB_HOST
-static std::string get_log_file_name() {
- struct tm now;
- time_t t;
- tzset();
- time(&t);
- localtime_r(&t, &now);
-
- char timestamp[PATH_MAX];
- strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
-
- return android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp,
- getpid());
-}
-
-void start_device_log(void) {
- int fd = unix_open(get_log_file_name().c_str(),
- O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
- if (fd == -1) {
- return;
- }
-
- // Redirect stdout and stderr to the log file.
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
- unix_close(fd);
-}
-#endif
-
-int adb_trace_mask;
-
-std::string get_trace_setting_from_env() {
- const char* setting = getenv("ADB_TRACE");
- if (setting == nullptr) {
- setting = "";
- }
-
- return std::string(setting);
-}
-
-#if !ADB_HOST
-std::string get_trace_setting_from_prop() {
- char buf[PROPERTY_VALUE_MAX];
- property_get("persist.adb.trace_mask", buf, "");
- return std::string(buf);
-}
-#endif
-
-std::string get_trace_setting() {
#if ADB_HOST
- return get_trace_setting_from_env();
+ fprintf(stderr, "error: %s: %s\n", buf, strerror(err));
#else
- return get_trace_setting_from_prop();
-#endif
-}
-
-// Split the space separated list of tags from the trace setting and build the
-// trace mask from it. note that '1' and 'all' are special cases to enable all
-// tracing.
-//
-// adb's trace setting comes from the ADB_TRACE environment variable, whereas
-// adbd's comes from the system property persist.adb.trace_mask.
-static void setup_trace_mask() {
- const std::string trace_setting = get_trace_setting();
-
- std::unordered_map<std::string, int> trace_flags = {
- {"1", 0},
- {"all", 0},
- {"adb", TRACE_ADB},
- {"sockets", TRACE_SOCKETS},
- {"packets", TRACE_PACKETS},
- {"rwx", TRACE_RWX},
- {"usb", TRACE_USB},
- {"sync", TRACE_SYNC},
- {"sysdeps", TRACE_SYSDEPS},
- {"transport", TRACE_TRANSPORT},
- {"jdwp", TRACE_JDWP},
- {"services", TRACE_SERVICES},
- {"auth", TRACE_AUTH}};
-
- std::vector<std::string> elements = android::base::Split(trace_setting, " ");
- for (const auto& elem : elements) {
- const auto& flag = trace_flags.find(elem);
- if (flag == trace_flags.end()) {
- D("Unknown trace flag: %s\n", flag->first.c_str());
- continue;
- }
-
- if (flag->second == 0) {
- // 0 is used for the special values "1" and "all" that enable all
- // tracing.
- adb_trace_mask = ~0;
- return;
- } else {
- adb_trace_mask |= 1 << flag->second;
- }
- }
-}
-
-void adb_trace_init(char** argv) {
-#if !ADB_HOST
- // Don't open log file if no tracing, since this will block
- // the crypto unmount of /data
- if (!get_trace_setting().empty()) {
- if (isatty(STDOUT_FILENO) == 0) {
- start_device_log();
- }
- }
+ LOG(ERROR) << "error: " << buf << ": " << strerror(err);
#endif
- setup_trace_mask();
- android::base::InitLogging(argv, AdbLogger);
+ va_end(ap);
+ abort();
+}
- D("%s", adb_version().c_str());
+uint32_t calculate_apacket_checksum(const apacket* p) {
+ const unsigned char* x = reinterpret_cast<const unsigned char*>(p->data);
+ uint32_t sum = 0;
+ size_t count = p->msg.data_length;
+
+ while (count-- > 0) {
+ sum += *x++;
+ }
+
+ return sum;
}
apacket* get_apacket(void)
@@ -234,16 +128,21 @@
void handle_online(atransport *t)
{
- D("adb: online\n");
+ D("adb: online");
t->online = 1;
}
void handle_offline(atransport *t)
{
- D("adb: offline\n");
+ D("adb: offline");
//Close the associated usb
t->online = 0;
- run_transport_disconnects(t);
+
+ // This is necessary to avoid a race condition that occurred when a transport closes
+ // while a client socket is still active.
+ close_all_sockets(t);
+
+ t->RunDisconnects();
}
#if DEBUG_PACKETS
@@ -289,7 +188,7 @@
static void send_ready(unsigned local, unsigned remote, atransport *t)
{
- D("Calling send_ready \n");
+ D("Calling send_ready");
apacket *p = get_apacket();
p->msg.command = A_OKAY;
p->msg.arg0 = local;
@@ -299,7 +198,7 @@
static void send_close(unsigned local, unsigned remote, atransport *t)
{
- D("Calling send_close \n");
+ D("Calling send_close");
apacket *p = get_apacket();
p->msg.command = A_CLSE;
p->msg.arg0 = local;
@@ -317,16 +216,14 @@
"ro.product.device",
};
- for (const auto& prop_name : cnxn_props) {
- char value[PROPERTY_VALUE_MAX];
- property_get(prop_name, value, "");
- connection_properties.push_back(
- android::base::StringPrintf("%s=%s", prop_name, value));
+ for (const auto& prop : cnxn_props) {
+ std::string value = std::string(prop) + "=" + android::base::GetProperty(prop, "");
+ connection_properties.push_back(value);
}
#endif
connection_properties.push_back(android::base::StringPrintf(
- "features=%s", android::base::Join(supported_features(), ',').c_str()));
+ "features=%s", FeatureSetToString(supported_features()).c_str()));
return android::base::StringPrintf(
"%s::%s", adb_device_banner,
@@ -334,7 +231,7 @@
}
void send_connect(atransport* t) {
- D("Calling send_connect \n");
+ D("Calling send_connect");
apacket* cp = get_apacket();
cp->msg.command = A_CNXN;
cp->msg.arg0 = t->get_protocol_version();
@@ -364,15 +261,19 @@
}
void parse_banner(const std::string& banner, atransport* t) {
- D("parse_banner: %s\n", banner.c_str());
+ D("parse_banner: %s", banner.c_str());
// The format is something like:
// "device::ro.product.name=x;ro.product.model=y;ro.product.device=z;".
std::vector<std::string> pieces = android::base::Split(banner, ":");
+ // Reset the features list or else if the server sends no features we may
+ // keep the existing feature set (http://b/24405971).
+ t->SetFeatures("");
+
if (pieces.size() > 2) {
const std::string& props = pieces[2];
- for (auto& prop : android::base::Split(props, ";")) {
+ for (const auto& prop : android::base::Split(props, ";")) {
// The list of properties was traditionally ;-terminated rather than ;-separated.
if (prop.empty()) continue;
@@ -388,32 +289,30 @@
} else if (key == "ro.product.device") {
qual_overwrite(&t->device, value);
} else if (key == "features") {
- for (const auto& feature : android::base::Split(value, ",")) {
- t->add_feature(feature);
- }
+ t->SetFeatures(value);
}
}
}
const std::string& type = pieces[0];
if (type == "bootloader") {
- D("setting connection_state to kCsBootloader\n");
+ D("setting connection_state to kCsBootloader");
t->connection_state = kCsBootloader;
update_transports();
} else if (type == "device") {
- D("setting connection_state to kCsDevice\n");
+ D("setting connection_state to kCsDevice");
t->connection_state = kCsDevice;
update_transports();
} else if (type == "recovery") {
- D("setting connection_state to kCsRecovery\n");
+ D("setting connection_state to kCsRecovery");
t->connection_state = kCsRecovery;
update_transports();
} else if (type == "sideload") {
- D("setting connection_state to kCsSideload\n");
+ D("setting connection_state to kCsSideload");
t->connection_state = kCsSideload;
update_transports();
} else {
- D("setting connection_state to kCsHost\n");
+ D("setting connection_state to kCsHost");
t->connection_state = kCsHost;
}
}
@@ -443,9 +342,7 @@
void handle_packet(apacket *p, atransport *t)
{
- asocket *s;
-
- D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],
+ D("handle_packet() %c%c%c%c", ((char*) (&(p->msg.command)))[0],
((char*) (&(p->msg.command)))[1],
((char*) (&(p->msg.command)))[2],
((char*) (&(p->msg.command)))[3]);
@@ -453,7 +350,7 @@
switch(p->msg.command){
case A_SYNC:
- if(p->msg.arg0){
+ if (p->msg.arg0){
send_packet(p, t);
#if ADB_HOST
send_connect(t);
@@ -470,26 +367,31 @@
break;
case A_AUTH:
- if (p->msg.arg0 == ADB_AUTH_TOKEN) {
- t->connection_state = kCsUnauthorized;
- t->key = adb_auth_nextkey(t->key);
- if (t->key) {
+ switch (p->msg.arg0) {
+#if ADB_HOST
+ case ADB_AUTH_TOKEN:
+ t->connection_state = kCsUnauthorized;
send_auth_response(p->data, p->msg.data_length, t);
- } else {
- /* No more private keys to try, send the public key */
- send_auth_publickey(t);
- }
- } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) {
- if (adb_auth_verify(t->token, p->data, p->msg.data_length)) {
- adb_auth_verified(t);
- t->failed_auth_attempts = 0;
- } else {
- if (t->failed_auth_attempts++ > 10)
- adb_sleep_ms(1000);
- send_auth_request(t);
- }
- } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) {
- adb_auth_confirm_key(p->data, p->msg.data_length, t);
+ break;
+#else
+ case ADB_AUTH_SIGNATURE:
+ if (adbd_auth_verify(t->token, sizeof(t->token), p->data, p->msg.data_length)) {
+ adbd_auth_verified(t);
+ t->failed_auth_attempts = 0;
+ } else {
+ if (t->failed_auth_attempts++ > 256) std::this_thread::sleep_for(1s);
+ send_auth_request(t);
+ }
+ break;
+
+ case ADB_AUTH_RSAPUBLICKEY:
+ adbd_auth_confirm_key(p->data, p->msg.data_length, t);
+ break;
+#endif
+ default:
+ t->connection_state = kCsOffline;
+ handle_offline(t);
+ break;
}
break;
@@ -497,8 +399,8 @@
if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
char *name = (char*) p->data;
name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
- s = create_local_service_socket(name);
- if(s == 0) {
+ asocket* s = create_local_service_socket(name, t);
+ if (s == nullptr) {
send_close(0, p->msg.arg0, t);
} else {
s->peer = create_remote_socket(p->msg.arg0, t);
@@ -511,7 +413,8 @@
case A_OKAY: /* READY(local-id, remote-id, "") */
if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
- if((s = find_local_socket(p->msg.arg1, 0))) {
+ asocket* s = find_local_socket(p->msg.arg1, 0);
+ if (s) {
if(s->peer == 0) {
/* On first READY message, create the connection. */
s->peer = create_remote_socket(p->msg.arg0, t);
@@ -521,16 +424,22 @@
/* Other READY messages must use the same local-id */
s->ready(s);
} else {
- D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s\n",
+ D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s",
p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
}
+ } else {
+ // When receiving A_OKAY from device for A_OPEN request, the host server may
+ // have closed the local socket because of client disconnection. Then we need
+ // to send A_CLSE back to device to close the service on device.
+ send_close(p->msg.arg1, p->msg.arg0, t);
}
}
break;
case A_CLSE: /* CLOSE(local-id, remote-id, "") or CLOSE(0, remote-id, "") */
if (t->online && p->msg.arg1 != 0) {
- if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
+ asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
+ if (s) {
/* According to protocol.txt, p->msg.arg0 might be 0 to indicate
* a failed OPEN only. However, due to a bug in previous ADB
* versions, CLOSE(0, remote-id, "") was also used for normal
@@ -542,7 +451,7 @@
* socket has a peer on the same transport.
*/
if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
- D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s\n",
+ D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s",
p->msg.arg1, t->serial, s->peer->transport->serial);
} else {
s->close(s);
@@ -553,12 +462,13 @@
case A_WRTE: /* WRITE(local-id, remote-id, <data>) */
if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
- if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
+ asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
+ if (s) {
unsigned rid = p->msg.arg0;
p->len = p->msg.data_length;
- if(s->enqueue(s, p) == 0) {
- D("Enqueue the socket\n");
+ if (s->enqueue(s, p) == 0) {
+ D("Enqueue the socket");
send_ready(s->id, rid, t);
}
return;
@@ -575,65 +485,203 @@
#if ADB_HOST
-int launch_server(int server_port)
-{
+#ifdef _WIN32
+
+// Try to make a handle non-inheritable and if there is an error, don't output
+// any error info, but leave GetLastError() for the caller to read. This is
+// convenient if the caller is expecting that this may fail and they'd like to
+// ignore such a failure.
+static bool _try_make_handle_noninheritable(HANDLE h) {
+ if (h != INVALID_HANDLE_VALUE && h != NULL) {
+ return SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0) ? true : false;
+ }
+
+ return true;
+}
+
+// Try to make a handle non-inheritable with the expectation that this should
+// succeed, so if this fails, output error info.
+static bool _make_handle_noninheritable(HANDLE h) {
+ if (!_try_make_handle_noninheritable(h)) {
+ // Show the handle value to give us a clue in case we have problems
+ // with pseudo-handle values.
+ fprintf(stderr, "Cannot make handle 0x%p non-inheritable: %s\n",
+ h, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ return false;
+ }
+
+ return true;
+}
+
+// Create anonymous pipe, preventing inheritance of the read pipe and setting
+// security of the write pipe to sa.
+static bool _create_anonymous_pipe(unique_handle* pipe_read_out,
+ unique_handle* pipe_write_out,
+ SECURITY_ATTRIBUTES* sa) {
+ HANDLE pipe_read_raw = NULL;
+ HANDLE pipe_write_raw = NULL;
+ if (!CreatePipe(&pipe_read_raw, &pipe_write_raw, sa, 0)) {
+ fprintf(stderr, "Cannot create pipe: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ return false;
+ }
+
+ unique_handle pipe_read(pipe_read_raw);
+ pipe_read_raw = NULL;
+ unique_handle pipe_write(pipe_write_raw);
+ pipe_write_raw = NULL;
+
+ if (!_make_handle_noninheritable(pipe_read.get())) {
+ return false;
+ }
+
+ *pipe_read_out = std::move(pipe_read);
+ *pipe_write_out = std::move(pipe_write);
+
+ return true;
+}
+
+// Read from a pipe (that we take ownership of) and write the result to stdout/stderr. Return on
+// error or when the pipe is closed. Internally makes inheritable handles, so this should not be
+// called if subprocesses may be started concurrently.
+static unsigned _redirect_pipe_thread(HANDLE h, DWORD nStdHandle) {
+ // Take ownership of the HANDLE and close when we're done.
+ unique_handle read_pipe(h);
+ const char* output_name = nStdHandle == STD_OUTPUT_HANDLE ? "stdout" : "stderr";
+ const int original_fd = fileno(nStdHandle == STD_OUTPUT_HANDLE ? stdout : stderr);
+ std::unique_ptr<FILE, decltype(&fclose)> stream(nullptr, fclose);
+
+ if (original_fd == -1) {
+ fprintf(stderr, "Failed to get file descriptor for %s: %s\n", output_name, strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ // If fileno() is -2, stdout/stderr is not associated with an output stream, so we should read,
+ // but don't write. Otherwise, make a FILE* identical to stdout/stderr except that it is in
+ // binary mode with no CR/LR translation since we're reading raw.
+ if (original_fd >= 0) {
+ // This internally makes a duplicate file handle that is inheritable, so callers should not
+ // call this function if subprocesses may be started concurrently.
+ const int fd = dup(original_fd);
+ if (fd == -1) {
+ fprintf(stderr, "Failed to duplicate file descriptor for %s: %s\n", output_name,
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ // Note that although we call fdopen() below with a binary flag, it may not adhere to that
+ // flag, so we have to set the mode manually.
+ if (_setmode(fd, _O_BINARY) == -1) {
+ fprintf(stderr, "Failed to set binary mode for duplicate of %s: %s\n", output_name,
+ strerror(errno));
+ unix_close(fd);
+ return EXIT_FAILURE;
+ }
+
+ stream.reset(fdopen(fd, "wb"));
+ if (stream.get() == nullptr) {
+ fprintf(stderr, "Failed to open duplicate stream for %s: %s\n", output_name,
+ strerror(errno));
+ unix_close(fd);
+ return EXIT_FAILURE;
+ }
+
+ // Unbuffer the stream because it will be buffered by default and we want subprocess output
+ // to be shown immediately.
+ if (setvbuf(stream.get(), NULL, _IONBF, 0) == -1) {
+ fprintf(stderr, "Failed to unbuffer %s: %s\n", output_name, strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ // fd will be closed when stream is closed.
+ }
+
+ while (true) {
+ char buf[64 * 1024];
+ DWORD bytes_read = 0;
+ if (!ReadFile(read_pipe.get(), buf, sizeof(buf), &bytes_read, NULL)) {
+ const DWORD err = GetLastError();
+ // ERROR_BROKEN_PIPE is expected when the subprocess closes
+ // the other end of the pipe.
+ if (err == ERROR_BROKEN_PIPE) {
+ return EXIT_SUCCESS;
+ } else {
+ fprintf(stderr, "Failed to read from %s: %s\n", output_name,
+ android::base::SystemErrorCodeToString(err).c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ // Don't try to write if our stdout/stderr was not setup by the parent process.
+ if (stream) {
+ // fwrite() actually calls adb_fwrite() which can write UTF-8 to the console.
+ const size_t bytes_written = fwrite(buf, 1, bytes_read, stream.get());
+ if (bytes_written != bytes_read) {
+ fprintf(stderr, "Only wrote %zu of %lu bytes to %s\n", bytes_written, bytes_read,
+ output_name);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+}
+
+static unsigned __stdcall _redirect_stdout_thread(HANDLE h) {
+ adb_thread_setname("stdout redirect");
+ return _redirect_pipe_thread(h, STD_OUTPUT_HANDLE);
+}
+
+static unsigned __stdcall _redirect_stderr_thread(HANDLE h) {
+ adb_thread_setname("stderr redirect");
+ return _redirect_pipe_thread(h, STD_ERROR_HANDLE);
+}
+
+#endif
+
+int launch_server(const std::string& socket_spec) {
#if defined(_WIN32)
/* we need to start the server in the background */
/* we create a PIPE that will be used to wait for the server's "OK" */
/* message since the pipe handles must be inheritable, we use a */
/* security attribute */
- HANDLE nul_read, nul_write;
- HANDLE pipe_read, pipe_write;
- HANDLE stdout_handle, stderr_handle;
SECURITY_ATTRIBUTES sa;
- STARTUPINFOW startup;
- PROCESS_INFORMATION pinfo;
- WCHAR program_path[ MAX_PATH ];
- int ret;
-
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
- /* Redirect stdin and stderr to Windows /dev/null. If we instead pass our
- * stdin/stderr handles and they are console handles, when the adb server
- * starts up, the C Runtime will see console handles for a process that
- * isn't connected to a console and it will configure stderr to be closed.
- * At that point, freopen() could be used to reopen stderr, but it would
- * take more massaging to fixup the file descriptor number that freopen()
- * uses. It's simplest to avoid all of this complexity by just redirecting
- * stdin/stderr to `nul' and then the C Runtime acts as expected.
- */
- nul_read = CreateFileW(L"nul", GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (nul_read == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "CreateFileW(nul, GENERIC_READ) failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
+ // Redirect stdin to Windows /dev/null. If we instead pass an original
+ // stdin/stdout/stderr handle and it is a console handle, when the adb
+ // server starts up, the C Runtime will see a console handle for a process
+ // that isn't connected to a console and it will configure
+ // stdin/stdout/stderr to be closed. At that point, freopen() could be used
+ // to reopen stderr/out, but it would take more massaging to fixup the file
+ // descriptor number that freopen() uses. It's simplest to avoid all of this
+ // complexity by just redirecting stdin to `nul' and then the C Runtime acts
+ // as expected.
+ unique_handle nul_read(CreateFileW(L"nul", GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL));
+ if (nul_read.get() == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Cannot open 'nul': %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
- nul_write = CreateFileW(L"nul", GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (nul_write == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "CreateFileW(nul, GENERIC_WRITE) failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- CloseHandle(nul_read);
+ // Create pipes with non-inheritable read handle, inheritable write handle. We need to connect
+ // the subprocess to pipes instead of just letting the subprocess inherit our existing
+ // stdout/stderr handles because a DETACHED_PROCESS cannot write to a console that it is not
+ // attached to.
+ unique_handle ack_read, ack_write;
+ if (!_create_anonymous_pipe(&ack_read, &ack_write, &sa)) {
return -1;
}
-
- /* create pipe, and ensure its read handle isn't inheritable */
- ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
- if (!ret) {
- fprintf(stderr, "CreatePipe() failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- CloseHandle(nul_read);
- CloseHandle(nul_write);
+ unique_handle stdout_read, stdout_write;
+ if (!_create_anonymous_pipe(&stdout_read, &stdout_write, &sa)) {
return -1;
}
-
- SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
+ unique_handle stderr_read, stderr_write;
+ if (!_create_anonymous_pipe(&stderr_read, &stderr_write, &sa)) {
+ return -1;
+ }
/* Some programs want to launch an adb command and collect its output by
* calling CreateProcess with inheritable stdout/stderr handles, then
@@ -645,52 +693,60 @@
* the calling process is stuck while read()-ing from the stdout/stderr
* descriptors, because they're connected to corresponding handles in the
* adb server process (even if the latter never uses/writes to them).
+ * Note that even if we don't pass these handles in the STARTUPINFO struct,
+ * if they're marked inheritable, they're still inherited, requiring us to
+ * deal with this.
+ *
+ * If we're still having problems with inheriting random handles in the
+ * future, consider using PROC_THREAD_ATTRIBUTE_HANDLE_LIST to explicitly
+ * specify which handles should be inherited: http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx
+ *
+ * Older versions of Windows return console pseudo-handles that cannot be
+ * made non-inheritable, so ignore those failures.
*/
- stdout_handle = GetStdHandle( STD_OUTPUT_HANDLE );
- stderr_handle = GetStdHandle( STD_ERROR_HANDLE );
- if (stdout_handle != INVALID_HANDLE_VALUE) {
- SetHandleInformation( stdout_handle, HANDLE_FLAG_INHERIT, 0 );
- }
- if (stderr_handle != INVALID_HANDLE_VALUE) {
- SetHandleInformation( stderr_handle, HANDLE_FLAG_INHERIT, 0 );
- }
+ _try_make_handle_noninheritable(GetStdHandle(STD_INPUT_HANDLE));
+ _try_make_handle_noninheritable(GetStdHandle(STD_OUTPUT_HANDLE));
+ _try_make_handle_noninheritable(GetStdHandle(STD_ERROR_HANDLE));
+ STARTUPINFOW startup;
ZeroMemory( &startup, sizeof(startup) );
startup.cb = sizeof(startup);
- startup.hStdInput = nul_read;
- startup.hStdOutput = nul_write;
- startup.hStdError = nul_write;
+ startup.hStdInput = nul_read.get();
+ startup.hStdOutput = stdout_write.get();
+ startup.hStdError = stderr_write.get();
startup.dwFlags = STARTF_USESTDHANDLES;
- ZeroMemory( &pinfo, sizeof(pinfo) );
-
- /* get path of current program */
- DWORD module_result = GetModuleFileNameW(NULL, program_path,
- arraysize(program_path));
- if ((module_result == arraysize(program_path)) || (module_result == 0)) {
- // String truncation or some other error.
- fprintf(stderr, "GetModuleFileNameW() failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- return -1;
- }
-
// Verify that the pipe_write handle value can be passed on the command line
// as %d and that the rest of adb code can pass it around in an int.
- const int pipe_write_as_int = cast_handle_to_int(pipe_write);
- if (cast_int_to_handle(pipe_write_as_int) != pipe_write) {
+ const int ack_write_as_int = cast_handle_to_int(ack_write.get());
+ if (cast_int_to_handle(ack_write_as_int) != ack_write.get()) {
// If this fires, either handle values are larger than 32-bits or else
// there is a bug in our casting.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
- fprintf(stderr, "CreatePipe handle value too large: 0x%p\n",
- pipe_write);
+ fprintf(stderr, "Cannot fit pipe handle value into 32-bits: 0x%p\n",
+ ack_write.get());
return -1;
}
- WCHAR args[64];
- snwprintf(args, arraysize(args),
- L"adb -P %d fork-server server --reply-fd %d", server_port,
- pipe_write_as_int);
- ret = CreateProcessW(
+ // get path of current program
+ WCHAR program_path[MAX_PATH];
+ const DWORD module_result = GetModuleFileNameW(NULL, program_path,
+ arraysize(program_path));
+ if ((module_result >= arraysize(program_path)) || (module_result == 0)) {
+ // String truncation or some other error.
+ fprintf(stderr, "Cannot get executable path: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ return -1;
+ }
+
+ WCHAR args[64];
+ snwprintf(args, arraysize(args), L"adb -L %s fork-server server --reply-fd %d",
+ socket_spec.c_str(), ack_write_as_int);
+
+ PROCESS_INFORMATION pinfo;
+ ZeroMemory(&pinfo, sizeof(pinfo));
+
+ if (!CreateProcessW(
program_path, /* program path */
args,
/* the fork-server argument will set the
@@ -702,64 +758,139 @@
NULL, /* use parent's environment block */
NULL, /* use parent's starting directory */
&startup, /* startup info, i.e. std handles */
- &pinfo );
-
- CloseHandle( nul_read );
- CloseHandle( nul_write );
- CloseHandle( pipe_write );
-
- if (!ret) {
- fprintf(stderr, "CreateProcess failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- CloseHandle( pipe_read );
+ &pinfo )) {
+ fprintf(stderr, "Cannot create process: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
- CloseHandle( pinfo.hProcess );
- CloseHandle( pinfo.hThread );
+ unique_handle process_handle(pinfo.hProcess);
+ pinfo.hProcess = NULL;
- /* wait for the "OK\n" message */
+ // Close handles that we no longer need to complete the rest.
+ CloseHandle(pinfo.hThread);
+ pinfo.hThread = NULL;
+
+ nul_read.reset();
+ ack_write.reset();
+ stdout_write.reset();
+ stderr_write.reset();
+
+ // Start threads to read from subprocess stdout/stderr and write to ours to make subprocess
+ // errors easier to diagnose. Note that the threads internally create inheritable handles, but
+ // that is ok because we've already spawned the subprocess.
+
+ // In the past, reading from a pipe before the child process's C Runtime
+ // started up and called GetFileType() caused a hang: http://blogs.msdn.com/b/oldnewthing/archive/2011/12/02/10243553.aspx#10244216
+ // This is reportedly fixed in Windows Vista: https://support.microsoft.com/en-us/kb/2009703
+ // I was unable to reproduce the problem on Windows XP. It sounds like a
+ // Windows Update may have fixed this: https://www.duckware.com/tech/peeknamedpipe.html
+ unique_handle stdout_thread(reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, _redirect_stdout_thread, stdout_read.get(),
+ 0, NULL)));
+ if (stdout_thread.get() == nullptr) {
+ fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+ return -1;
+ }
+ stdout_read.release(); // Transfer ownership to new thread
+
+ unique_handle stderr_thread(reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, _redirect_stderr_thread, stderr_read.get(),
+ 0, NULL)));
+ if (stderr_thread.get() == nullptr) {
+ fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+ return -1;
+ }
+ stderr_read.release(); // Transfer ownership to new thread
+
+ bool got_ack = false;
+
+ // Wait for the "OK\n" message, for the pipe to be closed, or other error.
{
- char temp[3];
- DWORD count;
+ char temp[3];
+ DWORD count = 0;
- ret = ReadFile( pipe_read, temp, 3, &count, NULL );
- CloseHandle( pipe_read );
- if ( !ret ) {
- fprintf(stderr, "could not read ok from ADB Server, error: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- return -1;
- }
- if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
- fprintf(stderr, "ADB server didn't ACK\n" );
- return -1;
+ if (ReadFile(ack_read.get(), temp, sizeof(temp), &count, NULL)) {
+ const CHAR expected[] = "OK\n";
+ const DWORD expected_length = arraysize(expected) - 1;
+ if (count == expected_length &&
+ memcmp(temp, expected, expected_length) == 0) {
+ got_ack = true;
+ } else {
+ fprintf(stderr, "ADB server didn't ACK\n");
+ }
+ } else {
+ const DWORD err = GetLastError();
+ // If the ACK was not written and the process exited, GetLastError()
+ // is probably ERROR_BROKEN_PIPE, in which case that info is not
+ // useful to the user.
+ fprintf(stderr, "could not read ok from ADB Server%s\n",
+ err == ERROR_BROKEN_PIPE ? "" :
+ android::base::StringPrintf(": %s",
+ android::base::SystemErrorCodeToString(err).c_str()).c_str());
}
}
-#else /* !defined(_WIN32) */
- char path[PATH_MAX];
- int fd[2];
+ // Always try to wait a bit for threads reading stdout/stderr to finish.
+ // If the process started ok, it should close the pipes causing the threads
+ // to finish. If the process had an error, it should exit, also causing
+ // the pipes to be closed. In that case we want to read all of the output
+ // and write it out so that the user can diagnose failures.
+ const DWORD thread_timeout_ms = 15 * 1000;
+ const HANDLE threads[] = { stdout_thread.get(), stderr_thread.get() };
+ const DWORD wait_result = WaitForMultipleObjects(arraysize(threads),
+ threads, TRUE, thread_timeout_ms);
+ if (wait_result == WAIT_TIMEOUT) {
+ // Threads did not finish after waiting a little while. Perhaps the
+ // server didn't close pipes, or it is hung.
+ fprintf(stderr, "Timed-out waiting for threads to finish reading from "
+ "ADB Server\n");
+ // Process handles are signaled when the process exits, so if we wait
+ // on the handle for 0 seconds and it returns 'timeout', that means that
+ // the process is still running.
+ if (WaitForSingleObject(process_handle.get(), 0) == WAIT_TIMEOUT) {
+ // We could TerminateProcess(), but that seems somewhat presumptive.
+ fprintf(stderr, "ADB Server is running: process id %lu\n",
+ pinfo.dwProcessId);
+ }
+ return -1;
+ }
+
+ if (wait_result != WAIT_OBJECT_0) {
+ fprintf(stderr, "Unexpected result waiting for threads: %lu: %s\n",
+ wait_result, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ return -1;
+ }
+
+ // For now ignore the thread exit codes and assume they worked properly.
+
+ if (!got_ack) {
+ return -1;
+ }
+#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)) {
fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
return -1;
}
- get_my_path(path, PATH_MAX);
+
+ std::string path = android::base::GetExecutablePath();
+
pid_t pid = fork();
- if(pid < 0) return -1;
+ if (pid < 0) return -1;
if (pid == 0) {
// child side of the fork
adb_close(fd[0]);
- char str_port[30];
- snprintf(str_port, sizeof(str_port), "%d", server_port);
char reply_fd[30];
snprintf(reply_fd, sizeof(reply_fd), "%d", fd[1]);
// child process
- int result = execl(path, "adb", "-P", str_port, "fork-server", "server", "--reply-fd", reply_fd, NULL);
+ int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
+ "--reply-fd", reply_fd, NULL);
// this should not return
fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
} else {
@@ -781,8 +912,6 @@
fprintf(stderr, "ADB server didn't ACK\n" );
return -1;
}
-
- setsid();
}
#endif /* !defined(_WIN32) */
return 0;
@@ -800,8 +929,7 @@
#if ADB_HOST
SendOkay(reply_fd);
#endif
- SendProtocolString(reply_fd, listeners);
- return 1;
+ return SendProtocolString(reply_fd, listeners);
}
if (!strcmp(service, "killforward-all")) {
@@ -847,7 +975,7 @@
}
std::string error_msg;
- atransport* transport = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
if (!transport) {
SendFail(reply_fd, error_msg);
return 1;
@@ -855,18 +983,25 @@
std::string error;
InstallStatus r;
+ int resolved_tcp_port = 0;
if (kill_forward) {
r = remove_listener(pieces[0].c_str(), transport);
} else {
- r = install_listener(pieces[0], pieces[1].c_str(), transport,
- no_rebind, &error);
+ r = install_listener(pieces[0], pieces[1].c_str(), transport, no_rebind,
+ &resolved_tcp_port, &error);
}
if (r == INSTALL_STATUS_OK) {
#if ADB_HOST
- /* On the host: 1st OKAY is connect, 2nd OKAY is status */
+ // On the host: 1st OKAY is connect, 2nd OKAY is status.
SendOkay(reply_fd);
#endif
SendOkay(reply_fd);
+
+ // If a TCP port was resolved, send the actual port number back.
+ if (resolved_tcp_port != 0) {
+ SendProtocolString(reply_fd, android::base::StringPrintf("%d", resolved_tcp_port));
+ }
+
return 1;
}
@@ -906,12 +1041,14 @@
fflush(stdout);
SendOkay(reply_fd);
- // At least on Windows, if we exit() without shutdown(SD_SEND) or
- // closesocket(), the client's next recv() will error-out with
- // WSAECONNRESET and they'll never read the OKAY.
+ // On Windows, if the process exits with open sockets that
+ // shutdown(SD_SEND) has not been called on, TCP RST segments will be
+ // sent to the peers which will cause their next recv() to error-out
+ // with WSAECONNRESET. In the case of this code, that means the client
+ // may not read the OKAY sent above.
adb_shutdown(reply_fd);
- exit(0);
+ android::base::quick_exit(0);
}
#if ADB_HOST
@@ -933,13 +1070,13 @@
serial = service;
}
- std::string error_msg;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
if (t != nullptr) {
s->transport = t;
SendOkay(reply_fd);
} else {
- SendFail(reply_fd, error_msg);
+ SendFail(reply_fd, error);
}
return 1;
}
@@ -948,18 +1085,47 @@
if (!strncmp(service, "devices", 7)) {
bool long_listing = (strcmp(service+7, "-l") == 0);
if (long_listing || service[7] == 0) {
- D("Getting device list...\n");
+ D("Getting device list...");
std::string device_list = list_transports(long_listing);
- D("Sending device list...\n");
+ D("Sending device list...");
return SendOkay(reply_fd, device_list);
}
return 1;
}
+ if (!strcmp(service, "reconnect-offline")) {
+ std::string response;
+ close_usb_devices([&response](const atransport* transport) {
+ switch (transport->connection_state) {
+ case kCsOffline:
+ case kCsUnauthorized:
+ response += "reconnecting ";
+ if (transport->serial) {
+ response += transport->serial;
+ } else {
+ response += "<unknown>";
+ }
+ response += "\n";
+ return true;
+ default:
+ return false;
+ }
+ });
+ if (!response.empty()) {
+ response.resize(response.size() - 1);
+ }
+ SendOkay(reply_fd, response);
+ return 0;
+ }
+
if (!strcmp(service, "features")) {
- SendOkay(reply_fd);
- SendProtocolString(
- reply_fd, android::base::Join(supported_features(), '\n'));
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t != nullptr) {
+ SendOkay(reply_fd, FeatureSetToString(t->features()));
+ } else {
+ SendFail(reply_fd, error);
+ }
return 0;
}
@@ -967,8 +1133,7 @@
if (!strncmp(service, "disconnect:", 11)) {
const std::string address(service + 11);
if (address.empty()) {
- // disconnect from all TCP devices
- unregister_all_tcp_transports();
+ kick_all_tcp_devices();
return SendOkay(reply_fd, "disconnected everything");
}
@@ -976,7 +1141,7 @@
std::string host;
int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
std::string error;
- if (!parse_host_and_port(address, &serial, &host, &port, &error)) {
+ if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
address.c_str(), error.c_str()));
}
@@ -985,39 +1150,58 @@
return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
serial.c_str()));
}
- unregister_transport(t);
+ kick_transport(t);
return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
}
- // returns our value for ADB_SERVER_VERSION
+ // Returns our value for ADB_SERVER_VERSION.
if (!strcmp(service, "version")) {
return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
}
// These always report "unknown" rather than the actual error, for scripts.
if (!strcmp(service, "get-serialno")) {
- std::string ignored;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
- return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown");
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t) {
+ return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+ } else {
+ return SendFail(reply_fd, error);
+ }
}
if (!strcmp(service, "get-devpath")) {
- std::string ignored;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
- return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown");
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t) {
+ return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+ } else {
+ return SendFail(reply_fd, error);
+ }
}
if (!strcmp(service, "get-state")) {
- std::string ignored;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
- return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown");
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t) {
+ return SendOkay(reply_fd, t->connection_state_name());
+ } else {
+ return SendFail(reply_fd, error);
+ }
}
- // indicates a new emulator instance has started
+ // Indicates a new emulator instance has started.
if (!strncmp(service, "emulator:", 9)) {
int port = atoi(service+9);
local_connect(port);
/* we don't even need to send a reply */
return 0;
}
+
+ if (!strcmp(service, "reconnect")) {
+ if (s->transport != nullptr) {
+ kick_transport(s->transport);
+ }
+ return SendOkay(reply_fd, "done");
+ }
#endif // ADB_HOST
int ret = handle_forward_request(service, type, serial, reply_fd);
diff --git a/adb/adb.h b/adb/adb.h
index 6855f3b..6a38f18 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -18,14 +18,17 @@
#define __ADB_H
#include <limits.h>
+#include <stdint.h>
#include <sys/types.h>
#include <string>
-#include <base/macros.h>
+#include <android-base/macros.h>
#include "adb_trace.h"
#include "fdevent.h"
+#include "socket.h"
+#include "usb.h"
constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
@@ -49,104 +52,31 @@
std::string adb_version();
// Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 32
+#define ADB_SERVER_VERSION 38
class atransport;
-struct usb_handle;
struct amessage {
- unsigned command; /* command identifier constant */
- unsigned arg0; /* first argument */
- unsigned arg1; /* second argument */
- unsigned data_length; /* length of payload (0 is allowed) */
- unsigned data_check; /* checksum of data payload */
- unsigned magic; /* command ^ 0xffffffff */
+ uint32_t command; /* command identifier constant */
+ uint32_t arg0; /* first argument */
+ uint32_t arg1; /* second argument */
+ uint32_t data_length; /* length of payload (0 is allowed) */
+ uint32_t data_check; /* checksum of data payload */
+ uint32_t magic; /* command ^ 0xffffffff */
};
struct apacket
{
apacket *next;
- unsigned len;
- unsigned char *ptr;
+ size_t len;
+ char* ptr;
amessage msg;
- unsigned char data[MAX_PAYLOAD];
+ char data[MAX_PAYLOAD];
};
-/* An asocket represents one half of a connection between a local and
-** remote entity. A local asocket is bound to a file descriptor. A
-** remote asocket is bound to the protocol engine.
-*/
-struct asocket {
- /* chain pointers for the local/remote list of
- ** asockets that this asocket lives in
- */
- asocket *next;
- asocket *prev;
-
- /* the unique identifier for this asocket
- */
- unsigned id;
-
- /* flag: set when the socket's peer has closed
- ** but packets are still queued for delivery
- */
- int closing;
-
- /* flag: quit adbd when both ends close the
- ** local service socket
- */
- int exit_on_close;
-
- /* the asocket we are connected to
- */
-
- asocket *peer;
-
- /* For local asockets, the fde is used to bind
- ** us to our fd event system. For remote asockets
- ** these fields are not used.
- */
- fdevent fde;
- int fd;
-
- /* queue of apackets waiting to be written
- */
- apacket *pkt_first;
- apacket *pkt_last;
-
- /* enqueue is called by our peer when it has data
- ** for us. It should return 0 if we can accept more
- ** data or 1 if not. If we return 1, we must call
- ** peer->ready() when we once again are ready to
- ** receive data.
- */
- int (*enqueue)(asocket *s, apacket *pkt);
-
- /* ready is called by the peer when it is ready for
- ** us to send data via enqueue again
- */
- void (*ready)(asocket *s);
-
- /* shutdown is called by the peer before it goes away.
- ** the socket should not do any further calls on its peer.
- ** Always followed by a call to close. Optional, i.e. can be NULL.
- */
- void (*shutdown)(asocket *s);
-
- /* close is called by the peer when it has gone away.
- ** we are not allowed to make any further calls on the
- ** peer once our close method is called.
- */
- void (*close)(asocket *s);
-
- /* A socket is bound to atransport */
- atransport *transport;
-
- size_t get_max_payload() const;
-};
-
+uint32_t calculate_apacket_checksum(const apacket* packet);
/* the adisconnect structure is used to record a callback that
** will be called whenever a transport is disconnected (e.g. by the user)
@@ -157,8 +87,6 @@
{
void (*func)(void* opaque, atransport* t);
void* opaque;
- adisconnect* next;
- adisconnect* prev;
};
@@ -191,52 +119,18 @@
kCsUnauthorized,
};
-/* A listener is an entity which binds to a local port
-** and, upon receiving a connection on that port, creates
-** an asocket to connect the new local connection to a
-** specific remote service.
-**
-** TODO: some listeners read from the new connection to
-** determine what exact service to connect to on the far
-** side.
-*/
-struct alistener
-{
- alistener *next;
- alistener *prev;
-
- fdevent fde;
- int fd;
-
- char *local_name;
- char *connect_to;
- atransport *transport;
- adisconnect disconnect;
-};
-
void print_packet(const char *label, apacket *p);
-asocket *find_local_socket(unsigned local_id, unsigned remote_id);
-void install_local_socket(asocket *s);
-void remove_socket(asocket *s);
-void close_all_sockets(atransport *t);
-
-asocket *create_local_socket(int fd);
-asocket *create_local_service_socket(const char *destination);
-
-asocket *create_remote_socket(unsigned id, atransport *t);
-void connect_to_remote(asocket *s, const char *destination);
-void connect_to_smartsocket(asocket *s);
-
-void fatal(const char *fmt, ...);
-void fatal_errno(const char *fmt, ...);
+// These use the system (v)fprintf, not the adb prefixed ones defined in sysdeps.h, so they
+// shouldn't be tagged with ADB_FORMAT_ARCHETYPE.
+void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
+void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
void handle_packet(apacket *p, atransport *t);
-void get_my_path(char *s, size_t maxLen);
-int launch_server(int server_port);
-int adb_main(int is_daemon, int server_port, int ack_reply_fd);
+int launch_server(const std::string& socket_spec);
+int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd);
/* initialize a transport object's func pointers and state */
#if ADB_HOST
@@ -245,11 +139,13 @@
int init_socket_transport(atransport *t, int s, int port, int local);
void init_usb_transport(atransport *t, usb_handle *usb, ConnectionState state);
+std::string getEmulatorSerialString(int console_port);
#if ADB_HOST
atransport* find_emulator_transport_by_adb_port(int adb_port);
+atransport* find_emulator_transport_by_console_port(int console_port);
#endif
-int service_to_fd(const char *name);
+int service_to_fd(const char* name, const atransport* transport);
#if ADB_HOST
asocket *host_service_to_socket(const char* name, const char *serial);
#endif
@@ -296,23 +192,9 @@
void local_init(int port);
-void local_connect(int port);
+bool local_connect(int port);
int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
-/* usb host/client interface */
-void usb_init();
-int usb_write(usb_handle *h, const void *data, int len);
-int usb_read(usb_handle *h, void *data, int len);
-int usb_close(usb_handle *h);
-void usb_kick(usb_handle *h);
-
-/* used for USB device detection */
-#if ADB_HOST
-int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
-#endif
-
-int adb_commandline(int argc, const char **argv);
-
ConnectionState connection_state(atransport *t);
extern const char* adb_device_banner;
@@ -324,8 +206,6 @@
#define CHUNK_SIZE (64*1024)
#if !ADB_HOST
-#define USB_ADB_PATH "/dev/android_adb"
-
#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x
diff --git a/adb/adb_auth.cpp b/adb/adb_auth.cpp
deleted file mode 100644
index 8a6b156..0000000
--- a/adb/adb_auth.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_ADB
-
-#include "sysdeps.h"
-#include "adb_auth.h"
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "adb.h"
-#include "transport.h"
-
-bool auth_required = true;
-
-void send_auth_request(atransport *t)
-{
- D("Calling send_auth_request\n");
- apacket *p;
- int ret;
-
- ret = adb_auth_generate_token(t->token, sizeof(t->token));
- if (ret != sizeof(t->token)) {
- D("Error generating token ret=%d\n", ret);
- return;
- }
-
- p = get_apacket();
- memcpy(p->data, t->token, ret);
- p->msg.command = A_AUTH;
- p->msg.arg0 = ADB_AUTH_TOKEN;
- p->msg.data_length = ret;
- send_packet(p, t);
-}
-
-void send_auth_response(uint8_t *token, size_t token_size, atransport *t)
-{
- D("Calling send_auth_response\n");
- apacket *p = get_apacket();
- int ret;
-
- ret = adb_auth_sign(t->key, token, token_size, p->data);
- if (!ret) {
- D("Error signing the token\n");
- put_apacket(p);
- return;
- }
-
- p->msg.command = A_AUTH;
- p->msg.arg0 = ADB_AUTH_SIGNATURE;
- p->msg.data_length = ret;
- send_packet(p, t);
-}
-
-void send_auth_publickey(atransport *t)
-{
- D("Calling send_auth_publickey\n");
- apacket *p = get_apacket();
- int ret;
-
- ret = adb_auth_get_userkey(p->data, MAX_PAYLOAD_V1);
- if (!ret) {
- D("Failed to get user public key\n");
- put_apacket(p);
- return;
- }
-
- p->msg.command = A_AUTH;
- p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
- p->msg.data_length = ret;
- send_packet(p, t);
-}
-
-void adb_auth_verified(atransport *t)
-{
- handle_online(t);
- send_connect(t);
-}
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index a13604a..a6f224f 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -19,14 +19,10 @@
#include "adb.h"
-extern bool auth_required;
+#include <deque>
+#include <memory>
-int adb_auth_keygen(const char* filename);
-void adb_auth_verified(atransport *t);
-
-void send_auth_request(atransport *t);
-void send_auth_response(uint8_t *token, size_t token_size, atransport *t);
-void send_auth_publickey(atransport *t);
+#include <openssl/rsa.h>
/* AUTH packets first argument */
/* Request */
@@ -37,30 +33,26 @@
#if ADB_HOST
-void adb_auth_init(void);
-int adb_auth_sign(void *key, const unsigned char* token, size_t token_size,
- unsigned char* sig);
-void *adb_auth_nextkey(void *current);
-int adb_auth_get_userkey(unsigned char *data, size_t len);
+void adb_auth_init();
-static inline int adb_auth_generate_token(void *token, size_t token_size) { return 0; }
-static inline int adb_auth_verify(void *token, void *sig, int siglen) { return 0; }
-static inline void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t) { }
+int adb_auth_keygen(const char* filename);
+std::string adb_auth_get_userkey();
+std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();
+
+void send_auth_response(const char* token, size_t token_size, atransport* t);
#else // !ADB_HOST
-static inline int adb_auth_sign(void* key, const unsigned char* token,
- size_t token_size, unsigned char* sig) {
- return 0;
-}
-static inline void *adb_auth_nextkey(void *current) { return NULL; }
-static inline int adb_auth_get_userkey(unsigned char *data, size_t len) { return 0; }
+extern bool auth_required;
void adbd_auth_init(void);
+void adbd_auth_verified(atransport *t);
+
void adbd_cloexec_auth_socket();
-int adb_auth_generate_token(void *token, size_t token_size);
-int adb_auth_verify(uint8_t* token, uint8_t* sig, int siglen);
-void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t);
+bool adbd_auth_verify(const char* token, size_t token_size, const char* sig, int sig_len);
+void adbd_auth_confirm_key(const char* data, size_t len, atransport* t);
+
+void send_auth_request(atransport *t);
#endif // ADB_HOST
diff --git a/adb/adb_auth_client.cpp b/adb/adb_auth_client.cpp
deleted file mode 100644
index be28202..0000000
--- a/adb/adb_auth_client.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_AUTH
-
-#include "sysdeps.h"
-#include "adb_auth.h"
-
-#include <resolv.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "cutils/list.h"
-#include "cutils/sockets.h"
-#include "mincrypt/rsa.h"
-#include "mincrypt/sha.h"
-
-#include "adb.h"
-#include "fdevent.h"
-#include "transport.h"
-
-struct adb_public_key {
- struct listnode node;
- RSAPublicKey key;
-};
-
-static const char *key_paths[] = {
- "/adb_keys",
- "/data/misc/adb/adb_keys",
- NULL
-};
-
-static fdevent listener_fde;
-static int framework_fd = -1;
-
-static void usb_disconnected(void* unused, atransport* t);
-static struct adisconnect usb_disconnect = { usb_disconnected, 0, 0, 0 };
-static atransport* usb_transport;
-static bool needs_retry = false;
-
-static void read_keys(const char *file, struct listnode *list)
-{
- FILE *f;
- char buf[MAX_PAYLOAD_V1];
- char *sep;
- int ret;
-
- f = fopen(file, "re");
- if (!f) {
- D("Can't open '%s'\n", file);
- return;
- }
-
- while (fgets(buf, sizeof(buf), f)) {
- /* Allocate 4 extra bytes to decode the base64 data in-place */
- auto key = reinterpret_cast<adb_public_key*>(
- calloc(1, sizeof(adb_public_key) + 4));
- if (key == nullptr) {
- D("Can't malloc key\n");
- break;
- }
-
- sep = strpbrk(buf, " \t");
- if (sep)
- *sep = '\0';
-
- ret = __b64_pton(buf, (u_char *)&key->key, sizeof(key->key) + 4);
- if (ret != sizeof(key->key)) {
- D("%s: Invalid base64 data ret=%d\n", file, ret);
- free(key);
- continue;
- }
-
- if (key->key.len != RSANUMWORDS) {
- D("%s: Invalid key len %d\n", file, key->key.len);
- free(key);
- continue;
- }
-
- list_add_tail(list, &key->node);
- }
-
- fclose(f);
-}
-
-static void free_keys(struct listnode *list)
-{
- struct listnode *item;
-
- while (!list_empty(list)) {
- item = list_head(list);
- list_remove(item);
- free(node_to_item(item, struct adb_public_key, node));
- }
-}
-
-static void load_keys(struct listnode *list)
-{
- const char* path;
- const char** paths = key_paths;
- struct stat buf;
-
- list_init(list);
-
- while ((path = *paths++)) {
- if (!stat(path, &buf)) {
- D("Loading keys from '%s'\n", path);
- read_keys(path, list);
- }
- }
-}
-
-int adb_auth_generate_token(void *token, size_t token_size)
-{
- FILE *f;
- int ret;
-
- f = fopen("/dev/urandom", "re");
- if (!f)
- return 0;
-
- ret = fread(token, token_size, 1, f);
-
- fclose(f);
- return ret * token_size;
-}
-
-int adb_auth_verify(uint8_t* token, uint8_t* sig, int siglen)
-{
- struct listnode *item;
- struct listnode key_list;
- int ret = 0;
-
- if (siglen != RSANUMBYTES)
- return 0;
-
- load_keys(&key_list);
-
- list_for_each(item, &key_list) {
- adb_public_key* key = node_to_item(item, struct adb_public_key, node);
- ret = RSA_verify(&key->key, sig, siglen, token, SHA_DIGEST_SIZE);
- if (ret)
- break;
- }
-
- free_keys(&key_list);
-
- return ret;
-}
-
-static void usb_disconnected(void* unused, atransport* t)
-{
- D("USB disconnect\n");
- remove_transport_disconnect(usb_transport, &usb_disconnect);
- usb_transport = NULL;
- needs_retry = false;
-}
-
-static void adb_auth_event(int fd, unsigned events, void *data)
-{
- char response[2];
- int ret;
-
- if (events & FDE_READ) {
- ret = unix_read(fd, response, sizeof(response));
- if (ret <= 0) {
- D("Framework disconnect\n");
- if (usb_transport)
- fdevent_remove(&usb_transport->auth_fde);
- framework_fd = -1;
- }
- else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
- if (usb_transport)
- adb_auth_verified(usb_transport);
- }
- }
-}
-
-void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
-{
- char msg[MAX_PAYLOAD_V1];
- int ret;
-
- if (!usb_transport) {
- usb_transport = t;
- add_transport_disconnect(t, &usb_disconnect);
- }
-
- if (framework_fd < 0) {
- D("Client not connected\n");
- needs_retry = true;
- return;
- }
-
- if (key[len - 1] != '\0') {
- D("Key must be a null-terminated string\n");
- return;
- }
-
- ret = snprintf(msg, sizeof(msg), "PK%s", key);
- if (ret >= (signed)sizeof(msg)) {
- D("Key too long. ret=%d\n", ret);
- return;
- }
- D("Sending '%s'\n", msg);
-
- ret = unix_write(framework_fd, msg, ret);
- if (ret < 0) {
- D("Failed to write PK, errno=%d\n", errno);
- return;
- }
-
- fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t);
- fdevent_add(&t->auth_fde, FDE_READ);
-}
-
-static void adb_auth_listener(int fd, unsigned events, void *data)
-{
- struct sockaddr addr;
- socklen_t alen;
- int s;
-
- alen = sizeof(addr);
-
- s = adb_socket_accept(fd, &addr, &alen);
- if (s < 0) {
- D("Failed to accept: errno=%d\n", errno);
- return;
- }
-
- framework_fd = s;
-
- if (needs_retry) {
- needs_retry = false;
- send_auth_request(usb_transport);
- }
-}
-
-void adbd_cloexec_auth_socket() {
- int fd = android_get_control_socket("adbd");
- if (fd == -1) {
- D("Failed to get adbd socket\n");
- return;
- }
- fcntl(fd, F_SETFD, FD_CLOEXEC);
-}
-
-void adbd_auth_init(void) {
- int fd = android_get_control_socket("adbd");
- if (fd == -1) {
- D("Failed to get adbd socket\n");
- return;
- }
-
- if (listen(fd, 4) == -1) {
- D("Failed to listen on '%d'\n", fd);
- return;
- }
-
- fdevent_install(&listener_fde, fd, adb_auth_listener, NULL);
- fdevent_add(&listener_fde, FDE_READ);
-}
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index e7f82a9..c3f1fe0 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -14,222 +14,108 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_AUTH
+#define TRACE_TAG AUTH
-#include "sysdeps.h"
-#include "adb_auth.h"
-
+#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
-#ifdef _WIN32
-# ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-# endif
-# include "windows.h"
-# include "shlobj.h"
-#else
-# include <sys/types.h>
-# include <sys/stat.h>
-# include <unistd.h>
+#if defined(__linux__)
+#include <sys/inotify.h>
#endif
-#include "adb.h"
+#include <map>
+#include <mutex>
+#include <set>
+#include <string>
-/* HACK: we need the RSAPublicKey struct
- * but RSA_verify conflits with openssl */
-#define RSA_verify RSA_verify_mincrypt
-#include "mincrypt/rsa.h"
-#undef RSA_verify
-
-#include <base/strings.h>
-#include <cutils/list.h>
-
+#include <android-base/errors.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/base64.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
-#if defined(OPENSSL_IS_BORINGSSL)
-#include <openssl/base64.h>
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+static std::mutex& g_keys_mutex = *new std::mutex;
+static std::map<std::string, std::shared_ptr<RSA>>& g_keys =
+ *new std::map<std::string, std::shared_ptr<RSA>>;
+static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
+
+static std::string get_user_info() {
+ LOG(INFO) << "get_user_info...";
+
+ std::string hostname;
+ if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
+#if !defined(_WIN32)
+ char buf[64];
+ if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
#endif
+ if (hostname.empty()) hostname = "unknown";
-#define ANDROID_PATH ".android"
-#define ADB_KEY_FILE "adbkey"
-
-struct adb_private_key {
- struct listnode node;
- RSA *rsa;
-};
-
-static struct listnode key_list;
-
-
-/* Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format */
-static int RSA_to_RSAPublicKey(RSA *rsa, RSAPublicKey *pkey)
-{
- int ret = 1;
- unsigned int i;
-
- BN_CTX* ctx = BN_CTX_new();
- BIGNUM* r32 = BN_new();
- BIGNUM* rr = BN_new();
- BIGNUM* r = BN_new();
- BIGNUM* rem = BN_new();
- BIGNUM* n = BN_new();
- BIGNUM* n0inv = BN_new();
-
- if (RSA_size(rsa) != RSANUMBYTES) {
- ret = 0;
- goto out;
- }
-
- BN_set_bit(r32, 32);
- BN_copy(n, rsa->n);
- BN_set_bit(r, RSANUMWORDS * 32);
- BN_mod_sqr(rr, r, n, ctx);
- BN_div(NULL, rem, n, r32, ctx);
- BN_mod_inverse(n0inv, rem, r32, ctx);
-
- pkey->len = RSANUMWORDS;
- pkey->n0inv = 0 - BN_get_word(n0inv);
- for (i = 0; i < RSANUMWORDS; i++) {
- BN_div(rr, rem, rr, r32, ctx);
- pkey->rr[i] = BN_get_word(rem);
- BN_div(n, rem, n, r32, ctx);
- pkey->n[i] = BN_get_word(rem);
- }
- pkey->exponent = BN_get_word(rsa->e);
-
-out:
- BN_free(n0inv);
- BN_free(n);
- BN_free(rem);
- BN_free(r);
- BN_free(rr);
- BN_free(r32);
- BN_CTX_free(ctx);
-
- return ret;
-}
-
-static void get_user_info(char *buf, size_t len)
-{
- char hostname[1024], username[1024];
- int ret = -1;
-
- if (getenv("HOSTNAME") != NULL) {
- strncpy(hostname, getenv("HOSTNAME"), sizeof(hostname));
- hostname[sizeof(hostname)-1] = '\0';
- ret = 0;
- }
-
-#ifndef _WIN32
- if (ret < 0)
- ret = gethostname(hostname, sizeof(hostname));
-#endif
- if (ret < 0)
- strcpy(hostname, "unknown");
-
- ret = -1;
-
- if (getenv("LOGNAME") != NULL) {
- strncpy(username, getenv("LOGNAME"), sizeof(username));
- username[sizeof(username)-1] = '\0';
- ret = 0;
- }
-
+ std::string username;
+ if (getenv("LOGNAME")) username = getenv("LOGNAME");
#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
- if (ret < 0)
- ret = getlogin_r(username, sizeof(username));
+ if (username.empty() && getlogin()) username = getlogin();
#endif
- if (ret < 0)
- strcpy(username, "unknown");
+ if (username.empty()) hostname = "unknown";
- ret = snprintf(buf, len, " %s@%s", username, hostname);
- if (ret >= (signed)len)
- buf[len - 1] = '\0';
+ return " " + username + "@" + hostname;
}
-static int write_public_keyfile(RSA *private_key, const char *private_key_path)
-{
- RSAPublicKey pkey;
- FILE *outfile = NULL;
- char path[PATH_MAX], info[MAX_PAYLOAD_V1];
- uint8_t* encoded = nullptr;
- size_t encoded_length;
- int ret = 0;
+static bool write_public_keyfile(RSA* private_key, const std::string& private_key_path) {
+ LOG(INFO) << "write_public_keyfile...";
- if (snprintf(path, sizeof(path), "%s.pub", private_key_path) >=
- (int)sizeof(path)) {
- D("Path too long while writing public key\n");
- return 0;
+ uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
+ if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
+ LOG(ERROR) << "Failed to convert to public key";
+ return false;
}
- if (!RSA_to_RSAPublicKey(private_key, &pkey)) {
- D("Failed to convert to publickey\n");
- return 0;
+ size_t base64_key_length;
+ if (!EVP_EncodedLength(&base64_key_length, sizeof(binary_key_data))) {
+ LOG(ERROR) << "Public key too large to base64 encode";
+ return false;
}
- outfile = fopen(path, "w");
- if (!outfile) {
- D("Failed to open '%s'\n", path);
- return 0;
+ std::string content;
+ content.resize(base64_key_length);
+ base64_key_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(&content[0]), binary_key_data,
+ sizeof(binary_key_data));
+
+ content += get_user_info();
+
+ std::string path(private_key_path + ".pub");
+ if (!android::base::WriteStringToFile(content, path)) {
+ PLOG(ERROR) << "Failed to write public key to '" << path << "'";
+ return false;
}
- D("Writing public key to '%s'\n", path);
-
-#if defined(OPENSSL_IS_BORINGSSL)
- if (!EVP_EncodedLength(&encoded_length, sizeof(pkey))) {
- D("Public key too large to base64 encode\n");
- goto out;
- }
-#else
- /* While we switch from OpenSSL to BoringSSL we have to implement
- * |EVP_EncodedLength| here. */
- encoded_length = 1 + ((sizeof(pkey) + 2) / 3 * 4);
-#endif
-
- encoded = new uint8_t[encoded_length];
- if (encoded == nullptr) {
- D("Allocation failure\n");
- goto out;
- }
-
- encoded_length = EVP_EncodeBlock(encoded, (uint8_t*) &pkey, sizeof(pkey));
- get_user_info(info, sizeof(info));
-
- if (fwrite(encoded, encoded_length, 1, outfile) != 1 ||
- fwrite(info, strlen(info), 1, outfile) != 1) {
- D("Write error while writing public key\n");
- goto out;
- }
-
- ret = 1;
-
- out:
- if (outfile != NULL) {
- fclose(outfile);
- }
- delete[] encoded;
- return ret;
+ return true;
}
-static int generate_key(const char *file)
-{
- EVP_PKEY* pkey = EVP_PKEY_new();
- BIGNUM* exponent = BN_new();
- RSA* rsa = RSA_new();
+static int generate_key(const std::string& file) {
+ LOG(INFO) << "generate_key(" << file << ")...";
+
mode_t old_mask;
FILE *f = NULL;
int ret = 0;
- D("generate_key '%s'\n", file);
-
+ EVP_PKEY* pkey = EVP_PKEY_new();
+ BIGNUM* exponent = BN_new();
+ RSA* rsa = RSA_new();
if (!pkey || !exponent || !rsa) {
- D("Failed to allocate key\n");
+ LOG(ERROR) << "Failed to allocate key";
goto out;
}
@@ -239,9 +125,9 @@
old_mask = umask(077);
- f = fopen(file, "w");
+ f = fopen(file.c_str(), "w");
if (!f) {
- D("Failed to open '%s'\n", file);
+ PLOG(ERROR) << "Failed to open " << file;
umask(old_mask);
goto out;
}
@@ -249,226 +135,346 @@
umask(old_mask);
if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
- D("Failed to write key\n");
+ D("Failed to write key");
goto out;
}
if (!write_public_keyfile(rsa, file)) {
- D("Failed to write public key\n");
+ D("Failed to write public key");
goto out;
}
ret = 1;
out:
- if (f)
- fclose(f);
+ if (f) fclose(f);
EVP_PKEY_free(pkey);
RSA_free(rsa);
BN_free(exponent);
return ret;
}
-static int read_key(const char *file, struct listnode *list)
-{
- D("read_key '%s'\n", file);
+static std::string hash_key(RSA* key) {
+ unsigned char* pubkey = nullptr;
+ int len = i2d_RSA_PUBKEY(key, &pubkey);
+ if (len < 0) {
+ LOG(ERROR) << "failed to encode RSA public key";
+ return std::string();
+ }
- FILE* fp = fopen(file, "r");
+ std::string result;
+ result.resize(SHA256_DIGEST_LENGTH);
+ SHA256(pubkey, len, reinterpret_cast<unsigned char*>(&result[0]));
+ OPENSSL_free(pubkey);
+ return result;
+}
+
+static bool read_key_file(const std::string& file) {
+ LOG(INFO) << "read_key_file '" << file << "'...";
+
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file.c_str(), "r"), fclose);
if (!fp) {
- D("Failed to open '%s': %s\n", file, strerror(errno));
- return 0;
+ PLOG(ERROR) << "Failed to open '" << file << "'";
+ return false;
}
- adb_private_key* key = new adb_private_key;
- key->rsa = RSA_new();
-
- if (!PEM_read_RSAPrivateKey(fp, &key->rsa, NULL, NULL)) {
- D("Failed to read key\n");
- fclose(fp);
- RSA_free(key->rsa);
- delete key;
- return 0;
+ RSA* key = RSA_new();
+ if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
+ LOG(ERROR) << "Failed to read key";
+ RSA_free(key);
+ return false;
}
- fclose(fp);
- list_add_tail(list, &key->node);
- return 1;
+ std::lock_guard<std::mutex> lock(g_keys_mutex);
+ std::string fingerprint = hash_key(key);
+ if (g_keys.find(fingerprint) != g_keys.end()) {
+ LOG(INFO) << "ignoring already-loaded key: " << file;
+ RSA_free(key);
+ } else {
+ g_keys[fingerprint] = std::shared_ptr<RSA>(key, RSA_free);
+ }
+
+ return true;
}
-static int get_user_keyfilepath(char *filename, size_t len)
-{
- const char *format, *home;
- char android_dir[PATH_MAX];
- struct stat buf;
-#ifdef _WIN32
- std::string home_str;
- home = getenv("ANDROID_SDK_HOME");
- if (!home) {
- WCHAR path[MAX_PATH];
- const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
- if (FAILED(hr)) {
- D("SHGetFolderPathW failed: %s\n",
- SystemErrorCodeToString(hr).c_str());
- return -1;
- }
- home_str = narrow(path);
- home = home_str.c_str();
- }
- format = "%s\\%s";
-#else
- home = getenv("HOME");
- if (!home)
- return -1;
- format = "%s/%s";
-#endif
+static bool read_keys(const std::string& path, bool allow_dir = true) {
+ LOG(INFO) << "read_keys '" << path << "'...";
- D("home '%s'\n", home);
-
- if (snprintf(android_dir, sizeof(android_dir), format, home,
- ANDROID_PATH) >= (int)sizeof(android_dir))
- return -1;
-
- if (stat(android_dir, &buf)) {
- if (adb_mkdir(android_dir, 0750) < 0) {
- D("Cannot mkdir '%s'\n", android_dir);
- return -1;
- }
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0) {
+ PLOG(ERROR) << "failed to stat '" << path << "'";
+ return false;
}
- return snprintf(filename, len, format, android_dir, ADB_KEY_FILE);
+ if (S_ISREG(st.st_mode)) {
+ return read_key_file(path);
+ } else if (S_ISDIR(st.st_mode)) {
+ if (!allow_dir) {
+ // inotify isn't recursive. It would break expectations to load keys in nested
+ // directories but not monitor them for new keys.
+ LOG(WARNING) << "refusing to recurse into directory '" << path << "'";
+ return false;
+ }
+
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+ if (!dir) {
+ PLOG(ERROR) << "failed to open directory '" << path << "'";
+ return false;
+ }
+
+ bool result = false;
+ while (struct dirent* dent = readdir(dir.get())) {
+ std::string name = dent->d_name;
+
+ // We can't use dent->d_type here because it's not available on Windows.
+ if (name == "." || name == "..") {
+ continue;
+ }
+
+ if (!android::base::EndsWith(name, ".adb_key")) {
+ LOG(INFO) << "skipping non-adb_key '" << path << "/" << name << "'";
+ continue;
+ }
+
+ result |= read_key_file((path + OS_PATH_SEPARATOR + name));
+ }
+ return result;
+ }
+
+ LOG(ERROR) << "unexpected type for '" << path << "': 0x" << std::hex << st.st_mode;
+ return false;
}
-static int get_user_key(struct listnode *list)
-{
- struct stat buf;
- char path[PATH_MAX];
- int ret;
+static std::string get_user_key_path() {
+ return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
+}
- ret = get_user_keyfilepath(path, sizeof(path));
- if (ret < 0 || ret >= (signed)sizeof(path)) {
- D("Error getting user key filename\n");
- return 0;
+static bool get_user_key() {
+ std::string path = get_user_key_path();
+ if (path.empty()) {
+ PLOG(ERROR) << "Error getting user key filename";
+ return false;
}
- D("user key '%s'\n", path);
-
- if (stat(path, &buf) == -1) {
+ struct stat buf;
+ if (stat(path.c_str(), &buf) == -1) {
+ LOG(INFO) << "User key '" << path << "' does not exist...";
if (!generate_key(path)) {
- D("Failed to generate new key\n");
- return 0;
+ LOG(ERROR) << "Failed to generate new key";
+ return false;
}
}
- return read_key(path, list);
+ return read_key_file(path);
}
-static void get_vendor_keys(struct listnode* key_list) {
+static std::set<std::string> get_vendor_keys() {
const char* adb_keys_path = getenv("ADB_VENDOR_KEYS");
if (adb_keys_path == nullptr) {
- return;
+ return std::set<std::string>();
}
- for (auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
- if (!read_key(path.c_str(), key_list)) {
- D("Failed to read '%s'\n", path.c_str());
- }
+ std::set<std::string> result;
+ for (const auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
+ result.emplace(path);
}
+ return result;
}
-int adb_auth_sign(void *node, const unsigned char* token, size_t token_size,
- unsigned char* sig)
-{
- unsigned int len;
- struct adb_private_key *key = node_to_item(node, struct adb_private_key, node);
+std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys() {
+ std::deque<std::shared_ptr<RSA>> result;
+ // Copy all the currently known keys.
+ std::lock_guard<std::mutex> lock(g_keys_mutex);
+ for (const auto& it : g_keys) {
+ result.push_back(it.second);
+ }
+
+ // Add a sentinel to the list. Our caller uses this to mean "out of private keys,
+ // but try using the public key" (the empty deque could otherwise mean this _or_
+ // that this function hasn't been called yet to request the keys).
+ result.push_back(nullptr);
+
+ return result;
+}
+
+static int adb_auth_sign(RSA* key, const char* token, size_t token_size, char* sig) {
if (token_size != TOKEN_SIZE) {
- D("Unexpected token size %zd\n", token_size);
+ D("Unexpected token size %zd", token_size);
return 0;
}
- if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) {
+ unsigned int len;
+ if (!RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
+ reinterpret_cast<uint8_t*>(sig), &len, key)) {
return 0;
}
- D("adb_auth_sign len=%d\n", len);
+ D("adb_auth_sign len=%d", len);
return (int)len;
}
-void *adb_auth_nextkey(void *current)
-{
- struct listnode *item;
-
- if (list_empty(&key_list))
- return NULL;
-
- if (!current)
- return list_head(&key_list);
-
- list_for_each(item, &key_list) {
- if (item == current) {
- /* current is the last item, we tried all the keys */
- if (item->next == &key_list)
- return NULL;
- return item->next;
- }
+std::string adb_auth_get_userkey() {
+ std::string path = get_user_key_path();
+ if (path.empty()) {
+ PLOG(ERROR) << "Error getting user key filename";
+ return "";
}
+ path += ".pub";
- return NULL;
-}
-
-int adb_auth_get_userkey(unsigned char *data, size_t len)
-{
- char path[PATH_MAX];
- int ret = get_user_keyfilepath(path, sizeof(path) - 4);
- if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) {
- D("Error getting user key filename\n");
- return 0;
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ PLOG(ERROR) << "Can't load '" << path << "'";
+ return "";
}
- strcat(path, ".pub");
-
- // TODO(danalbert): ReadFileToString
- // Note that on Windows, load_file() does not do CR/LF translation, but
- // ReadFileToString() uses the C Runtime which uses CR/LF translation by
- // default (by is overridable with _setmode()).
- unsigned size;
- char* file_data = reinterpret_cast<char*>(load_file(path, &size));
- if (file_data == nullptr) {
- D("Can't load '%s'\n", path);
- return 0;
- }
-
- if (len < (size_t)(size + 1)) {
- D("%s: Content too large ret=%d\n", path, size);
- free(file_data);
- return 0;
- }
-
- memcpy(data, file_data, size);
- free(file_data);
- file_data = nullptr;
- data[size] = '\0';
-
- return size + 1;
+ return content;
}
int adb_auth_keygen(const char* filename) {
- adb_trace_mask |= (1 << TRACE_AUTH);
return (generate_key(filename) == 0);
}
-void adb_auth_init(void)
-{
- int ret;
-
- D("adb_auth_init\n");
-
- list_init(&key_list);
-
- ret = get_user_key(&key_list);
- if (!ret) {
- D("Failed to get user key\n");
+#if defined(__linux__)
+static void adb_auth_inotify_update(int fd, unsigned fd_event, void*) {
+ LOG(INFO) << "adb_auth_inotify_update called";
+ if (!(fd_event & FDE_READ)) {
return;
}
- get_vendor_keys(&key_list);
+ char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
+ while (true) {
+ ssize_t rc = TEMP_FAILURE_RETRY(unix_read(fd, buf, sizeof(buf)));
+ if (rc == -1) {
+ if (errno == EAGAIN) {
+ LOG(INFO) << "done reading inotify fd";
+ break;
+ }
+ PLOG(FATAL) << "read of inotify event failed";
+ }
+
+ // The read potentially returned multiple events.
+ char* start = buf;
+ char* end = buf + rc;
+
+ while (start < end) {
+ inotify_event* event = reinterpret_cast<inotify_event*>(start);
+ auto root_it = g_monitored_paths.find(event->wd);
+ if (root_it == g_monitored_paths.end()) {
+ LOG(FATAL) << "observed inotify event for unmonitored path, wd = " << event->wd;
+ }
+
+ std::string path = root_it->second;
+ if (event->len > 0) {
+ path += '/';
+ path += event->name;
+ }
+
+ if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
+ if (event->mask & IN_ISDIR) {
+ LOG(INFO) << "ignoring new directory at '" << path << "'";
+ } else {
+ LOG(INFO) << "observed new file at '" << path << "'";
+ read_keys(path, false);
+ }
+ } else {
+ LOG(WARNING) << "unmonitored event for " << path << ": 0x" << std::hex
+ << event->mask;
+ }
+
+ start += sizeof(struct inotify_event) + event->len;
+ }
+ }
+}
+
+static void adb_auth_inotify_init(const std::set<std::string>& paths) {
+ LOG(INFO) << "adb_auth_inotify_init...";
+
+ int infd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
+ if (infd < 0) {
+ PLOG(ERROR) << "failed to create inotify fd";
+ return;
+ }
+
+ for (const std::string& path : paths) {
+ int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
+ if (wd < 0) {
+ PLOG(ERROR) << "failed to inotify_add_watch on path '" << path;
+ continue;
+ }
+
+ g_monitored_paths[wd] = path;
+ LOG(INFO) << "watch descriptor " << wd << " registered for " << path;
+ }
+
+ fdevent* event = fdevent_create(infd, adb_auth_inotify_update, nullptr);
+ fdevent_add(event, FDE_READ);
+}
+#endif
+
+void adb_auth_init() {
+ LOG(INFO) << "adb_auth_init...";
+
+ if (!get_user_key()) {
+ LOG(ERROR) << "Failed to get user key";
+ return;
+ }
+
+ const auto& key_paths = get_vendor_keys();
+
+#if defined(__linux__)
+ adb_auth_inotify_init(key_paths);
+#endif
+
+ for (const std::string& path : key_paths) {
+ read_keys(path.c_str());
+ }
+}
+
+static void send_auth_publickey(atransport* t) {
+ LOG(INFO) << "Calling send_auth_publickey";
+
+ std::string key = adb_auth_get_userkey();
+ if (key.empty()) {
+ D("Failed to get user public key");
+ return;
+ }
+
+ if (key.size() >= MAX_PAYLOAD_V1) {
+ D("User public key too large (%zu B)", key.size());
+ return;
+ }
+
+ apacket* p = get_apacket();
+ memcpy(p->data, key.c_str(), key.size() + 1);
+
+ p->msg.command = A_AUTH;
+ p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
+
+ // adbd expects a null-terminated string.
+ p->msg.data_length = key.size() + 1;
+ send_packet(p, t);
+}
+
+void send_auth_response(const char* token, size_t token_size, atransport* t) {
+ std::shared_ptr<RSA> key = t->NextKey();
+ if (key == nullptr) {
+ // No more private keys to try, send the public key.
+ send_auth_publickey(t);
+ return;
+ }
+
+ LOG(INFO) << "Calling send_auth_response";
+ apacket* p = get_apacket();
+
+ int ret = adb_auth_sign(key.get(), token, token_size, p->data);
+ if (!ret) {
+ D("Error signing the token");
+ put_apacket(p);
+ return;
+ }
+
+ p->msg.command = A_AUTH;
+ p->msg.arg0 = ADB_AUTH_SIGNATURE;
+ p->msg.data_length = ret;
+ send_packet(p, t);
}
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index 4bf647c..ef52189 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
#include "sysdeps.h"
#include "adb_client.h"
@@ -29,57 +29,33 @@
#include <sys/types.h>
#include <string>
+#include <thread>
#include <vector>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/sockets.h>
#include "adb_io.h"
#include "adb_utils.h"
+#include "socket_spec.h"
+#include "sysdeps/chrono.h"
static TransportType __adb_transport = kTransportAny;
static const char* __adb_serial = NULL;
-static int __adb_server_port = DEFAULT_ADB_PORT;
-static const char* __adb_server_name = NULL;
+static const char* __adb_server_socket_spec;
-static std::string perror_str(const char* msg) {
- return android::base::StringPrintf("%s: %s", msg, strerror(errno));
-}
-
-static bool ReadProtocolString(int fd, std::string* s, std::string* error) {
- char buf[5];
- if (!ReadFdExactly(fd, buf, 4)) {
- *error = perror_str("protocol fault (couldn't read status length)");
- return false;
- }
- buf[4] = 0;
-
- unsigned long len = strtoul(buf, 0, 16);
- s->resize(len, '\0');
- if (!ReadFdExactly(fd, &(*s)[0], len)) {
- *error = perror_str("protocol fault (couldn't read status message)");
- return false;
- }
-
- return true;
-}
-
-void adb_set_transport(TransportType type, const char* serial)
-{
+void adb_set_transport(TransportType type, const char* serial) {
__adb_transport = type;
__adb_serial = serial;
}
-void adb_set_tcp_specifics(int server_port)
-{
- __adb_server_port = server_port;
-}
-
-void adb_set_tcp_name(const char* hostname)
-{
- __adb_server_name = hostname;
+void adb_set_socket_spec(const char* socket_spec) {
+ if (__adb_server_socket_spec) {
+ LOG(FATAL) << "attempted to reinitialize adb_server_socket_spec " << socket_spec << " (was " << __adb_server_socket_spec << ")";
+ }
+ __adb_server_socket_spec = socket_spec;
}
static int switch_socket_transport(int fd, std::string* error) {
@@ -112,14 +88,14 @@
adb_close(fd);
return -1;
}
- D("Switch transport in progress\n");
+ D("Switch transport in progress");
if (!adb_status(fd, error)) {
adb_close(fd);
- D("Switch transport failed: %s\n", error->c_str());
+ D("Switch transport failed: %s", error->c_str());
return -1;
}
- D("Switch transport success\n");
+ D("Switch transport success");
return 0;
}
@@ -145,48 +121,40 @@
}
int _adb_connect(const std::string& service, std::string* error) {
- D("_adb_connect: %s\n", service.c_str());
- if (service.empty() || service.size() > 1024) {
+ D("_adb_connect: %s", service.c_str());
+ if (service.empty() || service.size() > MAX_PAYLOAD_V1) {
*error = android::base::StringPrintf("bad service name length (%zd)",
service.size());
return -1;
}
- int fd;
std::string reason;
- if (__adb_server_name) {
- fd = network_connect(__adb_server_name, __adb_server_port, SOCK_STREAM, 0, &reason);
- if (fd == -1) {
- *error = android::base::StringPrintf("can't connect to %s:%d: %s",
- __adb_server_name, __adb_server_port,
- reason.c_str());
- return -2;
- }
- } else {
- fd = network_loopback_client(__adb_server_port, SOCK_STREAM, &reason);
- if (fd == -1) {
- *error = android::base::StringPrintf("cannot connect to daemon: %s",
- reason.c_str());
- return -2;
- }
+ int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
+ if (fd < 0) {
+ *error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
+ __adb_server_socket_spec, reason.c_str());
+ return -2;
}
- if (memcmp(&service[0],"host",4) != 0 && switch_socket_transport(fd, error)) {
+ if ((memcmp(&service[0],"host",4) != 0 || service == "host:reconnect") &&
+ switch_socket_transport(fd, error)) {
return -1;
}
- if(!SendProtocolString(fd, service)) {
+ if (!SendProtocolString(fd, service)) {
*error = perror_str("write failure during connection");
adb_close(fd);
return -1;
}
- if (!adb_status(fd, error)) {
- adb_close(fd);
- return -1;
+ if (service != "reconnect") {
+ if (!adb_status(fd, error)) {
+ adb_close(fd);
+ return -1;
+ }
}
- D("_adb_connect: return fd %d\n", fd);
+ D("_adb_connect: return fd %d", fd);
return fd;
}
@@ -194,16 +162,15 @@
// first query the adb server's version
int fd = _adb_connect("host:version", error);
- D("adb_connect: service %s\n", service.c_str());
- if (fd == -2 && __adb_server_name) {
+ D("adb_connect: service %s", service.c_str());
+ if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
fprintf(stderr,"** Cannot start server on remote host\n");
// error is the original network connection error
return fd;
} else if (fd == -2) {
- fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
- __adb_server_port);
+ fprintf(stdout, "* daemon not running. starting it now at %s *\n", __adb_server_socket_spec);
start_server:
- if (launch_server(__adb_server_port)) {
+ if (launch_server(__adb_server_socket_spec)) {
fprintf(stderr,"* failed to start daemon *\n");
// launch_server() has already printed detailed error info, so just
// return a generic error string about the overall adb_connect()
@@ -213,31 +180,32 @@
} else {
fprintf(stdout,"* daemon started successfully *\n");
}
- /* give the server some time to start properly and detect devices */
- adb_sleep_ms(3000);
+ // Give the server some time to start properly and detect devices.
+ std::this_thread::sleep_for(3s);
// fall through to _adb_connect
} else {
- // if server was running, check its version to make sure it is not out of date
+ // If a server is already running, check its version matches.
int version = ADB_SERVER_VERSION - 1;
- // if we have a file descriptor, then parse version result
+ // If we have a file descriptor, then parse version result.
if (fd >= 0) {
std::string version_string;
if (!ReadProtocolString(fd, &version_string, error)) {
- goto error;
+ adb_close(fd);
+ return -1;
}
+ ReadOrderlyShutdown(fd);
adb_close(fd);
if (sscanf(&version_string[0], "%04x", &version) != 1) {
- *error = android::base::StringPrintf(
- "cannot parse version string: %s",
- version_string.c_str());
+ *error = android::base::StringPrintf("cannot parse version string: %s",
+ version_string.c_str());
return -1;
}
} else {
- // if fd is -1, then check for "unknown host service",
- // which would indicate a version of adb that does not support the
+ // If fd is -1 check for "unknown host service" which would
+ // indicate a version of adb that does not support the
// version command, in which case we should fall-through to kill it.
if (*error != "unknown host service") {
return fd;
@@ -245,9 +213,11 @@
}
if (version != ADB_SERVER_VERSION) {
- printf("adb server is out of date. killing...\n");
+ printf("adb server version (%d) doesn't match this client (%d); killing...\n",
+ version, ADB_SERVER_VERSION);
fd = _adb_connect("host:kill", error);
if (fd >= 0) {
+ ReadOrderlyShutdown(fd);
adb_close(fd);
} else {
// If we couldn't connect to the server or had some other error,
@@ -256,7 +226,7 @@
}
/* XXX can we better detect its death? */
- adb_sleep_ms(2000);
+ std::this_thread::sleep_for(2s);
goto start_server;
}
}
@@ -268,16 +238,13 @@
fd = _adb_connect(service, error);
if (fd == -1) {
- D("_adb_connect error: %s\n", error->c_str());
+ D("_adb_connect error: %s", error->c_str());
} else if(fd == -2) {
fprintf(stderr,"** daemon still not running\n");
}
- D("adb_connect: return fd %d\n", fd);
+ D("adb_connect: return fd %d", fd);
return fd;
-error:
- adb_close(fd);
- return -1;
}
@@ -295,11 +262,13 @@
return false;
}
+ ReadOrderlyShutdown(fd);
+ adb_close(fd);
return true;
}
bool adb_query(const std::string& service, std::string* result, std::string* error) {
- D("adb_query: %s\n", service.c_str());
+ D("adb_query: %s", service.c_str());
int fd = adb_connect(service, error);
if (fd < 0) {
return false;
@@ -310,5 +279,32 @@
adb_close(fd);
return false;
}
+
+ ReadOrderlyShutdown(fd);
+ adb_close(fd);
return true;
}
+
+std::string format_host_command(const char* command, TransportType type, const char* serial) {
+ if (serial) {
+ return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+ }
+
+ const char* prefix = "host";
+ if (type == kTransportUsb) {
+ prefix = "host-usb";
+ } else if (type == kTransportLocal) {
+ prefix = "host-local";
+ }
+ return android::base::StringPrintf("%s:%s", prefix, command);
+}
+
+bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) {
+ std::string result;
+ if (adb_query(format_host_command("features", __adb_transport, __adb_serial), &result, error)) {
+ *feature_set = StringToFeatureSet(result);
+ return true;
+ }
+ feature_set->clear();
+ return false;
+}
diff --git a/adb/adb_client.h b/adb/adb_client.h
index 5de0638..d07c1e9 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -18,13 +18,15 @@
#define _ADB_CLIENT_H_
#include "adb.h"
+#include "sysdeps.h"
+#include "transport.h"
#include <string>
// Connect to adb, connect to the named service, and return a valid fd for
// interacting with that service upon success or a negative number on failure.
-int adb_connect(const std::string& service, std::string* error);
-int _adb_connect(const std::string& service, std::string* error);
+int adb_connect(const std::string& service, std::string* _Nonnull error);
+int _adb_connect(const std::string& service, std::string* _Nonnull error);
// Connect to adb, connect to the named service, returns true if the connection
// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
@@ -32,24 +34,31 @@
// Connects to the named adb service and fills 'result' with the response.
// Returns true on success; returns false and fills 'error' on failure.
-bool adb_query(const std::string& service, std::string* result, std::string* error);
+bool adb_query(const std::string& service, std::string* _Nonnull result,
+ std::string* _Nonnull error);
// Set the preferred transport to connect to.
-void adb_set_transport(TransportType type, const char* serial);
+void adb_set_transport(TransportType type, const char* _Nullable serial);
-// Set TCP specifics of the transport to use.
-void adb_set_tcp_specifics(int server_port);
-
-// Set TCP Hostname of the transport to use.
-void adb_set_tcp_name(const char* hostname);
+// Set the socket specification for the adb server.
+// This function can only be called once, and the argument must live to the end of the process.
+void adb_set_socket_spec(const char* _Nonnull socket_spec);
// Send commands to the current emulator instance. Will fail if there is not
// exactly one emulator connected (or if you use -s <serial> with a <serial>
// that does not designate an emulator).
-int adb_send_emulator_command(int argc, const char** argv, const char* serial);
+int adb_send_emulator_command(int argc, const char* _Nonnull* _Nonnull argv,
+ const char* _Nullable serial);
// Reads a standard adb status response (OKAY|FAIL) and returns true in the
// event of OKAY, false in the event of FAIL or protocol error.
-bool adb_status(int fd, std::string* error);
+bool adb_status(int fd, std::string* _Nonnull error);
+
+// Create a host command corresponding to selected transport type/serial.
+std::string format_host_command(const char* _Nonnull command, TransportType type,
+ const char* _Nullable serial);
+
+// Get the feature set of the current preferred transport.
+bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
#endif
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 5ae6ec3..ca8729e 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -14,25 +14,49 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_RWX
+#define TRACE_TAG RWX
#include "adb_io.h"
#include <unistd.h>
-#include <base/stringprintf.h>
+#include <thread>
+#include <android-base/stringprintf.h>
+
+#include "adb.h"
#include "adb_trace.h"
#include "adb_utils.h"
#include "sysdeps.h"
bool SendProtocolString(int fd, const std::string& s) {
- int length = s.size();
- if (length > 0xffff) {
- length = 0xffff;
+ unsigned int length = s.size();
+ if (length > MAX_PAYLOAD_V1 - 4) {
+ errno = EMSGSIZE;
+ return false;
}
- return WriteFdFmt(fd, "%04x", length) && WriteFdExactly(fd, s);
+ // The cost of sending two strings outweighs the cost of formatting.
+ // "adb sync" performance is affected by this.
+ return WriteFdFmt(fd, "%04x%.*s", length, length, s.c_str());
+}
+
+bool ReadProtocolString(int fd, std::string* s, std::string* error) {
+ char buf[5];
+ if (!ReadFdExactly(fd, buf, 4)) {
+ *error = perror_str("protocol fault (couldn't read status length)");
+ return false;
+ }
+ buf[4] = 0;
+
+ unsigned long len = strtoul(buf, 0, 16);
+ s->resize(len, '\0');
+ if (!ReadFdExactly(fd, &(*s)[0], len)) {
+ *error = perror_str("protocol fault (couldn't read status message)");
+ return false;
+ }
+
+ return true;
}
bool SendOkay(int fd) {
@@ -48,26 +72,24 @@
size_t len0 = len;
- D("readx: fd=%d wanted=%zu\n", fd, len);
+ D("readx: fd=%d wanted=%zu", fd, len);
while (len > 0) {
int r = adb_read(fd, p, len);
if (r > 0) {
len -= r;
p += r;
} else if (r == -1) {
- D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+ D("readx: fd=%d error %d: %s", fd, errno, strerror(errno));
return false;
} else {
- D("readx: fd=%d disconnected\n", fd);
+ D("readx: fd=%d disconnected", fd);
errno = 0;
return false;
}
}
- D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len);
- if (ADB_TRACING) {
- dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
- }
+ VLOG(RWX) << "readx: fd=" << fd << " wanted=" << len0 << " got=" << (len0 - len)
+ << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
return true;
}
@@ -76,20 +98,18 @@
const char* p = reinterpret_cast<const char*>(buf);
int r;
- D("writex: fd=%d len=%d: ", fd, (int)len);
- if (ADB_TRACING) {
- dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
- }
+ VLOG(RWX) << "writex: fd=" << fd << " len=" << len
+ << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
while (len > 0) {
r = adb_write(fd, p, len);
if (r == -1) {
- D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+ D("writex: fd=%d error %d: %s", fd, errno, strerror(errno));
if (errno == EAGAIN) {
- adb_sleep_ms(1); // just yield some cpu time
+ std::this_thread::yield();
continue;
} else if (errno == EPIPE) {
- D("writex: fd=%d disconnected\n", fd);
+ D("writex: fd=%d disconnected", fd);
errno = 0;
return false;
} else {
@@ -121,3 +141,43 @@
return WriteFdExactly(fd, str);
}
+
+bool ReadOrderlyShutdown(int fd) {
+ char buf[16];
+
+ // Only call this function if you're sure that the peer does
+ // orderly/graceful shutdown of the socket, closing the socket so that
+ // adb_read() will return 0. If the peer keeps the socket open, adb_read()
+ // will never return.
+ int result = adb_read(fd, buf, sizeof(buf));
+ if (result == -1) {
+ // If errno is EAGAIN, that means this function was called on a
+ // nonblocking socket and it would have blocked (which would be bad
+ // because we'd probably block the main thread where nonblocking IO is
+ // done). Don't do that. If you have a nonblocking socket, use the
+ // fdevent APIs to get called on FDE_READ, and then call this function
+ // if you really need to, but it shouldn't be needed for server sockets.
+ CHECK_NE(errno, EAGAIN);
+
+ // Note that on Windows, orderly shutdown sometimes causes
+ // recv() == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET. That
+ // can be ignored.
+ return false;
+ } else if (result == 0) {
+ // Peer has performed an orderly/graceful shutdown.
+ return true;
+ } else {
+ // Unexpectedly received data. This is essentially a protocol error
+ // because you should not call this function unless you expect no more
+ // data. We don't repeatedly call adb_read() until we get zero because
+ // we don't know how long that would take, but we do know that the
+ // caller wants to close the socket soon.
+ VLOG(RWX) << "ReadOrderlyShutdown(" << fd << ") unexpectedly read "
+ << dump_hex(buf, result);
+ // Shutdown the socket to prevent the caller from reading or writing to
+ // it which doesn't make sense if we just read and discarded some data.
+ adb_shutdown(fd);
+ errno = EINVAL;
+ return false;
+ }
+}
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 8d50a6d..aa550af 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -30,23 +30,40 @@
// Writes a protocol-format string; a four hex digit length followed by the string data.
bool SendProtocolString(int fd, const std::string& s);
-/*
- * Reads exactly len bytes from fd into buf.
- *
- * Returns false if there is an error or if EOF was reached before len bytes
- * were read. If EOF was found, errno will be set to 0.
- *
- * If this function fails, the contents of buf are undefined.
- */
-bool ReadFdExactly(int fd, void *buf, size_t len);
+// Reads a protocol-format string; a four hex digit length followed by the string data.
+bool ReadProtocolString(int fd, std::string* s, std::string* error);
-/*
- * Writes exactly len bytes from buf to fd.
- *
- * Returns false if there is an error or if the fd was closed before the write
- * completed. If the other end of the fd (such as in a socket, pipe, or fifo),
- * is closed, errno will be set to 0.
- */
+// Reads exactly len bytes from fd into buf.
+//
+// Returns false if there is an error or if EOF was reached before len bytes
+// were read. If EOF was found, errno will be set to 0.
+//
+// If this function fails, the contents of buf are undefined.
+bool ReadFdExactly(int fd, void* buf, size_t len);
+
+// Given a client socket, wait for orderly/graceful shutdown. Call this:
+//
+// * Before closing a client socket.
+// * Only when no more data is expected to come in.
+// * Only when the server is not waiting for data from the client (because then
+// the client and server will deadlock waiting for each other).
+// * Only when the server is expected to close its socket right now.
+// * Don't call shutdown(SHUT_WR) before calling this because that will shutdown
+// the client socket early, defeating the purpose of calling this.
+//
+// Waiting for orderly/graceful shutdown of the server socket will cause the
+// server socket to close before the client socket. That prevents the client
+// socket from staying in TIME_WAIT which eventually causes subsequent
+// connect()s from the client to fail with WSAEADDRINUSE on Windows.
+// Returns true if it is sure that orderly/graceful shutdown has occurred with
+// no additional data read from the server.
+bool ReadOrderlyShutdown(int fd);
+
+// Writes exactly len bytes from buf to fd.
+//
+// Returns false if there is an error or if the fd was closed before the write
+// completed. If the other end of the fd (such as in a socket, pipe, or fifo),
+// is closed, errno will be set to 0.
bool WriteFdExactly(int fd, const void* buf, size_t len);
// Same as above, but for strings.
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index f637073..611b239 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -27,22 +27,29 @@
#include <string>
-#include "base/file.h"
-#include "base/test_utils.h"
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
// All of these tests fail on Windows because they use the C Runtime open(),
-// but the adb_io APIs expect file descriptors from adb_open(). Also, the
-// android::base file APIs use the C Runtime which uses CR/LF translation by
-// default (changeable with _setmode()), but the adb_io APIs use adb_read()
-// and adb_write() which do no translation.
+// but the adb_io APIs expect file descriptors from adb_open(). This could
+// theoretically be fixed by making adb_read()/adb_write() fallback to using
+// read()/write() if an unrecognized fd is used, and by making adb_open() return
+// fds far from the range that open() returns. But all of that might defeat the
+// purpose of the tests.
-TEST(io, ReadFdExactly_whole) {
+#if defined(_WIN32)
+#define POSIX_TEST(x,y) TEST(DISABLED_ ## x,y)
+#else
+#define POSIX_TEST TEST
+#endif
+
+POSIX_TEST(io, ReadFdExactly_whole) {
const char expected[] = "Foobar";
TemporaryFile tf;
ASSERT_NE(-1, tf.fd);
ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
// Test reading the whole file.
char buf[sizeof(expected)] = {};
@@ -50,13 +57,13 @@
EXPECT_STREQ(expected, buf);
}
-TEST(io, ReadFdExactly_eof) {
+POSIX_TEST(io, ReadFdExactly_eof) {
const char expected[] = "Foobar";
TemporaryFile tf;
ASSERT_NE(-1, tf.fd);
ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
// Test that not having enough data will fail.
char buf[sizeof(expected) + 1] = {};
@@ -64,13 +71,13 @@
EXPECT_EQ(0, errno) << strerror(errno);
}
-TEST(io, ReadFdExactly_partial) {
+POSIX_TEST(io, ReadFdExactly_partial) {
const char input[] = "Foobar";
TemporaryFile tf;
ASSERT_NE(-1, tf.fd);
ASSERT_TRUE(android::base::WriteStringToFd(input, tf.fd)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
// Test reading a partial file.
char buf[sizeof(input) - 1] = {};
@@ -81,7 +88,7 @@
EXPECT_STREQ(expected.c_str(), buf);
}
-TEST(io, WriteFdExactly_whole) {
+POSIX_TEST(io, WriteFdExactly_whole) {
const char expected[] = "Foobar";
TemporaryFile tf;
ASSERT_NE(-1, tf.fd);
@@ -89,21 +96,21 @@
// Test writing the whole string to the file.
ASSERT_TRUE(WriteFdExactly(tf.fd, expected, sizeof(expected)))
<< strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
std::string s;
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
EXPECT_STREQ(expected, s.c_str());
}
-TEST(io, WriteFdExactly_partial) {
+POSIX_TEST(io, WriteFdExactly_partial) {
const char buf[] = "Foobar";
TemporaryFile tf;
ASSERT_NE(-1, tf.fd);
// Test writing a partial string to the file.
ASSERT_TRUE(WriteFdExactly(tf.fd, buf, sizeof(buf) - 2)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
std::string expected(buf);
expected.pop_back();
@@ -113,7 +120,7 @@
EXPECT_EQ(expected, s);
}
-TEST(io, WriteFdExactly_ENOSPC) {
+POSIX_TEST(io, WriteFdExactly_ENOSPC) {
int fd = open("/dev/full", O_WRONLY);
ASSERT_NE(-1, fd);
@@ -122,27 +129,27 @@
ASSERT_EQ(ENOSPC, errno);
}
-TEST(io, WriteFdExactly_string) {
+POSIX_TEST(io, WriteFdExactly_string) {
const char str[] = "Foobar";
TemporaryFile tf;
ASSERT_NE(-1, tf.fd);
// Test writing a partial string to the file.
ASSERT_TRUE(WriteFdExactly(tf.fd, str)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
std::string s;
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
EXPECT_STREQ(str, s.c_str());
}
-TEST(io, WriteFdFmt) {
+POSIX_TEST(io, WriteFdFmt) {
TemporaryFile tf;
ASSERT_NE(-1, tf.fd);
// Test writing a partial string to the file.
ASSERT_TRUE(WriteFdFmt(tf.fd, "Foo%s%d", "bar", 123)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
std::string s;
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 8fb2d19..18b1492 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -19,35 +19,64 @@
#include <stdio.h>
#include <stdlib.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/sockets.h>
+#include "socket_spec.h"
#include "sysdeps.h"
#include "transport.h"
-int gListenAll = 0; /* Not static because it is used in commandline.c. */
+// A listener is an entity which binds to a local port and, upon receiving a connection on that
+// port, creates an asocket to connect the new local connection to a specific remote service.
+//
+// TODO: some listeners read from the new connection to determine what exact service to connect to
+// on the far side.
+class alistener {
+ public:
+ alistener(const std::string& _local_name, const std::string& _connect_to);
+ ~alistener();
-static alistener listener_list = {
- .next = &listener_list,
- .prev = &listener_list,
+ fdevent fde;
+ int fd = -1;
+
+ std::string local_name;
+ std::string connect_to;
+ atransport* transport = nullptr;
+ adisconnect disconnect;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(alistener);
};
+alistener::alistener(const std::string& _local_name, const std::string& _connect_to)
+ : local_name(_local_name), connect_to(_connect_to) {
+}
+
+alistener::~alistener() {
+ // Closes the corresponding fd.
+ fdevent_remove(&fde);
+
+ if (transport) {
+ transport->RemoveDisconnect(&disconnect);
+ }
+}
+
+// listener_list retains ownership of all created alistener objects. Removing an alistener from
+// this list will cause it to be deleted.
+typedef std::list<std::unique_ptr<alistener>> ListenerList;
+static ListenerList& listener_list = *new ListenerList();
+
static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
- asocket *s;
+ if (ev & FDE_READ) {
+ int fd = adb_socket_accept(_fd, nullptr, nullptr);
+ if (fd < 0) return;
- if(ev & FDE_READ) {
- struct sockaddr addr;
- socklen_t alen;
- int fd;
+ int rcv_buf_size = CHUNK_SIZE;
+ adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size));
- alen = sizeof(addr);
- fd = adb_socket_accept(_fd, &addr, &alen);
- if(fd < 0) return;
-
- adb_socket_setbufsize(fd, CHUNK_SIZE);
-
- s = create_local_socket(fd);
- if(s) {
+ asocket* s = create_local_socket(fd);
+ if (s) {
connect_to_smartsocket(s);
return;
}
@@ -62,12 +91,7 @@
asocket *s;
if (ev & FDE_READ) {
- struct sockaddr addr;
- socklen_t alen;
- int fd;
-
- alen = sizeof(addr);
- fd = adb_socket_accept(_fd, &addr, &alen);
+ int fd = adb_socket_accept(_fd, nullptr, nullptr);
if (fd < 0) {
return;
}
@@ -75,7 +99,7 @@
s = create_local_socket(fd);
if (s) {
s->transport = listener->transport;
- connect_to_remote(s, listener->connect_to);
+ connect_to_remote(s, listener->connect_to.c_str());
return;
}
@@ -83,64 +107,21 @@
}
}
-static void free_listener(alistener* l)
-{
- if (l->next) {
- l->next->prev = l->prev;
- l->prev->next = l->next;
- l->next = l->prev = l;
- }
-
- // closes the corresponding fd
- fdevent_remove(&l->fde);
-
- if (l->local_name)
- free((char*)l->local_name);
-
- if (l->connect_to)
- free((char*)l->connect_to);
-
- if (l->transport) {
- remove_transport_disconnect(l->transport, &l->disconnect);
- }
- free(l);
-}
-
-static void listener_disconnect(void* listener, atransport* t) {
- free_listener(reinterpret_cast<alistener*>(listener));
-}
-
-static int local_name_to_fd(const char* name, std::string* error) {
- if (!strncmp("tcp:", name, 4)) {
- int port = atoi(name + 4);
- if (gListenAll > 0) {
- return network_inaddr_any_server(port, SOCK_STREAM, error);
- } else {
- return network_loopback_server(port, SOCK_STREAM, error);
+// Called as a transport disconnect function. |arg| is the raw alistener*.
+static void listener_disconnect(void* arg, atransport*) {
+ for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
+ if (iter->get() == arg) {
+ (*iter)->transport = nullptr;
+ listener_list.erase(iter);
+ return;
}
}
-#if !defined(_WIN32) // No Unix-domain sockets on Windows.
- // It's nonsensical to support the "reserved" space on the adb host side
- if (!strncmp(name, "local:", 6)) {
- return network_local_server(name + 6,
- ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM, error);
- } else if (!strncmp(name, "localabstract:", 14)) {
- return network_local_server(name + 14,
- ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM, error);
- } else if (!strncmp(name, "localfilesystem:", 16)) {
- return network_local_server(name + 16,
- ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM, error);
- }
-
-#endif
- *error = android::base::StringPrintf("unknown local portname '%s'", name);
- return -1;
}
// Write the list of current listeners (network redirections) into a string.
std::string format_listeners() {
std::string result;
- for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
+ for (auto& l : listener_list) {
// Ignore special listeners like those for *smartsocket*
if (l->connect_to[0] == '*') {
continue;
@@ -149,122 +130,92 @@
// Entries from "adb reverse" have no serial.
android::base::StringAppendF(&result, "%s %s %s\n",
l->transport->serial ? l->transport->serial : "(reverse)",
- l->local_name, l->connect_to);
+ l->local_name.c_str(), l->connect_to.c_str());
}
return result;
}
-InstallStatus remove_listener(const char *local_name, atransport* transport) {
- alistener *l;
-
- for (l = listener_list.next; l != &listener_list; l = l->next) {
- if (!strcmp(local_name, l->local_name)) {
- listener_disconnect(l, l->transport);
+InstallStatus remove_listener(const char* local_name, atransport* transport) {
+ for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
+ if (local_name == (*iter)->local_name) {
+ listener_list.erase(iter);
return INSTALL_STATUS_OK;
}
}
return INSTALL_STATUS_LISTENER_NOT_FOUND;
}
-void remove_all_listeners(void)
-{
- alistener *l, *l_next;
- for (l = listener_list.next; l != &listener_list; l = l_next) {
- l_next = l->next;
+void remove_all_listeners() {
+ auto iter = listener_list.begin();
+ while (iter != listener_list.end()) {
// Never remove smart sockets.
- if (l->connect_to[0] == '*')
- continue;
- listener_disconnect(l, l->transport);
+ if ((*iter)->connect_to[0] == '*') {
+ ++iter;
+ } else {
+ iter = listener_list.erase(iter);
+ }
}
}
-InstallStatus install_listener(const std::string& local_name,
- const char *connect_to,
- atransport* transport,
- int no_rebind,
- std::string* error)
-{
- for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
+InstallStatus install_listener(const std::string& local_name, const char* connect_to,
+ atransport* transport, int no_rebind, int* resolved_tcp_port,
+ std::string* error) {
+ for (auto& l : listener_list) {
if (local_name == l->local_name) {
- char* cto;
-
- /* can't repurpose a smartsocket */
+ // Can't repurpose a smartsocket.
if(l->connect_to[0] == '*') {
*error = "cannot repurpose smartsocket";
return INSTALL_STATUS_INTERNAL_ERROR;
}
- /* can't repurpose a listener if 'no_rebind' is true */
+ // Can't repurpose a listener if 'no_rebind' is true.
if (no_rebind) {
*error = "cannot rebind";
return INSTALL_STATUS_CANNOT_REBIND;
}
- cto = strdup(connect_to);
- if(cto == 0) {
- *error = "cannot duplicate string";
- return INSTALL_STATUS_INTERNAL_ERROR;
- }
-
- free((void*) l->connect_to);
- l->connect_to = cto;
+ l->connect_to = connect_to;
if (l->transport != transport) {
- remove_transport_disconnect(l->transport, &l->disconnect);
+ l->transport->RemoveDisconnect(&l->disconnect);
l->transport = transport;
- add_transport_disconnect(l->transport, &l->disconnect);
+ l->transport->AddDisconnect(&l->disconnect);
}
return INSTALL_STATUS_OK;
}
}
- alistener* listener = reinterpret_cast<alistener*>(
- calloc(1, sizeof(alistener)));
- if (listener == nullptr) {
- goto nomem;
- }
+ std::unique_ptr<alistener> listener(new alistener(local_name, connect_to));
- listener->local_name = strdup(local_name.c_str());
- if (listener->local_name == nullptr) {
- goto nomem;
- }
-
- listener->connect_to = strdup(connect_to);
- if (listener->connect_to == nullptr) {
- goto nomem;
- }
-
- listener->fd = local_name_to_fd(listener->local_name, error);
+ int resolved = 0;
+ listener->fd = socket_spec_listen(listener->local_name, error, &resolved);
if (listener->fd < 0) {
- free(listener->local_name);
- free(listener->connect_to);
- free(listener);
return INSTALL_STATUS_CANNOT_BIND;
}
+ // If the caller requested port 0, update the listener name with the resolved port.
+ if (resolved != 0) {
+ listener->local_name = android::base::StringPrintf("tcp:%d", resolved);
+ if (resolved_tcp_port) {
+ *resolved_tcp_port = resolved;
+ }
+ }
+
close_on_exec(listener->fd);
- if (!strcmp(listener->connect_to, "*smartsocket*")) {
- fdevent_install(&listener->fde, listener->fd, ss_listener_event_func,
- listener);
+ if (listener->connect_to == "*smartsocket*") {
+ fdevent_install(&listener->fde, listener->fd, ss_listener_event_func, listener.get());
} else {
- fdevent_install(&listener->fde, listener->fd, listener_event_func,
- listener);
+ fdevent_install(&listener->fde, listener->fd, listener_event_func, listener.get());
}
fdevent_set(&listener->fde, FDE_READ);
- listener->next = &listener_list;
- listener->prev = listener_list.prev;
- listener->next->prev = listener;
- listener->prev->next = listener;
listener->transport = transport;
if (transport) {
- listener->disconnect.opaque = listener;
+ listener->disconnect.opaque = listener.get();
listener->disconnect.func = listener_disconnect;
- add_transport_disconnect(transport, &listener->disconnect);
+ transport->AddDisconnect(&listener->disconnect);
}
- return INSTALL_STATUS_OK;
-nomem:
- fatal("cannot allocate listener");
- return INSTALL_STATUS_INTERNAL_ERROR;
+ listener_list.push_back(std::move(listener));
+ return INSTALL_STATUS_OK;
}
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index fa98eed..8eba00a 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -21,6 +21,8 @@
#include <string>
+#include <android-base/macros.h>
+
// error/status codes for install_listener.
enum InstallStatus {
INSTALL_STATUS_OK = 0,
@@ -30,10 +32,8 @@
INSTALL_STATUS_LISTENER_NOT_FOUND = -4,
};
-InstallStatus install_listener(const std::string& local_name,
- const char* connect_to,
- atransport* transport,
- int no_rebind,
+InstallStatus install_listener(const std::string& local_name, const char* connect_to,
+ atransport* transport, int no_rebind, int* resolved_tcp_port,
std::string* error);
std::string format_listeners();
diff --git a/adb/adb_listeners_test.cpp b/adb/adb_listeners_test.cpp
new file mode 100644
index 0000000..b697769
--- /dev/null
+++ b/adb/adb_listeners_test.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb_listeners.h"
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "fdevent.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+// Returns true if the given listener is present in format_listeners(). Empty parameters will
+// be ignored.
+static bool listener_is_installed(const std::string& serial, const std::string& source,
+ const std::string& dest) {
+ // format_listeners() gives lines of "<serial> <source> <dest>\n".
+ for (const std::string& line : android::base::Split(format_listeners(), "\n")) {
+ std::vector<std::string> info = android::base::Split(line, " ");
+ if (info.size() == 3 &&
+ (serial.empty() || info[0] == serial) &&
+ (source.empty() || info[1] == source) &&
+ (dest.empty() || info[2] == dest)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+class AdbListenersTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ // We don't need an fdevent loop, but adding/removing listeners must be done from the
+ // fdevent thread if one exists. Since previously run tests may have created an fdevent
+ // thread, we need to reset to prevent the thread check.
+ fdevent_reset();
+ }
+
+ void TearDown() override {
+ // Clean up any listeners that may have been installed.
+ remove_all_listeners();
+
+ // Make sure we didn't leave any dangling events.
+ ASSERT_EQ(0u, fdevent_installed_count());
+ }
+
+ protected:
+ atransport transport_;
+};
+
+TEST_F(AdbListenersTest, test_install_listener) {
+ std::string error;
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ ASSERT_TRUE(listener_is_installed("", "tcp:9000", "tcp:9000"));
+}
+
+TEST_F(AdbListenersTest, test_install_listener_rebind) {
+ std::string error;
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9000", "tcp:9001", &transport_, false, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ ASSERT_TRUE(listener_is_installed("", "tcp:9000", "tcp:9001"));
+}
+
+TEST_F(AdbListenersTest, test_install_listener_no_rebind) {
+ std::string error;
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9000", "tcp:9000", &transport_, true, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ ASSERT_EQ(INSTALL_STATUS_CANNOT_REBIND,
+ install_listener("tcp:9000", "tcp:9001", &transport_, true, nullptr, &error));
+ ASSERT_FALSE(error.empty());
+
+ ASSERT_TRUE(listener_is_installed("", "tcp:9000", "tcp:9000"));
+}
+
+TEST_F(AdbListenersTest, test_install_listener_tcp_port_0) {
+ int port = 0;
+ std::string error;
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:0", "tcp:9000", &transport_, true, &port, &error));
+ ASSERT_TRUE(error.empty());
+
+ ASSERT_TRUE(listener_is_installed("", android::base::StringPrintf("tcp:%d", port), "tcp:9000"));
+}
+
+TEST_F(AdbListenersTest, test_remove_listener) {
+ std::string error;
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ ASSERT_EQ(INSTALL_STATUS_OK, remove_listener("tcp:9000", &transport_));
+ ASSERT_TRUE(format_listeners().empty());
+}
+
+TEST_F(AdbListenersTest, test_remove_nonexistent_listener) {
+ std::string error;
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ ASSERT_EQ(INSTALL_STATUS_LISTENER_NOT_FOUND, remove_listener("tcp:1", &transport_));
+ ASSERT_TRUE(listener_is_installed("", "tcp:9000", "tcp:9000"));
+}
+
+TEST_F(AdbListenersTest, test_remove_all_listeners) {
+ std::string error;
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9001", "tcp:9001", &transport_, false, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ remove_all_listeners();
+ ASSERT_TRUE(format_listeners().empty());
+}
+
+TEST_F(AdbListenersTest, test_transport_disconnect) {
+ std::string error;
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9000", "tcp:9000", &transport_, false, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ ASSERT_EQ(INSTALL_STATUS_OK,
+ install_listener("tcp:9001", "tcp:9001", &transport_, false, nullptr, &error));
+ ASSERT_TRUE(error.empty());
+
+ transport_.RunDisconnects();
+ ASSERT_TRUE(format_listeners().empty());
+}
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
new file mode 100644
index 0000000..c369d60
--- /dev/null
+++ b/adb/adb_trace.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps.h"
+#include "adb_trace.h"
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "adb.h"
+
+#if !ADB_HOST
+#include <android-base/properties.h>
+#endif
+
+#if !ADB_HOST
+const char* adb_device_banner = "device";
+static android::base::LogdLogger gLogdLogger;
+#else
+const char* adb_device_banner = "host";
+#endif
+
+void AdbLogger(android::base::LogId id, android::base::LogSeverity severity,
+ const char* tag, const char* file, unsigned int line,
+ const char* message) {
+ android::base::StderrLogger(id, severity, tag, file, line, message);
+#if !ADB_HOST
+ gLogdLogger(id, severity, tag, file, line, message);
+#endif
+}
+
+
+#if !ADB_HOST
+static std::string get_log_file_name() {
+ struct tm now;
+ time_t t;
+ tzset();
+ time(&t);
+ localtime_r(&t, &now);
+
+ char timestamp[PATH_MAX];
+ strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
+
+ return android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp,
+ getpid());
+}
+
+void start_device_log(void) {
+ int fd = unix_open(get_log_file_name().c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+ if (fd == -1) {
+ return;
+ }
+
+ // Redirect stdout and stderr to the log file.
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+ unix_close(fd);
+}
+#endif
+
+int adb_trace_mask;
+
+std::string get_trace_setting_from_env() {
+ const char* setting = getenv("ADB_TRACE");
+ if (setting == nullptr) {
+ setting = "";
+ }
+
+ return std::string(setting);
+}
+
+std::string get_trace_setting() {
+#if ADB_HOST
+ return get_trace_setting_from_env();
+#else
+ return android::base::GetProperty("persist.adb.trace_mask", "");
+#endif
+}
+
+// Split the space separated list of tags from the trace setting and build the
+// trace mask from it. note that '1' and 'all' are special cases to enable all
+// tracing.
+//
+// adb's trace setting comes from the ADB_TRACE environment variable, whereas
+// adbd's comes from the system property persist.adb.trace_mask.
+static void setup_trace_mask() {
+ const std::string trace_setting = get_trace_setting();
+ if (trace_setting.empty()) {
+ return;
+ }
+
+ std::unordered_map<std::string, int> trace_flags = {
+ {"1", -1},
+ {"all", -1},
+ {"adb", ADB},
+ {"sockets", SOCKETS},
+ {"packets", PACKETS},
+ {"rwx", RWX},
+ {"usb", USB},
+ {"sync", SYNC},
+ {"sysdeps", SYSDEPS},
+ {"transport", TRANSPORT},
+ {"jdwp", JDWP},
+ {"services", SERVICES},
+ {"auth", AUTH},
+ {"fdevent", FDEVENT},
+ {"shell", SHELL}};
+
+ std::vector<std::string> elements = android::base::Split(trace_setting, " ");
+ for (const auto& elem : elements) {
+ const auto& flag = trace_flags.find(elem);
+ if (flag == trace_flags.end()) {
+ LOG(ERROR) << "Unknown trace flag: " << elem;
+ continue;
+ }
+
+ if (flag->second == -1) {
+ // -1 is used for the special values "1" and "all" that enable all
+ // tracing.
+ adb_trace_mask = ~0;
+ return;
+ } else {
+ adb_trace_mask |= 1 << flag->second;
+ }
+ }
+}
+
+void adb_trace_init(char** argv) {
+#if !ADB_HOST
+ // Don't open log file if no tracing, since this will block
+ // the crypto unmount of /data
+ if (!get_trace_setting().empty()) {
+ if (unix_isatty(STDOUT_FILENO) == 0) {
+ start_device_log();
+ }
+ }
+#endif
+
+#if !defined(_WIN32)
+ // adb historically ignored $ANDROID_LOG_TAGS but passed it through to logcat.
+ // If set, move it out of the way so that libbase logging doesn't try to parse it.
+ std::string log_tags;
+ char* ANDROID_LOG_TAGS = getenv("ANDROID_LOG_TAGS");
+ if (ANDROID_LOG_TAGS) {
+ log_tags = ANDROID_LOG_TAGS;
+ unsetenv("ANDROID_LOG_TAGS");
+ }
+#endif
+
+ android::base::InitLogging(argv, &AdbLogger);
+
+#if !defined(_WIN32)
+ // Put $ANDROID_LOG_TAGS back so we can pass it to logcat.
+ if (!log_tags.empty()) setenv("ANDROID_LOG_TAGS", log_tags.c_str(), 1);
+#endif
+
+ setup_trace_mask();
+
+ VLOG(ADB) << adb_version();
+}
+
+void adb_trace_enable(AdbTrace trace_tag) {
+ adb_trace_mask |= (1 << trace_tag);
+}
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index dbc7ec8..aaffa29 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -17,100 +17,49 @@
#ifndef __ADB_TRACE_H
#define __ADB_TRACE_H
-#if !ADB_HOST
-#include <android/log.h>
-#else
-#include <stdio.h>
-#endif
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
/* IMPORTANT: if you change the following list, don't
* forget to update the corresponding 'tags' table in
- * the adb_trace_init() function implemented in adb.c
+ * the adb_trace_init() function implemented in adb_trace.cpp.
*/
enum AdbTrace {
- TRACE_ADB = 0, /* 0x001 */
- TRACE_SOCKETS,
- TRACE_PACKETS,
- TRACE_TRANSPORT,
- TRACE_RWX, /* 0x010 */
- TRACE_USB,
- TRACE_SYNC,
- TRACE_SYSDEPS,
- TRACE_JDWP, /* 0x100 */
- TRACE_SERVICES,
- TRACE_AUTH,
- TRACE_FDEVENT,
-} ;
+ ADB = 0, /* 0x001 */
+ SOCKETS,
+ PACKETS,
+ TRANSPORT,
+ RWX, /* 0x010 */
+ USB,
+ SYNC,
+ SYSDEPS,
+ JDWP, /* 0x100 */
+ SERVICES,
+ AUTH,
+ FDEVENT,
+ SHELL
+};
-#if !ADB_HOST
-/*
- * When running inside the emulator, guest's adbd can connect to 'adb-debug'
- * qemud service that can display adb trace messages (on condition that emulator
- * has been started with '-debug adb' option).
- */
+#define VLOG_IS_ON(TAG) \
+ ((adb_trace_mask & (1 << (TAG))) != 0)
-/* Delivers a trace message to the emulator via QEMU pipe. */
-void adb_qemu_trace(const char* fmt, ...);
-/* Macro to use to send ADB trace messages to the emulator. */
-#define DQ(...) adb_qemu_trace(__VA_ARGS__)
-#else
-#define DQ(...) ((void)0)
-#endif /* !ADB_HOST */
+#define VLOG(TAG) \
+ if (LIKELY(!VLOG_IS_ON(TAG))) \
+ ; \
+ else \
+ LOG(INFO)
+
+// You must define TRACE_TAG before using this macro.
+#define D(...) \
+ VLOG(TRACE_TAG) << android::base::StringPrintf(__VA_ARGS__)
+
extern int adb_trace_mask;
-extern unsigned char adb_trace_output_count;
void adb_trace_init(char**);
+void adb_trace_enable(AdbTrace trace_tag);
-# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
-
-/* you must define TRACE_TAG before using this macro */
-#if ADB_HOST
-# define D(...) \
- do { \
- if (ADB_TRACING) { \
- int save_errno = errno; \
- adb_mutex_lock(&D_lock); \
- fprintf(stderr, "%16s: %5d:%5lu | ", \
- __FUNCTION__, \
- getpid(), adb_thread_id()); \
- errno = save_errno; \
- fprintf(stderr, __VA_ARGS__ ); \
- fflush(stderr); \
- adb_mutex_unlock(&D_lock); \
- errno = save_errno; \
- } \
- } while (0)
-# define DR(...) \
- do { \
- if (ADB_TRACING) { \
- int save_errno = errno; \
- adb_mutex_lock(&D_lock); \
- errno = save_errno; \
- fprintf(stderr, __VA_ARGS__ ); \
- fflush(stderr); \
- adb_mutex_unlock(&D_lock); \
- errno = save_errno; \
- } \
- } while (0)
-#else
-# define D(...) \
- do { \
- if (ADB_TRACING) { \
- __android_log_print( \
- ANDROID_LOG_INFO, \
- __FUNCTION__, \
- __VA_ARGS__ ); \
- } \
- } while (0)
-# define DR(...) \
- do { \
- if (ADB_TRACING) { \
- __android_log_print( \
- ANDROID_LOG_INFO, \
- __FUNCTION__, \
- __VA_ARGS__ ); \
- } \
- } while (0)
-#endif /* ADB_HOST */
+#define ATRACE_TAG ATRACE_TAG_ADB
+#include <cutils/trace.h>
+#include <utils/Trace.h>
#endif /* __ADB_TRACE_H */
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
new file mode 100644
index 0000000..34c1bbc
--- /dev/null
+++ b/adb/adb_unique_fd.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+
+// Helper to automatically close an FD when it goes out of scope.
+struct AdbCloser {
+ static void Close(int fd);
+};
+
+using unique_fd = android::base::unique_fd_impl<AdbCloser>;
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index ca843bd..5a3b401 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
#include "adb_utils.h"
+#include "adb_unique_fd.h"
#include <libgen.h>
#include <stdlib.h>
@@ -25,15 +26,46 @@
#include <unistd.h>
#include <algorithm>
+#include <mutex>
+#include <vector>
-#include <base/logging.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include "adb.h"
#include "adb_trace.h"
#include "sysdeps.h"
-ADB_MUTEX_DEFINE(dirname_lock);
+#ifdef _WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include "windows.h"
+# include "shlobj.h"
+#else
+#include <pwd.h>
+#endif
+
+
+#if defined(_WIN32)
+constexpr char kNullFileName[] = "NUL";
+#else
+constexpr char kNullFileName[] = "/dev/null";
+#endif
+
+void close_stdin() {
+ int fd = unix_open(kNullFileName, O_RDONLY);
+ if (fd == -1) {
+ fatal_errno("failed to open %s", kNullFileName);
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(fd, STDIN_FILENO)) == -1) {
+ fatal_errno("failed to redirect stdin to %s", kNullFileName);
+ }
+ unix_close(fd);
+}
bool getcwd(std::string* s) {
char* cwd = getcwd(nullptr, 0);
@@ -69,34 +101,52 @@
}
std::string adb_basename(const std::string& path) {
- size_t base = path.find_last_of(OS_PATH_SEPARATORS);
- return (base != std::string::npos) ? path.substr(base + 1) : path;
-}
+ static std::mutex& basename_lock = *new std::mutex();
-std::string adb_dirname(const std::string& path) {
- // Copy path because dirname may modify the string passed in.
- std::string parent_storage(path);
+ // Copy path because basename may modify the string passed in.
+ std::string result(path);
- // Use lock because dirname() may write to a process global and return a
+ // Use lock because basename() may write to a process global and return a
// pointer to that. Note that this locking strategy only works if all other
- // callers to dirname in the process also grab this same lock.
- adb_mutex_lock(&dirname_lock);
+ // callers to basename in the process also grab this same lock.
+ std::lock_guard<std::mutex> lock(basename_lock);
// Note that if std::string uses copy-on-write strings, &str[0] will cause
// the copy to be made, so there is no chance of us accidentally writing to
// the storage for 'path'.
- char* parent = dirname(&parent_storage[0]);
+ char* name = basename(&result[0]);
// In case dirname returned a pointer to a process global, copy that string
// before leaving the lock.
- const std::string result(parent);
-
- adb_mutex_unlock(&dirname_lock);
+ result.assign(name);
return result;
}
-// Given a relative or absolute filepath, create the parent directory hierarchy
+std::string adb_dirname(const std::string& path) {
+ static std::mutex& dirname_lock = *new std::mutex();
+
+ // Copy path because dirname may modify the string passed in.
+ std::string result(path);
+
+ // Use lock because dirname() may write to a process global and return a
+ // pointer to that. Note that this locking strategy only works if all other
+ // callers to dirname in the process also grab this same lock.
+ std::lock_guard<std::mutex> lock(dirname_lock);
+
+ // Note that if std::string uses copy-on-write strings, &str[0] will cause
+ // the copy to be made, so there is no chance of us accidentally writing to
+ // the storage for 'path'.
+ char* parent = dirname(&result[0]);
+
+ // In case dirname returned a pointer to a process global, copy that string
+ // before leaving the lock.
+ result.assign(parent);
+
+ return result;
+}
+
+// Given a relative or absolute filepath, create the directory hierarchy
// as needed. Returns true if the hierarchy is/was setup.
bool mkdirs(const std::string& path) {
// TODO: all the callers do unlink && mkdirs && adb_creat ---
@@ -115,12 +165,14 @@
// - Recursive, so it uses stack space relative to number of directory
// components.
- const std::string parent(adb_dirname(path));
-
- if (directory_exists(parent)) {
+ // If path points to a symlink to a directory, that's fine.
+ struct stat sb;
+ if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
return true;
}
+ const std::string parent(adb_dirname(path));
+
// If dirname returned the same path as what we passed in, don't go recursive.
// This can happen on Windows when walking up the directory hierarchy and not
// finding anything that already exists (unlike POSIX that will eventually
@@ -130,20 +182,20 @@
return false;
}
- // Recursively make parent directories of 'parent'.
+ // Recursively make parent directories of 'path'.
if (!mkdirs(parent)) {
return false;
}
- // Now that the parent directory hierarchy of 'parent' has been ensured,
- // create parent itself.
- if (adb_mkdir(parent, 0775) == -1) {
- // Can't just check for errno == EEXIST because it might be a file that
- // exists.
+ // Now that the parent directory hierarchy of 'path' has been ensured,
+ // create path itself.
+ if (adb_mkdir(path, 0775) == -1) {
const int saved_errno = errno;
- if (directory_exists(parent)) {
+ // If someone else created the directory, that is ok.
+ if (directory_exists(path)) {
return true;
}
+ // There might be a pre-existing file at 'path', or there might have been some other error.
errno = saved_errno;
return false;
}
@@ -151,7 +203,7 @@
return true;
}
-void dump_hex(const void* data, size_t byte_count) {
+std::string dump_hex(const void* data, size_t byte_count) {
byte_count = std::min(byte_count, size_t(16));
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
@@ -163,65 +215,101 @@
line.push_back(' ');
for (size_t i = 0; i < byte_count; ++i) {
- int c = p[i];
- if (c < 32 || c > 127) {
- c = '.';
- }
- line.push_back(c);
+ int ch = p[i];
+ line.push_back(isprint(ch) ? ch : '.');
}
- DR("%s\n", line.c_str());
+ return line;
}
-bool parse_host_and_port(const std::string& address,
- std::string* canonical_address,
- std::string* host, int* port,
- std::string* error) {
- host->clear();
+std::string perror_str(const char* msg) {
+ return android::base::StringPrintf("%s: %s", msg, strerror(errno));
+}
- bool ipv6 = true;
- bool saw_port = false;
- size_t colons = std::count(address.begin(), address.end(), ':');
- size_t dots = std::count(address.begin(), address.end(), '.');
- std::string port_str;
- if (address[0] == '[') {
- // [::1]:123
- if (address.rfind("]:") == std::string::npos) {
- *error = android::base::StringPrintf("bad IPv6 address '%s'", address.c_str());
+#if !defined(_WIN32)
+// Windows version provided in sysdeps_win32.cpp
+bool set_file_block_mode(int fd, bool block) {
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
return false;
- }
- *host = address.substr(1, (address.find("]:") - 1));
- port_str = address.substr(address.rfind("]:") + 2);
- saw_port = true;
- } else if (dots == 0 && colons >= 2 && colons <= 7) {
- // ::1
- *host = address;
- } else if (colons <= 1) {
- // 1.2.3.4 or some.accidental.domain.com
- ipv6 = false;
- std::vector<std::string> pieces = android::base::Split(address, ":");
- *host = pieces[0];
- if (pieces.size() > 1) {
- port_str = pieces[1];
- saw_port = true;
- }
}
-
- if (host->empty()) {
- *error = android::base::StringPrintf("no host in '%s'", address.c_str());
- return false;
- }
-
- if (saw_port) {
- if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 || *port > 65535) {
- *error = android::base::StringPrintf("bad port number '%s' in '%s'",
- port_str.c_str(), address.c_str());
+ flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
+ if (fcntl(fd, F_SETFL, flags) != 0) {
+ PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
return false;
- }
}
-
- *canonical_address = android::base::StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
- LOG(DEBUG) << "parsed " << address << " as " << *host << " and " << *port
- << " (" << *canonical_address << ")";
return true;
}
+#endif
+
+bool forward_targets_are_valid(const std::string& source, const std::string& dest,
+ std::string* error) {
+ if (android::base::StartsWith(source, "tcp:")) {
+ // The source port may be 0 to allow the system to select an open port.
+ int port;
+ if (!android::base::ParseInt(&source[4], &port) || port < 0) {
+ *error = android::base::StringPrintf("Invalid source port: '%s'", &source[4]);
+ return false;
+ }
+ }
+
+ if (android::base::StartsWith(dest, "tcp:")) {
+ // The destination port must be > 0.
+ int port;
+ if (!android::base::ParseInt(&dest[4], &port) || port <= 0) {
+ *error = android::base::StringPrintf("Invalid destination port: '%s'", &dest[4]);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+std::string adb_get_homedir_path() {
+#ifdef _WIN32
+ WCHAR path[MAX_PATH];
+ const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
+ if (FAILED(hr)) {
+ D("SHGetFolderPathW failed: %s", android::base::SystemErrorCodeToString(hr).c_str());
+ return {};
+ }
+ std::string home_str;
+ if (!android::base::WideToUTF8(path, &home_str)) {
+ return {};
+ }
+ return home_str;
+#else
+ if (const char* const home = getenv("HOME")) {
+ return home;
+ }
+
+ struct passwd pwent;
+ struct passwd* result;
+ int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+ std::vector<char> buf(pwent_max);
+ int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);
+ if (rc == 0 && result) {
+ return result->pw_dir;
+ }
+
+ LOG(FATAL) << "failed to get user home directory";
+ return {};
+#endif
+}
+
+std::string adb_get_android_dir_path() {
+ std::string user_dir = adb_get_homedir_path();
+ std::string android_dir = user_dir + OS_PATH_SEPARATOR + ".android";
+ struct stat buf;
+ if (stat(android_dir.c_str(), &buf) == -1) {
+ if (adb_mkdir(android_dir.c_str(), 0750) == -1) {
+ PLOG(FATAL) << "Cannot mkdir '" << android_dir << "'";
+ }
+ }
+ return android_dir;
+}
+
+void AdbCloser::Close(int fd) {
+ adb_close(fd);
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 739efcc..16317e0 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,6 +19,10 @@
#include <string>
+#include <android-base/macros.h>
+
+void close_stdin();
+
bool getcwd(std::string* cwd);
bool directory_exists(const std::string& path);
@@ -27,21 +31,29 @@
std::string adb_basename(const std::string& path);
std::string adb_dirname(const std::string& path);
+// Return the user's home directory.
+std::string adb_get_homedir_path();
+
+// Return the adb user directory.
+std::string adb_get_android_dir_path();
+
bool mkdirs(const std::string& path);
std::string escape_arg(const std::string& s);
-void dump_hex(const void* ptr, size_t byte_count);
+std::string dump_hex(const void* ptr, size_t byte_count);
-// Parses 'address' into 'host' and 'port'.
-// If no port is given, takes the default from *port.
-// 'canonical_address' then becomes "host:port" or "[host]:port" as appropriate.
-// Note that no real checking is done that 'host' or 'port' is valid; that's
-// left to getaddrinfo(3).
-// Returns false on failure and sets *error to an appropriate message.
-bool parse_host_and_port(const std::string& address,
- std::string* canonical_address,
- std::string* host, int* port,
- std::string* error);
+std::string perror_str(const char* msg);
+
+bool set_file_block_mode(int fd, bool block);
+
+extern int adb_close(int fd);
+
+// Given forward/reverse targets, returns true if they look sane. If an error is found, fills
+// |error| and returns false.
+// Currently this only checks "tcp:" targets. Additional checking could be added for other targets
+// if needed.
+bool forward_targets_are_valid(const std::string& source, const std::string& dest,
+ std::string* error);
#endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 17c8d0a..4cac485 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -30,8 +30,8 @@
#include "sysdeps.h"
-#include <base/macros.h>
-#include <base/test_utils.h>
+#include <android-base/macros.h>
+#include <android-base/test_utils.h>
#ifdef _WIN32
static std::string subdir(const char* parent, const char* child) {
@@ -52,18 +52,6 @@
ASSERT_TRUE(directory_exists(profiles_dir));
- // On modern (English?) Windows, this is a directory symbolic link to
- // C:\ProgramData. Symbolic links are rare on Windows and the user requires
- // a special permission (by default granted to Administrative users) to
- // create symbolic links.
- ASSERT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
-
- // On modern (English?) Windows, this is a directory junction to
- // C:\Users\Default. Junctions are used throughout user profile directories
- // for backwards compatibility and they don't require any special permissions
- // to create.
- ASSERT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
-
ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
#else
ASSERT_TRUE(directory_exists("/proc"));
@@ -72,6 +60,28 @@
#endif
}
+#if defined(_WIN32)
+TEST(adb_utils, directory_exists_win32_symlink_junction) {
+ char profiles_dir[MAX_PATH];
+ DWORD cch = arraysize(profiles_dir);
+
+ // On typical Windows 7, returns C:\Users
+ ASSERT_TRUE(GetProfilesDirectoryA(profiles_dir, &cch));
+
+ // On modern (English?) Windows, this is a directory symbolic link to
+ // C:\ProgramData. Symbolic links are rare on Windows and the user requires
+ // a special permission (by default granted to Administrative users) to
+ // create symbolic links.
+ EXPECT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
+
+ // On modern (English?) Windows, this is a directory junction to
+ // C:\Users\Default. Junctions are used throughout user profile directories
+ // for backwards compatibility and they don't require any special permissions
+ // to create.
+ EXPECT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
+}
+#endif
+
TEST(adb_utils, escape_arg) {
ASSERT_EQ(R"('')", escape_arg(""));
@@ -102,103 +112,77 @@
TEST(adb_utils, adb_basename) {
EXPECT_EQ("sh", adb_basename("/system/bin/sh"));
EXPECT_EQ("sh", adb_basename("sh"));
+ EXPECT_EQ("sh", adb_basename("/system/bin/sh/"));
}
-TEST(adb_utils, parse_host_and_port) {
- std::string canonical_address;
- std::string host;
- int port;
- std::string error;
-
- // Name, default port.
- port = 123;
- ASSERT_TRUE(parse_host_and_port("www.google.com", &canonical_address, &host, &port, &error));
- ASSERT_EQ("www.google.com:123", canonical_address);
- ASSERT_EQ("www.google.com", host);
- ASSERT_EQ(123, port);
-
- // Name, explicit port.
- ASSERT_TRUE(parse_host_and_port("www.google.com:666", &canonical_address, &host, &port, &error));
- ASSERT_EQ("www.google.com:666", canonical_address);
- ASSERT_EQ("www.google.com", host);
- ASSERT_EQ(666, port);
-
- // IPv4, default port.
- port = 123;
- ASSERT_TRUE(parse_host_and_port("1.2.3.4", &canonical_address, &host, &port, &error));
- ASSERT_EQ("1.2.3.4:123", canonical_address);
- ASSERT_EQ("1.2.3.4", host);
- ASSERT_EQ(123, port);
-
- // IPv4, explicit port.
- ASSERT_TRUE(parse_host_and_port("1.2.3.4:666", &canonical_address, &host, &port, &error));
- ASSERT_EQ("1.2.3.4:666", canonical_address);
- ASSERT_EQ("1.2.3.4", host);
- ASSERT_EQ(666, port);
-
- // Simple IPv6, default port.
- port = 123;
- ASSERT_TRUE(parse_host_and_port("::1", &canonical_address, &host, &port, &error));
- ASSERT_EQ("[::1]:123", canonical_address);
- ASSERT_EQ("::1", host);
- ASSERT_EQ(123, port);
-
- // Simple IPv6, explicit port.
- ASSERT_TRUE(parse_host_and_port("[::1]:666", &canonical_address, &host, &port, &error));
- ASSERT_EQ("[::1]:666", canonical_address);
- ASSERT_EQ("::1", host);
- ASSERT_EQ(666, port);
-
- // Hairy IPv6, default port.
- port = 123;
- ASSERT_TRUE(parse_host_and_port("fe80::200:5aee:feaa:20a2", &canonical_address, &host, &port, &error));
- ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical_address);
- ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
- ASSERT_EQ(123, port);
-
- // Simple IPv6, explicit port.
- ASSERT_TRUE(parse_host_and_port("[fe80::200:5aee:feaa:20a2]:666", &canonical_address, &host, &port, &error));
- ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical_address);
- ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
- ASSERT_EQ(666, port);
-
- // Invalid IPv4.
- EXPECT_FALSE(parse_host_and_port("1.2.3.4:", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("1.2.3.4::", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("1.2.3.4:hello", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port(":123", &canonical_address, &host, &port, &error));
-
- // Invalid IPv6.
- EXPECT_FALSE(parse_host_and_port(":1", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("::::::::1", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("[::1", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("[::1]", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("[::1]:", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("[::1]::", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("[::1]:hello", &canonical_address, &host, &port, &error));
-
- // Invalid ports.
- EXPECT_FALSE(parse_host_and_port("[::1]:-1", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("[::1]:0", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("[::1]:65536", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("1.2.3.4:-1", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("1.2.3.4:0", &canonical_address, &host, &port, &error));
- EXPECT_FALSE(parse_host_and_port("1.2.3.4:65536", &canonical_address, &host, &port, &error));
+TEST(adb_utils, adb_dirname) {
+ EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh"));
+ EXPECT_EQ(".", adb_dirname("sh"));
+ EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/"));
}
-void test_mkdirs(const std::string basepath) {
- EXPECT_TRUE(mkdirs(basepath));
- EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
- EXPECT_FALSE(mkdirs(basepath + "/subdir/"));
+void test_mkdirs(const std::string& basepath) {
+ // Test creating a directory hierarchy.
+ ASSERT_TRUE(mkdirs(basepath));
+ // Test finding an existing directory hierarchy.
+ ASSERT_TRUE(mkdirs(basepath));
+ // Test mkdirs on an existing hierarchy with a trailing slash.
+ ASSERT_TRUE(mkdirs(basepath + '/'));
+#if defined(_WIN32)
+ ASSERT_TRUE(mkdirs(basepath + '\\'));
+#endif
+
+ const std::string filepath = basepath + "/file";
+ // Verify that the hierarchy was created by trying to create a file in it.
+ ASSERT_NE(-1, adb_creat(filepath.c_str(), 0600));
+ // If a file exists where we want a directory, the operation should fail.
+ ASSERT_FALSE(mkdirs(filepath));
}
TEST(adb_utils, mkdirs) {
TemporaryDir td;
// Absolute paths.
- test_mkdirs(std::string(td.path) + "/dir/subdir/file");
+ test_mkdirs(std::string(td.path) + "/dir/subdir");
// Relative paths.
ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
- test_mkdirs(std::string("relative/subrel/file"));
+ test_mkdirs(std::string("relative/subrel"));
+}
+
+#if !defined(_WIN32)
+TEST(adb_utils, set_file_block_mode) {
+ int fd = adb_open("/dev/null", O_RDWR | O_APPEND);
+ ASSERT_GE(fd, 0);
+ int flags = fcntl(fd, F_GETFL, 0);
+ ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
+ ASSERT_TRUE(set_file_block_mode(fd, false));
+ int new_flags = fcntl(fd, F_GETFL, 0);
+ ASSERT_EQ(flags | O_NONBLOCK, new_flags);
+ ASSERT_TRUE(set_file_block_mode(fd, true));
+ new_flags = fcntl(fd, F_GETFL, 0);
+ ASSERT_EQ(flags, new_flags);
+ ASSERT_EQ(0, adb_close(fd));
+}
+#endif
+
+TEST(adb_utils, test_forward_targets_are_valid) {
+ std::string error;
+
+ // Source port can be >= 0.
+ EXPECT_FALSE(forward_targets_are_valid("tcp:-1", "tcp:9000", &error));
+ EXPECT_TRUE(forward_targets_are_valid("tcp:0", "tcp:9000", &error));
+ EXPECT_TRUE(forward_targets_are_valid("tcp:8000", "tcp:9000", &error));
+
+ // Destination port must be >0.
+ EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:-1", &error));
+ EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:0", &error));
+
+ // Port must be a number.
+ EXPECT_FALSE(forward_targets_are_valid("tcp:", "tcp:9000", &error));
+ EXPECT_FALSE(forward_targets_are_valid("tcp:a", "tcp:9000", &error));
+ EXPECT_FALSE(forward_targets_are_valid("tcp:22x", "tcp:9000", &error));
+ EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:", &error));
+ EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:a", &error));
+ EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:22x", &error));
}
diff --git a/adb/adbd_auth.cpp b/adb/adbd_auth.cpp
new file mode 100644
index 0000000..b5f87be
--- /dev/null
+++ b/adb/adbd_auth.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG AUTH
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "fdevent.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <resolv.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+static fdevent listener_fde;
+static fdevent framework_fde;
+static int framework_fd = -1;
+
+static void usb_disconnected(void* unused, atransport* t);
+static struct adisconnect usb_disconnect = { usb_disconnected, nullptr};
+static atransport* usb_transport;
+static bool needs_retry = false;
+
+bool auth_required = true;
+
+bool adbd_auth_verify(const char* token, size_t token_size, const char* sig, int sig_len) {
+ static constexpr const char* key_paths[] = { "/adb_keys", "/data/misc/adb/adb_keys", nullptr };
+
+ for (const auto& path : key_paths) {
+ if (access(path, R_OK) == 0) {
+ LOG(INFO) << "Loading keys from " << path;
+
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ PLOG(ERROR) << "Couldn't read " << path;
+ continue;
+ }
+
+ for (const auto& line : android::base::Split(content, "\n")) {
+ // TODO: do we really have to support both ' ' and '\t'?
+ char* sep = strpbrk(const_cast<char*>(line.c_str()), " \t");
+ if (sep) *sep = '\0';
+
+ // b64_pton requires one additional byte in the target buffer for
+ // decoding to succeed. See http://b/28035006 for details.
+ uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+ if (__b64_pton(line.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
+ LOG(ERROR) << "Invalid base64 key " << line.c_str() << " in " << path;
+ continue;
+ }
+
+ RSA* key = nullptr;
+ if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
+ LOG(ERROR) << "Failed to parse key " << line.c_str() << " in " << path;
+ continue;
+ }
+
+ bool verified =
+ (RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
+ reinterpret_cast<const uint8_t*>(sig), sig_len, key) == 1);
+ RSA_free(key);
+ if (verified) return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool adbd_auth_generate_token(void* token, size_t token_size) {
+ FILE* fp = fopen("/dev/urandom", "re");
+ if (!fp) return false;
+ bool okay = (fread(token, token_size, 1, fp) == 1);
+ fclose(fp);
+ return okay;
+}
+
+static void usb_disconnected(void* unused, atransport* t) {
+ LOG(INFO) << "USB disconnect";
+ usb_transport = NULL;
+ needs_retry = false;
+}
+
+static void framework_disconnected() {
+ LOG(INFO) << "Framework disconnect";
+ fdevent_remove(&framework_fde);
+ framework_fd = -1;
+}
+
+static void adbd_auth_event(int fd, unsigned events, void*) {
+ if (events & FDE_READ) {
+ char response[2];
+ int ret = unix_read(fd, response, sizeof(response));
+ if (ret <= 0) {
+ framework_disconnected();
+ } else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
+ if (usb_transport) {
+ adbd_auth_verified(usb_transport);
+ }
+ }
+ }
+}
+
+void adbd_auth_confirm_key(const char* key, size_t len, atransport* t) {
+ if (!usb_transport) {
+ usb_transport = t;
+ t->AddDisconnect(&usb_disconnect);
+ }
+
+ if (framework_fd < 0) {
+ LOG(ERROR) << "Client not connected";
+ needs_retry = true;
+ return;
+ }
+
+ if (key[len - 1] != '\0') {
+ LOG(ERROR) << "Key must be a null-terminated string";
+ return;
+ }
+
+ char msg[MAX_PAYLOAD_V1];
+ int msg_len = snprintf(msg, sizeof(msg), "PK%s", key);
+ if (msg_len >= static_cast<int>(sizeof(msg))) {
+ LOG(ERROR) << "Key too long (" << msg_len << ")";
+ return;
+ }
+ LOG(DEBUG) << "Sending '" << msg << "'";
+
+ if (unix_write(framework_fd, msg, msg_len) == -1) {
+ PLOG(ERROR) << "Failed to write PK";
+ return;
+ }
+}
+
+static void adbd_auth_listener(int fd, unsigned events, void* data) {
+ int s = adb_socket_accept(fd, nullptr, nullptr);
+ if (s < 0) {
+ PLOG(ERROR) << "Failed to accept";
+ return;
+ }
+
+ if (framework_fd >= 0) {
+ LOG(WARNING) << "adb received framework auth socket connection again";
+ framework_disconnected();
+ }
+
+ framework_fd = s;
+ fdevent_install(&framework_fde, framework_fd, adbd_auth_event, nullptr);
+ fdevent_add(&framework_fde, FDE_READ);
+
+ if (needs_retry) {
+ needs_retry = false;
+ send_auth_request(usb_transport);
+ }
+}
+
+void adbd_cloexec_auth_socket() {
+ int fd = android_get_control_socket("adbd");
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to get adbd socket";
+ return;
+ }
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+void adbd_auth_init(void) {
+ int fd = android_get_control_socket("adbd");
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to get adbd socket";
+ return;
+ }
+
+ if (listen(fd, 4) == -1) {
+ PLOG(ERROR) << "Failed to listen on '" << fd << "'";
+ return;
+ }
+
+ fdevent_install(&listener_fde, fd, adbd_auth_listener, NULL);
+ fdevent_add(&listener_fde, FDE_READ);
+}
+
+void send_auth_request(atransport* t) {
+ LOG(INFO) << "Calling send_auth_request...";
+
+ if (!adbd_auth_generate_token(t->token, sizeof(t->token))) {
+ PLOG(ERROR) << "Error generating token";
+ return;
+ }
+
+ apacket* p = get_apacket();
+ memcpy(p->data, t->token, sizeof(t->token));
+ p->msg.command = A_AUTH;
+ p->msg.arg0 = ADB_AUTH_TOKEN;
+ p->msg.data_length = sizeof(t->token);
+ send_packet(p, t);
+}
+
+void adbd_auth_verified(atransport *t)
+{
+ handle_online(t);
+ send_connect(t);
+}
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
new file mode 100644
index 0000000..9b59d05
--- /dev/null
+++ b/adb/bugreport.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG ADB
+
+#include "bugreport.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+
+#include "sysdeps.h"
+#include "adb_utils.h"
+#include "file_sync_service.h"
+
+static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:";
+static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
+static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
+static constexpr char BUGZ_OK_PREFIX[] = "OK:";
+static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
+
+// Custom callback used to handle the output of zipped bugreports.
+class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
+ public:
+ BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file,
+ bool show_progress, Bugreport* br)
+ : br_(br),
+ src_file_(),
+ dest_dir_(dest_dir),
+ dest_file_(dest_file),
+ line_message_(),
+ invalid_lines_(),
+ show_progress_(show_progress),
+ status_(0),
+ line_() {
+ SetLineMessage("generating");
+ }
+
+ void OnStdout(const char* buffer, int length) {
+ for (int i = 0; i < length; i++) {
+ char c = buffer[i];
+ if (c == '\n') {
+ ProcessLine(line_);
+ line_.clear();
+ } else {
+ line_.append(1, c);
+ }
+ }
+ }
+
+ void OnStderr(const char* buffer, int length) {
+ OnStream(nullptr, stderr, buffer, length);
+ }
+ int Done(int unused_) {
+ // Process remaining line, if any.
+ ProcessLine(line_);
+
+ // Warn about invalid lines, if any.
+ if (!invalid_lines_.empty()) {
+ fprintf(stderr,
+ "WARNING: bugreportz generated %zu line(s) with unknown commands, "
+ "device might not support zipped bugreports:\n",
+ invalid_lines_.size());
+ for (const auto& line : invalid_lines_) {
+ fprintf(stderr, "\t%s\n", line.c_str());
+ }
+ fprintf(stderr,
+ "If the zipped bugreport was not generated, try 'adb bugreport' instead.\n");
+ }
+
+ // Pull the generated bug report.
+ if (status_ == 0) {
+ if (src_file_.empty()) {
+ fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX,
+ BUGZ_FAIL_PREFIX);
+ return -1;
+ }
+ std::string destination;
+ if (dest_dir_.empty()) {
+ destination = dest_file_;
+ } else {
+ destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(),
+ OS_PATH_SEPARATOR, dest_file_.c_str());
+ }
+ std::vector<const char*> srcs{src_file_.c_str()};
+ SetLineMessage("pulling");
+ status_ =
+ br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1;
+ if (status_ != 0) {
+ fprintf(stderr,
+ "Bug report finished but could not be copied to '%s'.\n"
+ "Try to run 'adb pull %s <directory>'\n"
+ "to copy it to a directory that can be written.\n",
+ destination.c_str(), src_file_.c_str());
+ }
+ }
+ return status_;
+ }
+
+ private:
+ void SetLineMessage(const std::string& action) {
+ line_message_ = action + " " + adb_basename(dest_file_);
+ }
+
+ void SetSrcFile(const std::string path) {
+ src_file_ = path;
+ if (!dest_dir_.empty()) {
+ // Only uses device-provided name when user passed a directory.
+ dest_file_ = adb_basename(path);
+ SetLineMessage("generating");
+ }
+ }
+
+ void ProcessLine(const std::string& line) {
+ if (line.empty()) return;
+
+ if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) {
+ SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]);
+ } else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
+ SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
+ } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
+ const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
+ fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
+ status_ = -1;
+ } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
+ // progress_line should have the following format:
+ //
+ // BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL
+ //
+ size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
+ size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
+ int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
+ int total = std::stoi(line.substr(idx2 + 1));
+ br_->UpdateProgress(line_message_, progress, total);
+ } else {
+ invalid_lines_.push_back(line);
+ }
+ }
+
+ Bugreport* br_;
+
+ // Path of bugreport on device.
+ std::string src_file_;
+
+ // Bugreport destination on host, depending on argument passed on constructor:
+ // - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name
+ // of the bugreport reported by the device.
+ // - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the
+ // name of the bugreport reported by the device.
+ // - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor.
+ std::string dest_dir_, dest_file_;
+
+ // Message displayed on LinePrinter, it's updated every time the destination above change.
+ std::string line_message_;
+
+ // Lines sent by bugreportz that contain invalid commands; will be displayed at the end.
+ std::vector<std::string> invalid_lines_;
+
+ // Whether PROGRESS_LINES should be interpreted as progress.
+ bool show_progress_;
+
+ // Overall process of the operation, as returned by Done().
+ int status_;
+
+ // Temporary buffer containing the characters read since the last newline (\n).
+ std::string line_;
+
+ DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
+};
+
+// Implemented in commandline.cpp
+int usage();
+
+int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
+ if (argc > 2) return usage();
+
+ // Gets bugreportz version.
+ std::string bugz_stdout, bugz_stderr;
+ DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
+ int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
+ std::string bugz_version = android::base::Trim(bugz_stderr);
+ std::string bugz_output = android::base::Trim(bugz_stdout);
+
+ if (status != 0 || bugz_version.empty()) {
+ D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status,
+ bugz_output.c_str(), bugz_version.c_str());
+ if (argc == 1) {
+ // Device does not support bugreportz: if called as 'adb bugreport', just falls out to
+ // the flat-file version.
+ fprintf(stderr,
+ "Failed to get bugreportz version, which is only available on devices "
+ "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
+ return SendShellCommand(transport_type, serial, "bugreport", false);
+ }
+
+ // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
+ // 'bugreport' would generate a lot of output the user might not be prepared to handle).
+ fprintf(stderr,
+ "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n"
+ "If the device does not run Android 7.0 or above, try 'adb bugreport' instead.\n",
+ bugz_output.c_str(), status);
+ return status != 0 ? status : -1;
+ }
+
+ std::string dest_file, dest_dir;
+
+ if (argc == 1) {
+ // No args - use current directory
+ if (!getcwd(&dest_dir)) {
+ perror("adb: getcwd failed");
+ return 1;
+ }
+ } else {
+ // Check whether argument is a directory or file
+ if (directory_exists(argv[1])) {
+ dest_dir = argv[1];
+ } else {
+ dest_file = argv[1];
+ }
+ }
+
+ if (dest_file.empty()) {
+ // Uses a default value until device provides the proper name
+ dest_file = "bugreport.zip";
+ } else {
+ if (!android::base::EndsWithIgnoreCase(dest_file, ".zip")) {
+ dest_file += ".zip";
+ }
+ }
+
+ bool show_progress = true;
+ std::string bugz_command = "bugreportz -p";
+ if (bugz_version == "1.0") {
+ // 1.0 does not support progress notifications, so print a disclaimer
+ // message instead.
+ fprintf(stderr,
+ "Bugreport is in progress and it could take minutes to complete.\n"
+ "Please be patient and do not cancel or disconnect your device "
+ "until it completes.\n");
+ show_progress = false;
+ bugz_command = "bugreportz";
+ }
+ BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
+ return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
+}
+
+void Bugreport::UpdateProgress(const std::string& message, int progress, int total) {
+ int progress_percentage = (progress * 100 / total);
+ line_printer_.Print(
+ android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
+ LinePrinter::INFO);
+}
+
+int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
+ const std::string& command, bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback) {
+ return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
+}
+
+bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+ const char* name) {
+ return do_sync_pull(srcs, dst, copy_attrs, name);
+}
diff --git a/adb/bugreport.h b/adb/bugreport.h
new file mode 100644
index 0000000..ee99cbc
--- /dev/null
+++ b/adb/bugreport.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BUGREPORT_H
+#define BUGREPORT_H
+
+#include <vector>
+
+#include "adb.h"
+#include "commandline.h"
+#include "line_printer.h"
+
+class Bugreport {
+ friend class BugreportStandardStreamsCallback;
+
+ public:
+ Bugreport() : line_printer_() {
+ }
+ int DoIt(TransportType transport_type, const char* serial, int argc, const char** argv);
+
+ protected:
+ // Functions below are abstractions of external functions so they can be
+ // mocked on tests.
+ virtual int SendShellCommand(
+ TransportType transport_type, const char* serial, const std::string& command,
+ bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+
+ virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+ const char* name);
+
+ private:
+ virtual void UpdateProgress(const std::string& file_name, int progress, int total);
+ LinePrinter line_printer_;
+ DISALLOW_COPY_AND_ASSIGN(Bugreport);
+};
+
+#endif // BUGREPORT_H
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
new file mode 100644
index 0000000..1129285
--- /dev/null
+++ b/adb/bugreport_test.cpp
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bugreport.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+
+#include "sysdeps.h"
+#include "adb_utils.h"
+
+using ::testing::_;
+using ::testing::Action;
+using ::testing::ActionInterface;
+using ::testing::DoAll;
+using ::testing::ElementsAre;
+using ::testing::HasSubstr;
+using ::testing::MakeAction;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+// Empty function so tests don't need to be linked against file_sync_service.cpp, which requires
+// SELinux and its transitive dependencies...
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+ const char* name) {
+ ADD_FAILURE() << "do_sync_pull() should have been mocked";
+ return false;
+}
+
+// Empty functions so tests don't need to be linked against commandline.cpp
+DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
+int usage() {
+ return -42;
+}
+int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
+ bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+ ADD_FAILURE() << "send_shell_command() should have been mocked";
+ return -42;
+}
+
+enum StreamType {
+ kStreamStdout,
+ kStreamStderr,
+};
+
+// gmock black magic to provide a WithArg<4>(WriteOnStdout(output)) matcher
+typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*);
+
+class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> {
+ public:
+ explicit OnStandardStreamsCallbackAction(StreamType type, const std::string& output)
+ : type_(type), output_(output) {
+ }
+ virtual Result Perform(const ArgumentTuple& args) {
+ if (type_ == kStreamStdout) {
+ ::std::tr1::get<0>(args)->OnStdout(output_.c_str(), output_.size());
+ }
+ if (type_ == kStreamStderr) {
+ ::std::tr1::get<0>(args)->OnStderr(output_.c_str(), output_.size());
+ }
+ }
+
+ private:
+ StreamType type_;
+ std::string output_;
+};
+
+// Matcher used to emulated StandardStreamsCallbackInterface.OnStdout(buffer,
+// length)
+Action<OnStandardStreamsCallbackFunction> WriteOnStdout(const std::string& output) {
+ return MakeAction(new OnStandardStreamsCallbackAction(kStreamStdout, output));
+}
+
+// Matcher used to emulated StandardStreamsCallbackInterface.OnStderr(buffer,
+// length)
+Action<OnStandardStreamsCallbackFunction> WriteOnStderr(const std::string& output) {
+ return MakeAction(new OnStandardStreamsCallbackAction(kStreamStderr, output));
+}
+
+typedef int CallbackDoneFunction(StandardStreamsCallbackInterface*);
+
+class CallbackDoneAction : public ActionInterface<CallbackDoneFunction> {
+ public:
+ explicit CallbackDoneAction(int status) : status_(status) {
+ }
+ virtual Result Perform(const ArgumentTuple& args) {
+ int status = ::std::tr1::get<0>(args)->Done(status_);
+ return status;
+ }
+
+ private:
+ int status_;
+};
+
+// Matcher used to emulated StandardStreamsCallbackInterface.Done(status)
+Action<CallbackDoneFunction> ReturnCallbackDone(int status = -1337) {
+ return MakeAction(new CallbackDoneAction(status));
+}
+
+class BugreportMock : public Bugreport {
+ public:
+ MOCK_METHOD5(SendShellCommand,
+ int(TransportType transport_type, const char* serial, const std::string& command,
+ bool disable_shell_protocol, StandardStreamsCallbackInterface* callback));
+ MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst,
+ bool copy_attrs, const char* name));
+ MOCK_METHOD3(UpdateProgress, void(const std::string&, int, int));
+};
+
+class BugreportTest : public ::testing::Test {
+ public:
+ void SetUp() {
+ if (!getcwd(&cwd_)) {
+ ADD_FAILURE() << "getcwd failed: " << strerror(errno);
+ return;
+ }
+ }
+
+ void ExpectBugreportzVersion(const std::string& version) {
+ EXPECT_CALL(br_,
+ SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())),
+ WithArg<4>(ReturnCallbackDone(0))));
+ }
+
+ void ExpectProgress(int progress, int total, const std::string& file = "file.zip") {
+ EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress, total));
+ }
+
+ BugreportMock br_;
+ std::string cwd_; // TODO: make it static
+};
+
+// Tests when called with invalid number of arguments
+TEST_F(BugreportTest, InvalidNumberArgs) {
+ const char* args[] = {"bugreport", "to", "principal"};
+ ASSERT_EQ(-42, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
+}
+
+// Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
+// to the flat-file format ('bugreport' binary on device)
+TEST_F(BugreportTest, NoArgumentsPreNDevice) {
+ // clang-format off
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
+ // Write some bogus output on stdout to make sure it's ignored
+ WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
+ WithArg<4>(ReturnCallbackDone(0))));
+ // clang-format on
+ std::string bugreport = "Reported the bug was.";
+ CaptureStdout();
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
+
+ const char* args[] = {"bugreport"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+ ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
+}
+
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.0 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsNDevice) {
+ ExpectBugreportzVersion("1.0");
+
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("pulling da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+}
+
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsPostNDevice) {
+ ExpectBugreportzVersion("1.1");
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+ ExpectProgress(50, 100, "da_bugreport.zip");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
+ WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("pulling da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
+TEST_F(BugreportTest, OkNDevice) {
+ ExpectBugreportzVersion("1.0");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+ true, StrEq("pulling file.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds but response was sent in
+// multiple buffer writers and without progress updates.
+TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
+ ExpectBugreportzVersion("1.0");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
+ WithArg<4>(WriteOnStdout("/bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+ true, StrEq("pulling file.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
+TEST_F(BugreportTest, OkProgress) {
+ ExpectBugreportzVersion("1.1");
+ ExpectProgress(1, 100);
+ ExpectProgress(10, 100);
+ ExpectProgress(50, 100);
+ ExpectProgress(99, 100);
+ // clang-format off
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
+ .WillOnce(DoAll(
+ // Name might change on OK, so make sure the right one is picked.
+ WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
+ // Progress line in one write
+ WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
+ // Add some bogus lines
+ WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
+ // Multiple progress lines in one write
+ WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
+ // Progress line in multiple writes
+ WithArg<4>(WriteOnStdout("PROG")),
+ WithArg<4>(WriteOnStdout("RESS:99")),
+ WithArg<4>(WriteOnStdout("/100\n")),
+ // Split last message as well, just in case
+ WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
+ WithArg<4>(WriteOnStdout(".zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ // clang-format on
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+ true, StrEq("pulling file.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory.
+TEST_F(BugreportTest, OkDirectory) {
+ ExpectBugreportzVersion("1.1");
+ TemporaryDir td;
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("pulling da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", td.path};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file' when it succeeds
+TEST_F(BugreportTest, OkNoExtension) {
+ ExpectBugreportzVersion("1.1");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+ true, StrEq("pulling file.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", "file"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
+TEST_F(BugreportTest, OkNDeviceDirectory) {
+ ExpectBugreportzVersion("1.0");
+ TemporaryDir td;
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("pulling da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", td.path};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreport itself failed
+TEST_F(BugreportTest, BugreportzReturnedFail) {
+ ExpectBugreportzVersion("1.1");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(
+ DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
+
+ CaptureStderr();
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreport itself failed but response
+// was sent in
+// multiple buffer writes
+TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
+ ExpectBugreportzVersion("1.1");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
+ WithArg<4>(ReturnCallbackDone())));
+
+ CaptureStderr();
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreportz returned an unsupported
+// response.
+TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
+ ExpectBugreportzVersion("1.1");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
+ WithArg<4>(ReturnCallbackDone())));
+
+ CaptureStderr();
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreportz -v command failed
+TEST_F(BugreportTest, BugreportzVersionFailed) {
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
+ .WillOnce(Return(666));
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
+TEST_F(BugreportTest, BugreportzVersionEmpty) {
+ ExpectBugreportzVersion("");
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when the main bugreportz command failed
+TEST_F(BugreportTest, BugreportzFailed) {
+ ExpectBugreportzVersion("1.1");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(Return(666));
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreport could not be pulled
+TEST_F(BugreportTest, PullFails) {
+ ExpectBugreportzVersion("1.1");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+ true, HasSubstr("file.zip")))
+ .WillOnce(Return(false));
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 206442a..97a54fd 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -14,109 +14,60 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
#include "sysdeps.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
-// We only build the affinity WAR code for Linux.
-#if defined(__linux__)
-#include <sched.h>
-#endif
+#include <thread>
-#include "base/file.h"
-#include "base/logging.h"
-#include "base/stringprintf.h"
+#include <android-base/errors.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/quick_exit.h>
+#include <android-base/stringprintf.h>
#include "adb.h"
#include "adb_auth.h"
#include "adb_listeners.h"
+#include "adb_utils.h"
+#include "commandline.h"
+#include "sysdeps/chrono.h"
#include "transport.h"
-#if defined(WORKAROUND_BUG6558362) && defined(__linux__)
-static const bool kWorkaroundBug6558362 = true;
-#else
-static const bool kWorkaroundBug6558362 = false;
-#endif
-
-static void adb_workaround_affinity(void) {
-#if defined(__linux__)
- const char affinity_env[] = "ADB_CPU_AFFINITY_BUG6558362";
- const char* cpunum_str = getenv(affinity_env);
- if (cpunum_str == nullptr || *cpunum_str == '\0') {
- return;
- }
-
- char* strtol_res;
- int cpu_num = strtol(cpunum_str, &strtol_res, 0);
- if (*strtol_res != '\0') {
- fatal("bad number (%s) in env var %s. Expecting 0..n.\n", cpunum_str,
- affinity_env);
- }
-
- cpu_set_t cpu_set;
- sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
- D("orig cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
-
- CPU_ZERO(&cpu_set);
- CPU_SET(cpu_num, &cpu_set);
- sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
-
- sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
- D("new cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
-#else
- // No workaround was ever implemented for the other platforms.
-#endif
-}
-
-#if defined(_WIN32)
-static const char kNullFileName[] = "NUL";
-
-static BOOL WINAPI ctrlc_handler(DWORD type) {
- exit(STATUS_CONTROL_C_EXIT);
- return TRUE;
-}
-
static std::string GetLogFilePath() {
+#if defined(_WIN32)
const char log_name[] = "adb.log";
WCHAR temp_path[MAX_PATH];
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
- if ((nchars >= arraysize(temp_path)) || (nchars == 0)) {
+ if (nchars >= arraysize(temp_path) || nchars == 0) {
// If string truncation or some other error.
fatal("cannot retrieve temporary file path: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
}
- return narrow(temp_path) + log_name;
-}
+ std::string temp_path_utf8;
+ if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
+ fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+ }
+
+ return temp_path_utf8 + log_name;
#else
-static const char kNullFileName[] = "/dev/null";
-
-static std::string GetLogFilePath() {
- return std::string("/tmp/adb.log");
-}
+ const char* tmp_dir = getenv("TMPDIR");
+ if (tmp_dir == nullptr) tmp_dir = "/tmp";
+ return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
#endif
-
-static void close_stdin() {
- int fd = unix_open(kNullFileName, O_RDONLY);
- if (fd == -1) {
- fatal("cannot open '%s': %s", kNullFileName, strerror(errno));
- }
- if (dup2(fd, STDIN_FILENO) == -1) {
- fatal("cannot redirect stdin: %s", strerror(errno));
- }
- unix_close(fd);
}
static void setup_daemon_logging(void) {
const std::string log_file_path(GetLogFilePath());
- int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND,
- 0640);
+ int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
if (fd == -1) {
fatal("cannot open '%s': %s", log_file_path.c_str(), strerror(errno));
}
@@ -128,39 +79,86 @@
}
unix_close(fd);
-#ifdef _WIN32
- // On Windows, stderr is buffered by default, so switch to non-buffered
- // to match Linux.
- setvbuf(stderr, NULL, _IONBF, 0);
-#endif
fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+ LOG(INFO) << adb_version();
}
-int adb_main(int is_daemon, int server_port, int ack_reply_fd) {
#if defined(_WIN32)
+static BOOL WINAPI ctrlc_handler(DWORD type) {
+ // TODO: Consider trying to kill a starting up adb server (if we're in
+ // launch_server) by calling GenerateConsoleCtrlEvent().
+ android::base::quick_exit(STATUS_CONTROL_C_EXIT);
+ return TRUE;
+}
+#endif
+
+int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd) {
+#if defined(_WIN32)
+ // adb start-server starts us up with stdout and stderr hooked up to
+ // anonymous pipes. When the C Runtime sees this, it makes stderr and
+ // stdout buffered, but to improve the chance that error output is seen,
+ // unbuffer stdout and stderr just like if we were run at the console.
+ // This also keeps stderr unbuffered when it is redirected to adb.log.
+ if (is_daemon) {
+ if (setvbuf(stdout, NULL, _IONBF, 0) == -1) {
+ fatal("cannot make stdout unbuffered: %s", strerror(errno));
+ }
+ if (setvbuf(stderr, NULL, _IONBF, 0) == -1) {
+ fatal("cannot make stderr unbuffered: %s", strerror(errno));
+ }
+ }
+
SetConsoleCtrlHandler(ctrlc_handler, TRUE);
#else
- signal(SIGPIPE, SIG_IGN);
+ signal(SIGINT, [](int) {
+ android::base::quick_exit(0);
+ });
#endif
init_transport_registration();
- if (kWorkaroundBug6558362 && is_daemon) {
- adb_workaround_affinity();
- }
-
usb_init();
local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
- adb_auth_init();
std::string error;
- std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
- if (install_listener(local_name, "*smartsocket*", nullptr, 0, &error)) {
- fatal("could not install *smartsocket* listener: %s", error.c_str());
+
+ auto start = std::chrono::steady_clock::now();
+
+ // If we told a previous adb server to quit because of version mismatch, we can get to this
+ // point before it's finished exiting. Retry for a while to give it some time.
+ while (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error) !=
+ INSTALL_STATUS_OK) {
+ if (std::chrono::steady_clock::now() - start > 0.5s) {
+ fatal("could not install *smartsocket* listener: %s", error.c_str());
+ }
+
+ std::this_thread::sleep_for(100ms);
}
- // Inform our parent that we are up and running.
if (is_daemon) {
+ close_stdin();
+ setup_daemon_logging();
+ }
+
+ adb_auth_init();
+
+ if (is_daemon) {
+#if !defined(_WIN32)
+ // Start a new session for the daemon. Do this here instead of after the fork so
+ // that a ctrl-c between the "starting server" and "done starting server" messages
+ // gets a chance to terminate the server.
+ // setsid will fail with EPERM if it's already been a lead process of new session.
+ // Ignore such error.
+ if (setsid() == -1 && errno != EPERM) {
+ fatal("setsid() failed: %s", strerror(errno));
+ }
+#endif
+
+ // Inform our parent that we are up and running.
+
+ // Any error output written to stderr now goes to adb.log. We could
+ // keep around a copy of the stderr fd and use that to write any errors
+ // encountered by the following code, but that is probably overkill.
#if defined(_WIN32)
const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
const CHAR ack[] = "OK\n";
@@ -168,7 +166,7 @@
DWORD written = 0;
if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
- SystemErrorCodeToString(GetLastError()).c_str());
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
}
if (written != bytes_to_write) {
fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
@@ -183,44 +181,15 @@
}
unix_close(ack_reply_fd);
#endif
- close_stdin();
- setup_daemon_logging();
}
- D("Event loop starting\n");
+ D("Event loop starting");
fdevent_loop();
return 0;
}
-#ifdef _WIN32
-static bool _argv_is_utf8 = false;
-#endif
-
int main(int argc, char** argv) {
-#ifdef _WIN32
- if (!_argv_is_utf8) {
- fatal("_argv_is_utf8 is not set, suggesting that wmain was not "
- "called. Did you forget to link with -municode?");
- }
-#endif
-
- adb_sysdeps_init();
adb_trace_init(argv);
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}
-
-#ifdef _WIN32
-
-extern "C"
-int wmain(int argc, wchar_t **argv) {
- // Set diagnostic flag to try to detect if the build system was not
- // configured to call wmain.
- _argv_is_utf8 = true;
-
- // Convert args from UTF-16 to UTF-8 and pass that to main().
- NarrowArgs narrow_args(argc, argv);
- return main(argc, narrow_args.data());
-}
-
-#endif
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
new file mode 100644
index 0000000..f02dccf
--- /dev/null
+++ b/adb/client/usb_dispatch.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include "usb.h"
+
+static bool should_use_libusb() {
+ static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
+ return enable;
+}
+
+void usb_init() {
+ if (should_use_libusb()) {
+ LOG(DEBUG) << "using libusb backend";
+ libusb::usb_init();
+ } else {
+ LOG(DEBUG) << "using native backend";
+ native::usb_init();
+ }
+}
+
+int usb_write(usb_handle* h, const void* data, int len) {
+ return should_use_libusb()
+ ? libusb::usb_write(reinterpret_cast<libusb::usb_handle*>(h), data, len)
+ : native::usb_write(reinterpret_cast<native::usb_handle*>(h), data, len);
+}
+
+int usb_read(usb_handle* h, void* data, int len) {
+ return should_use_libusb()
+ ? libusb::usb_read(reinterpret_cast<libusb::usb_handle*>(h), data, len)
+ : native::usb_read(reinterpret_cast<native::usb_handle*>(h), data, len);
+}
+
+int usb_close(usb_handle* h) {
+ return should_use_libusb() ? libusb::usb_close(reinterpret_cast<libusb::usb_handle*>(h))
+ : native::usb_close(reinterpret_cast<native::usb_handle*>(h));
+}
+
+void usb_kick(usb_handle* h) {
+ should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
+ : native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
+}
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
new file mode 100644
index 0000000..7adb262
--- /dev/null
+++ b/adb/client/usb_libusb.cpp
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "usb.h"
+
+#include "sysdeps.h"
+
+#include <stdint.h>
+
+#include <atomic>
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+
+#include <libusb/libusb.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/quick_exit.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "adb.h"
+#include "transport.h"
+#include "usb.h"
+
+using namespace std::literals;
+
+using android::base::StringPrintf;
+
+// RAII wrappers for libusb.
+struct ConfigDescriptorDeleter {
+ void operator()(libusb_config_descriptor* desc) {
+ libusb_free_config_descriptor(desc);
+ }
+};
+
+using unique_config_descriptor = std::unique_ptr<libusb_config_descriptor, ConfigDescriptorDeleter>;
+
+struct DeviceHandleDeleter {
+ void operator()(libusb_device_handle* h) {
+ libusb_close(h);
+ }
+};
+
+using unique_device_handle = std::unique_ptr<libusb_device_handle, DeviceHandleDeleter>;
+
+struct transfer_info {
+ transfer_info(const char* name, uint16_t zero_mask) :
+ name(name),
+ transfer(libusb_alloc_transfer(0)),
+ zero_mask(zero_mask)
+ {
+ }
+
+ ~transfer_info() {
+ libusb_free_transfer(transfer);
+ }
+
+ const char* name;
+ libusb_transfer* transfer;
+ bool transfer_complete;
+ std::condition_variable cv;
+ std::mutex mutex;
+ uint16_t zero_mask;
+
+ void Notify() {
+ LOG(DEBUG) << "notifying " << name << " transfer complete";
+ transfer_complete = true;
+ cv.notify_one();
+ }
+};
+
+namespace libusb {
+struct usb_handle : public ::usb_handle {
+ usb_handle(const std::string& device_address, const std::string& serial,
+ unique_device_handle&& device_handle, uint8_t interface, uint8_t bulk_in,
+ uint8_t bulk_out, size_t zero_mask)
+ : device_address(device_address),
+ serial(serial),
+ closing(false),
+ device_handle(device_handle.release()),
+ read("read", zero_mask),
+ write("write", zero_mask),
+ interface(interface),
+ bulk_in(bulk_in),
+ bulk_out(bulk_out) {
+ }
+
+ ~usb_handle() {
+ Close();
+ }
+
+ void Close() {
+ std::unique_lock<std::mutex> lock(device_handle_mutex);
+ // Cancelling transfers will trigger more Closes, so make sure this only happens once.
+ if (closing) {
+ return;
+ }
+ closing = true;
+
+ // Make sure that no new transfers come in.
+ libusb_device_handle* handle = device_handle;
+ if (!handle) {
+ return;
+ }
+
+ device_handle = nullptr;
+
+ // Cancel already dispatched transfers.
+ libusb_cancel_transfer(read.transfer);
+ libusb_cancel_transfer(write.transfer);
+
+ libusb_release_interface(handle, interface);
+ libusb_close(handle);
+ }
+
+ std::string device_address;
+ std::string serial;
+
+ std::atomic<bool> closing;
+ std::mutex device_handle_mutex;
+ libusb_device_handle* device_handle;
+
+ transfer_info read;
+ transfer_info write;
+
+ uint8_t interface;
+ uint8_t bulk_in;
+ uint8_t bulk_out;
+};
+
+static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>();
+static auto& usb_handles_mutex = *new std::mutex();
+
+static std::thread* device_poll_thread = nullptr;
+static std::atomic<bool> terminate_device_poll_thread(false);
+
+static std::string get_device_address(libusb_device* device) {
+ return StringPrintf("usb:%d:%d", libusb_get_bus_number(device),
+ libusb_get_device_address(device));
+}
+
+static bool endpoint_is_output(uint8_t endpoint) {
+ return (endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT;
+}
+
+static bool should_perform_zero_transfer(uint8_t endpoint, size_t write_length, uint16_t zero_mask) {
+ return endpoint_is_output(endpoint) && write_length != 0 && zero_mask != 0 &&
+ (write_length & zero_mask) == 0;
+}
+
+static void poll_for_devices() {
+ libusb_device** list;
+ adb_thread_setname("device poll");
+ while (!terminate_device_poll_thread) {
+ const ssize_t device_count = libusb_get_device_list(nullptr, &list);
+
+ LOG(VERBOSE) << "found " << device_count << " attached devices";
+
+ for (ssize_t i = 0; i < device_count; ++i) {
+ libusb_device* device = list[i];
+ std::string device_address = get_device_address(device);
+ std::string device_serial;
+
+ // Figure out if we want to open the device.
+ libusb_device_descriptor device_desc;
+ int rc = libusb_get_device_descriptor(device, &device_desc);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to get device descriptor for device at " << device_address
+ << ": " << libusb_error_name(rc);
+ }
+
+ if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
+ // Assume that all Android devices have the device class set to per interface.
+ // TODO: Is this assumption valid?
+ LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
+ continue;
+ }
+
+ libusb_config_descriptor* config_raw;
+ rc = libusb_get_active_config_descriptor(device, &config_raw);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to get active config descriptor for device at "
+ << device_address << ": " << libusb_error_name(rc);
+ continue;
+ }
+ const unique_config_descriptor config(config_raw);
+
+ // Use size_t for interface_num so <iostream>s don't mangle it.
+ size_t interface_num;
+ uint16_t zero_mask;
+ uint8_t bulk_in = 0, bulk_out = 0;
+ bool found_adb = false;
+
+ for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
+ const libusb_interface& interface = config->interface[interface_num];
+ if (interface.num_altsetting != 1) {
+ // Assume that interfaces with alternate settings aren't adb interfaces.
+ // TODO: Is this assumption valid?
+ LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at "
+ << device_address << " (interface " << interface_num << ")";
+ continue;
+ }
+
+ const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
+ if (!is_adb_interface(interface_desc.bInterfaceClass,
+ interface_desc.bInterfaceSubClass,
+ interface_desc.bInterfaceProtocol)) {
+ LOG(VERBOSE) << "skipping non-adb interface at " << device_address
+ << " (interface " << interface_num << ")";
+ continue;
+ }
+
+ LOG(VERBOSE) << "found potential adb interface at " << device_address
+ << " (interface " << interface_num << ")";
+
+ bool found_in = false;
+ bool found_out = false;
+ for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints;
+ ++endpoint_num) {
+ const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
+ const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
+ const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
+
+ const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
+
+ if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
+ continue;
+ }
+
+ if (endpoint_is_output(endpoint_addr) && !found_out) {
+ found_out = true;
+ bulk_out = endpoint_addr;
+ zero_mask = endpoint_desc.wMaxPacketSize - 1;
+ } else if (!endpoint_is_output(endpoint_addr) && !found_in) {
+ found_in = true;
+ bulk_in = endpoint_addr;
+ }
+ }
+
+ if (found_in && found_out) {
+ found_adb = true;
+ break;
+ } else {
+ LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
+ << "(interface " << interface_num << "): missing bulk endpoints "
+ << "(found_in = " << found_in << ", found_out = " << found_out
+ << ")";
+ }
+ }
+
+ if (!found_adb) {
+ LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
+ continue;
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ if (usb_handles.find(device_address) != usb_handles.end()) {
+ LOG(VERBOSE) << "device at " << device_address
+ << " has already been registered, skipping";
+ continue;
+ }
+ }
+
+ libusb_device_handle* handle_raw;
+ rc = libusb_open(list[i], &handle_raw);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to open usb device at " << device_address << ": "
+ << libusb_error_name(rc);
+ continue;
+ }
+
+ unique_device_handle handle(handle_raw);
+ LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
+ << StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
+
+ device_serial.resize(255);
+ rc = libusb_get_string_descriptor_ascii(
+ handle_raw, device_desc.iSerialNumber,
+ reinterpret_cast<unsigned char*>(&device_serial[0]), device_serial.length());
+ if (rc == 0) {
+ LOG(WARNING) << "received empty serial from device at " << device_address;
+ continue;
+ } else if (rc < 0) {
+ LOG(WARNING) << "failed to get serial from device at " << device_address
+ << libusb_error_name(rc);
+ continue;
+ }
+ device_serial.resize(rc);
+
+ // Try to reset the device.
+ rc = libusb_reset_device(handle_raw);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to reset opened device '" << device_serial
+ << "': " << libusb_error_name(rc);
+ continue;
+ }
+
+ // WARNING: this isn't released via RAII.
+ rc = libusb_claim_interface(handle.get(), interface_num);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
+ << libusb_error_name(rc);
+ continue;
+ }
+
+ for (uint8_t endpoint : {bulk_in, bulk_out}) {
+ rc = libusb_clear_halt(handle.get(), endpoint);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to clear halt on device '" << device_serial
+ << "' endpoint 0x" << std::hex << endpoint << ": "
+ << libusb_error_name(rc);
+ libusb_release_interface(handle.get(), interface_num);
+ continue;
+ }
+ }
+
+ auto result =
+ std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
+ interface_num, bulk_in, bulk_out, zero_mask);
+ usb_handle* usb_handle_raw = result.get();
+
+ {
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ usb_handles[device_address] = std::move(result);
+ }
+
+ register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), 1);
+
+ LOG(INFO) << "registered new usb device '" << device_serial << "'";
+ }
+ libusb_free_device_list(list, 1);
+
+ std::this_thread::sleep_for(500ms);
+ }
+}
+
+void usb_init() {
+ LOG(DEBUG) << "initializing libusb...";
+ int rc = libusb_init(nullptr);
+ if (rc != 0) {
+ LOG(FATAL) << "failed to initialize libusb: " << libusb_error_name(rc);
+ }
+
+ // Spawn a thread for libusb_handle_events.
+ std::thread([]() {
+ adb_thread_setname("libusb");
+ while (true) {
+ libusb_handle_events(nullptr);
+ }
+ }).detach();
+
+ // Spawn a thread to do device enumeration.
+ // TODO: Use libusb_hotplug_* instead?
+ device_poll_thread = new std::thread(poll_for_devices);
+ android::base::at_quick_exit([]() {
+ terminate_device_poll_thread = true;
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ for (auto& it : usb_handles) {
+ it.second->Close();
+ }
+ lock.unlock();
+ device_poll_thread->join();
+ });
+}
+
+// Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.
+static int perform_usb_transfer(usb_handle* h, transfer_info* info,
+ std::unique_lock<std::mutex> device_lock) {
+ libusb_transfer* transfer = info->transfer;
+
+ transfer->user_data = info;
+ transfer->callback = [](libusb_transfer* transfer) {
+ transfer_info* info = static_cast<transfer_info*>(transfer->user_data);
+
+ LOG(DEBUG) << info->name << " transfer callback entered";
+
+ // Make sure that the original submitter has made it to the condition_variable wait.
+ std::unique_lock<std::mutex> lock(info->mutex);
+
+ LOG(DEBUG) << info->name << " callback successfully acquired lock";
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ LOG(WARNING) << info->name
+ << " transfer failed: " << libusb_error_name(transfer->status);
+ info->Notify();
+ return;
+ }
+
+ if (transfer->actual_length != transfer->length) {
+ LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
+ transfer->length -= transfer->actual_length;
+ transfer->buffer += transfer->actual_length;
+ int rc = libusb_submit_transfer(transfer);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to submit " << info->name
+ << " transfer: " << libusb_error_name(rc);
+ transfer->status = LIBUSB_TRANSFER_ERROR;
+ info->Notify();
+ }
+ return;
+ }
+
+ if (should_perform_zero_transfer(transfer->endpoint, transfer->length, info->zero_mask)) {
+ LOG(DEBUG) << "submitting zero-length write";
+ transfer->length = 0;
+ int rc = libusb_submit_transfer(transfer);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to submit zero-length write: " << libusb_error_name(rc);
+ transfer->status = LIBUSB_TRANSFER_ERROR;
+ info->Notify();
+ }
+ return;
+ }
+
+ LOG(VERBOSE) << info->name << "transfer fully complete";
+ info->Notify();
+ };
+
+ LOG(DEBUG) << "locking " << info->name << " transfer_info mutex";
+ std::unique_lock<std::mutex> lock(info->mutex);
+ info->transfer_complete = false;
+ LOG(DEBUG) << "submitting " << info->name << " transfer";
+ int rc = libusb_submit_transfer(transfer);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to submit " << info->name << " transfer: " << libusb_error_name(rc);
+ errno = EIO;
+ return -1;
+ }
+
+ LOG(DEBUG) << info->name << " transfer successfully submitted";
+ device_lock.unlock();
+ info->cv.wait(lock, [info]() { return info->transfer_complete; });
+ if (transfer->status != 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int usb_write(usb_handle* h, const void* d, int len) {
+ LOG(DEBUG) << "usb_write of length " << len;
+
+ std::unique_lock<std::mutex> lock(h->device_handle_mutex);
+ if (!h->device_handle) {
+ errno = EIO;
+ return -1;
+ }
+
+ transfer_info* info = &h->write;
+ info->transfer->dev_handle = h->device_handle;
+ info->transfer->flags = 0;
+ info->transfer->endpoint = h->bulk_out;
+ info->transfer->type = LIBUSB_TRANSFER_TYPE_BULK;
+ info->transfer->length = len;
+ info->transfer->buffer = reinterpret_cast<unsigned char*>(const_cast<void*>(d));
+ info->transfer->num_iso_packets = 0;
+
+ int rc = perform_usb_transfer(h, info, std::move(lock));
+ LOG(DEBUG) << "usb_write(" << len << ") = " << rc;
+ return rc;
+}
+
+int usb_read(usb_handle* h, void* d, int len) {
+ LOG(DEBUG) << "usb_read of length " << len;
+
+ std::unique_lock<std::mutex> lock(h->device_handle_mutex);
+ if (!h->device_handle) {
+ errno = EIO;
+ return -1;
+ }
+
+ transfer_info* info = &h->read;
+ info->transfer->dev_handle = h->device_handle;
+ info->transfer->flags = 0;
+ info->transfer->endpoint = h->bulk_in;
+ info->transfer->type = LIBUSB_TRANSFER_TYPE_BULK;
+ info->transfer->length = len;
+ info->transfer->buffer = reinterpret_cast<unsigned char*>(d);
+ info->transfer->num_iso_packets = 0;
+
+ int rc = perform_usb_transfer(h, info, std::move(lock));
+ LOG(DEBUG) << "usb_read(" << len << ") = " << rc;
+ return rc;
+}
+
+int usb_close(usb_handle* h) {
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ auto it = usb_handles.find(h->device_address);
+ if (it == usb_handles.end()) {
+ LOG(FATAL) << "attempted to close unregistered usb_handle for '" << h->serial << "'";
+ }
+ usb_handles.erase(h->device_address);
+ return 0;
+}
+
+void usb_kick(usb_handle* h) {
+ h->Close();
+}
+} // namespace libusb
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
new file mode 100644
index 0000000..13b7674
--- /dev/null
+++ b/adb/client/usb_linux.cpp
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG USB
+
+#include "sysdeps.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <list>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "adb.h"
+#include "transport.h"
+#include "usb.h"
+
+using namespace std::chrono_literals;
+using namespace std::literals;
+
+/* usb scan debugging is waaaay too verbose */
+#define DBGX(x...)
+
+namespace native {
+struct usb_handle : public ::usb_handle {
+ ~usb_handle() {
+ if (fd != -1) unix_close(fd);
+ }
+
+ std::string path;
+ int fd = -1;
+ unsigned char ep_in;
+ unsigned char ep_out;
+
+ unsigned zero_mask;
+ unsigned writeable = 1;
+
+ usbdevfs_urb urb_in;
+ usbdevfs_urb urb_out;
+
+ bool urb_in_busy = false;
+ bool urb_out_busy = false;
+ bool dead = false;
+
+ std::condition_variable cv;
+ std::mutex mutex;
+
+ // for garbage collecting disconnected devices
+ bool mark;
+
+ // ID of thread currently in REAPURB
+ pthread_t reaper_thread = 0;
+};
+
+static auto& g_usb_handles_mutex = *new std::mutex();
+static auto& g_usb_handles = *new std::list<usb_handle*>();
+
+static int is_known_device(const char* dev_name) {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ for (usb_handle* usb : g_usb_handles) {
+ if (usb->path == dev_name) {
+ // set mark flag to indicate this device is still alive
+ usb->mark = true;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void kick_disconnected_devices() {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ // kick any devices in the device list that were not found in the device scan
+ for (usb_handle* usb : g_usb_handles) {
+ if (!usb->mark) {
+ usb_kick(usb);
+ } else {
+ usb->mark = false;
+ }
+ }
+}
+
+static inline bool contains_non_digit(const char* name) {
+ while (*name) {
+ if (!isdigit(*name++)) return true;
+ }
+ return false;
+}
+
+static void find_usb_device(const std::string& base,
+ void (*register_device_callback)
+ (const char*, const char*, unsigned char, unsigned char, int, int, unsigned))
+{
+ std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
+ if (!bus_dir) return;
+
+ dirent* de;
+ while ((de = readdir(bus_dir.get())) != 0) {
+ if (contains_non_digit(de->d_name)) continue;
+
+ std::string bus_name = base + "/" + de->d_name;
+
+ std::unique_ptr<DIR, int(*)(DIR*)> dev_dir(opendir(bus_name.c_str()), closedir);
+ if (!dev_dir) continue;
+
+ while ((de = readdir(dev_dir.get()))) {
+ unsigned char devdesc[4096];
+ unsigned char* bufptr = devdesc;
+ unsigned char* bufend;
+ struct usb_device_descriptor* device;
+ struct usb_config_descriptor* config;
+ struct usb_interface_descriptor* interface;
+ struct usb_endpoint_descriptor *ep1, *ep2;
+ unsigned zero_mask = 0;
+ unsigned vid, pid;
+
+ if (contains_non_digit(de->d_name)) continue;
+
+ std::string dev_name = bus_name + "/" + de->d_name;
+ if (is_known_device(dev_name.c_str())) {
+ continue;
+ }
+
+ int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ continue;
+ }
+
+ size_t desclength = unix_read(fd, devdesc, sizeof(devdesc));
+ bufend = bufptr + desclength;
+
+ // should have device and configuration descriptors, and atleast two endpoints
+ if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
+ D("desclength %zu is too small", desclength);
+ unix_close(fd);
+ continue;
+ }
+
+ device = (struct usb_device_descriptor*)bufptr;
+ bufptr += USB_DT_DEVICE_SIZE;
+
+ if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
+ unix_close(fd);
+ continue;
+ }
+
+ vid = device->idVendor;
+ pid = device->idProduct;
+ DBGX("[ %s is V:%04x P:%04x ]\n", dev_name.c_str(), vid, pid);
+
+ // should have config descriptor next
+ config = (struct usb_config_descriptor *)bufptr;
+ bufptr += USB_DT_CONFIG_SIZE;
+ if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
+ D("usb_config_descriptor not found");
+ unix_close(fd);
+ continue;
+ }
+
+ // loop through all the descriptors and look for the ADB interface
+ while (bufptr < bufend) {
+ unsigned char length = bufptr[0];
+ unsigned char type = bufptr[1];
+
+ if (type == USB_DT_INTERFACE) {
+ interface = (struct usb_interface_descriptor *)bufptr;
+ bufptr += length;
+
+ if (length != USB_DT_INTERFACE_SIZE) {
+ D("interface descriptor has wrong size");
+ break;
+ }
+
+ DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
+ "bInterfaceProtocol: %d, bNumEndpoints: %d\n",
+ interface->bInterfaceClass, interface->bInterfaceSubClass,
+ interface->bInterfaceProtocol, interface->bNumEndpoints);
+
+ if (interface->bNumEndpoints == 2 &&
+ is_adb_interface(interface->bInterfaceClass, interface->bInterfaceSubClass,
+ interface->bInterfaceProtocol)) {
+ struct stat st;
+ char pathbuf[128];
+ char link[256];
+ char *devpath = nullptr;
+
+ DBGX("looking for bulk endpoints\n");
+ // looks like ADB...
+ ep1 = (struct usb_endpoint_descriptor *)bufptr;
+ bufptr += USB_DT_ENDPOINT_SIZE;
+ // For USB 3.0 SuperSpeed devices, skip potential
+ // USB 3.0 SuperSpeed Endpoint Companion descriptor
+ if (bufptr+2 <= devdesc + desclength &&
+ bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
+ bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
+ bufptr += USB_DT_SS_EP_COMP_SIZE;
+ }
+ ep2 = (struct usb_endpoint_descriptor *)bufptr;
+ bufptr += USB_DT_ENDPOINT_SIZE;
+ if (bufptr+2 <= devdesc + desclength &&
+ bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
+ bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
+ bufptr += USB_DT_SS_EP_COMP_SIZE;
+ }
+
+ if (bufptr > devdesc + desclength ||
+ ep1->bLength != USB_DT_ENDPOINT_SIZE ||
+ ep1->bDescriptorType != USB_DT_ENDPOINT ||
+ ep2->bLength != USB_DT_ENDPOINT_SIZE ||
+ ep2->bDescriptorType != USB_DT_ENDPOINT) {
+ D("endpoints not found");
+ break;
+ }
+
+ // both endpoints should be bulk
+ if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
+ ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
+ D("bulk endpoints not found");
+ continue;
+ }
+ /* aproto 01 needs 0 termination */
+ if(interface->bInterfaceProtocol == 0x01) {
+ zero_mask = ep1->wMaxPacketSize - 1;
+ }
+
+ // we have a match. now we just need to figure out which is in and which is out.
+ unsigned char local_ep_in, local_ep_out;
+ if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+ local_ep_in = ep1->bEndpointAddress;
+ local_ep_out = ep2->bEndpointAddress;
+ } else {
+ local_ep_in = ep2->bEndpointAddress;
+ local_ep_out = ep1->bEndpointAddress;
+ }
+
+ // Determine the device path
+ if (!fstat(fd, &st) && S_ISCHR(st.st_mode)) {
+ snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d",
+ major(st.st_rdev), minor(st.st_rdev));
+ ssize_t link_len = readlink(pathbuf, link, sizeof(link) - 1);
+ if (link_len > 0) {
+ link[link_len] = '\0';
+ const char* slash = strrchr(link, '/');
+ if (slash) {
+ snprintf(pathbuf, sizeof(pathbuf),
+ "usb:%s", slash + 1);
+ devpath = pathbuf;
+ }
+ }
+ }
+
+ register_device_callback(dev_name.c_str(), devpath,
+ local_ep_in, local_ep_out,
+ interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
+ break;
+ }
+ } else {
+ bufptr += length;
+ }
+ } // end of while
+
+ unix_close(fd);
+ }
+ }
+}
+
+static int usb_bulk_write(usb_handle* h, const void* data, int len) {
+ std::unique_lock<std::mutex> lock(h->mutex);
+ D("++ usb_bulk_write ++");
+
+ usbdevfs_urb* urb = &h->urb_out;
+ memset(urb, 0, sizeof(*urb));
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ urb->endpoint = h->ep_out;
+ urb->status = -1;
+ urb->buffer = const_cast<void*>(data);
+ urb->buffer_length = len;
+
+ if (h->dead) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
+ return -1;
+ }
+
+ h->urb_out_busy = true;
+ while (true) {
+ auto now = std::chrono::system_clock::now();
+ if (h->cv.wait_until(lock, now + 5s) == std::cv_status::timeout || h->dead) {
+ // TODO: call USBDEVFS_DISCARDURB?
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ if (!h->urb_out_busy) {
+ if (urb->status != 0) {
+ errno = -urb->status;
+ return -1;
+ }
+ return urb->actual_length;
+ }
+ }
+}
+
+static int usb_bulk_read(usb_handle* h, void* data, int len) {
+ std::unique_lock<std::mutex> lock(h->mutex);
+ D("++ usb_bulk_read ++");
+
+ usbdevfs_urb* urb = &h->urb_in;
+ memset(urb, 0, sizeof(*urb));
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ urb->endpoint = h->ep_in;
+ urb->status = -1;
+ urb->buffer = data;
+ urb->buffer_length = len;
+
+ if (h->dead) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
+ return -1;
+ }
+
+ h->urb_in_busy = true;
+ while (true) {
+ D("[ reap urb - wait ]");
+ h->reaper_thread = pthread_self();
+ int fd = h->fd;
+ lock.unlock();
+
+ // This ioctl must not have TEMP_FAILURE_RETRY because we send SIGALRM to break out.
+ usbdevfs_urb* out = nullptr;
+ int res = ioctl(fd, USBDEVFS_REAPURB, &out);
+ int saved_errno = errno;
+
+ lock.lock();
+ h->reaper_thread = 0;
+ if (h->dead) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (res < 0) {
+ if (saved_errno == EINTR) {
+ continue;
+ }
+ D("[ reap urb - error ]");
+ errno = saved_errno;
+ return -1;
+ }
+ D("[ urb @%p status = %d, actual = %d ]", out, out->status, out->actual_length);
+
+ if (out == &h->urb_in) {
+ D("[ reap urb - IN complete ]");
+ h->urb_in_busy = false;
+ if (urb->status != 0) {
+ errno = -urb->status;
+ return -1;
+ }
+ return urb->actual_length;
+ }
+ if (out == &h->urb_out) {
+ D("[ reap urb - OUT compelete ]");
+ h->urb_out_busy = false;
+ h->cv.notify_all();
+ }
+ }
+}
+
+
+int usb_write(usb_handle *h, const void *_data, int len)
+{
+ D("++ usb_write ++");
+
+ unsigned char *data = (unsigned char*) _data;
+ int n = usb_bulk_write(h, data, len);
+ if (n != len) {
+ D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno));
+ return -1;
+ }
+
+ if (h->zero_mask && !(len & h->zero_mask)) {
+ // If we need 0-markers and our transfer is an even multiple of the packet size,
+ // then send a zero marker.
+ return usb_bulk_write(h, _data, 0);
+ }
+
+ D("-- usb_write --");
+ return 0;
+}
+
+int usb_read(usb_handle *h, void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ int n;
+
+ D("++ usb_read ++");
+ while(len > 0) {
+ int xfer = len;
+
+ D("[ usb read %d fd = %d], path=%s", xfer, h->fd, h->path.c_str());
+ n = usb_bulk_read(h, data, xfer);
+ D("[ usb read %d ] = %d, path=%s", xfer, n, h->path.c_str());
+ if(n != xfer) {
+ if((errno == ETIMEDOUT) && (h->fd != -1)) {
+ D("[ timeout ]");
+ if(n > 0){
+ data += n;
+ len -= n;
+ }
+ continue;
+ }
+ D("ERROR: n = %d, errno = %d (%s)",
+ n, errno, strerror(errno));
+ return -1;
+ }
+
+ len -= xfer;
+ data += xfer;
+ }
+
+ D("-- usb_read --");
+ return 0;
+}
+
+void usb_kick(usb_handle* h) {
+ std::lock_guard<std::mutex> lock(h->mutex);
+ D("[ kicking %p (fd = %d) ]", h, h->fd);
+ if (!h->dead) {
+ h->dead = true;
+
+ if (h->writeable) {
+ /* HACK ALERT!
+ ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
+ ** This is a workaround for that problem.
+ */
+ if (h->reaper_thread) {
+ pthread_kill(h->reaper_thread, SIGALRM);
+ }
+
+ /* cancel any pending transactions
+ ** these will quietly fail if the txns are not active,
+ ** but this ensures that a reader blocked on REAPURB
+ ** will get unblocked
+ */
+ ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_in);
+ ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_out);
+ h->urb_in.status = -ENODEV;
+ h->urb_out.status = -ENODEV;
+ h->urb_in_busy = false;
+ h->urb_out_busy = false;
+ h->cv.notify_all();
+ } else {
+ unregister_usb_transport(h);
+ }
+ }
+}
+
+int usb_close(usb_handle* h) {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ g_usb_handles.remove(h);
+
+ D("-- usb close %p (fd = %d) --", h, h->fd);
+
+ delete h;
+
+ return 0;
+}
+
+static void register_device(const char* dev_name, const char* dev_path,
+ unsigned char ep_in, unsigned char ep_out,
+ int interface, int serial_index,
+ unsigned zero_mask) {
+ // Since Linux will not reassign the device ID (and dev_name) as long as the
+ // device is open, we can add to the list here once we open it and remove
+ // from the list when we're finally closed and everything will work out
+ // fine.
+ //
+ // If we have a usb_handle on the list of handles with a matching name, we
+ // have no further work to do.
+ {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ for (usb_handle* usb: g_usb_handles) {
+ if (usb->path == dev_name) {
+ return;
+ }
+ }
+ }
+
+ D("[ usb located new device %s (%d/%d/%d) ]", dev_name, ep_in, ep_out, interface);
+ std::unique_ptr<usb_handle> usb(new usb_handle);
+ usb->path = dev_name;
+ usb->ep_in = ep_in;
+ usb->ep_out = ep_out;
+ usb->zero_mask = zero_mask;
+
+ // Initialize mark so we don't get garbage collected after the device scan.
+ usb->mark = true;
+
+ usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
+ if (usb->fd == -1) {
+ // Opening RW failed, so see if we have RO access.
+ usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (usb->fd == -1) {
+ D("[ usb open %s failed: %s]", usb->path.c_str(), strerror(errno));
+ return;
+ }
+ usb->writeable = 0;
+ }
+
+ D("[ usb opened %s%s, fd=%d]",
+ usb->path.c_str(), (usb->writeable ? "" : " (read-only)"), usb->fd);
+
+ if (usb->writeable) {
+ if (ioctl(usb->fd, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
+ D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]", usb->fd, strerror(errno));
+ return;
+ }
+ }
+
+ // Read the device's serial number.
+ std::string serial_path = android::base::StringPrintf(
+ "/sys/bus/usb/devices/%s/serial", dev_path + 4);
+ std::string serial;
+ if (!android::base::ReadFileToString(serial_path, &serial)) {
+ D("[ usb read %s failed: %s ]", serial_path.c_str(), strerror(errno));
+ // We don't actually want to treat an unknown serial as an error because
+ // devices aren't able to communicate a serial number in early bringup.
+ // http://b/20883914
+ serial = "";
+ }
+ serial = android::base::Trim(serial);
+
+ // Add to the end of the active handles.
+ usb_handle* done_usb = usb.release();
+ {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ g_usb_handles.push_back(done_usb);
+ }
+ register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
+}
+
+static void device_poll_thread(void*) {
+ adb_thread_setname("device poll");
+ D("Created device thread");
+ while (true) {
+ // TODO: Use inotify.
+ find_usb_device("/dev/bus/usb", register_device);
+ kick_disconnected_devices();
+ std::this_thread::sleep_for(1s);
+ }
+}
+
+void usb_init() {
+ struct sigaction actions;
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = [](int) {};
+ sigaction(SIGALRM, &actions, nullptr);
+
+ if (!adb_thread_create(device_poll_thread, nullptr)) {
+ fatal_errno("cannot create device_poll thread");
+ }
+}
+} // namespace native
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
new file mode 100644
index 0000000..d4fc7c0
--- /dev/null
+++ b/adb/client/usb_osx.cpp
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG USB
+
+#include "sysdeps.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOMessage.h>
+#include <mach/mach_port.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <atomic>
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "adb.h"
+#include "transport.h"
+
+using namespace std::chrono_literals;
+
+namespace native {
+struct usb_handle
+{
+ UInt8 bulkIn;
+ UInt8 bulkOut;
+ IOUSBInterfaceInterface190** interface;
+ unsigned int zero_mask;
+
+ // For garbage collecting disconnected devices.
+ bool mark;
+ std::string devpath;
+ std::atomic<bool> dead;
+
+ usb_handle() : bulkIn(0), bulkOut(0), interface(nullptr),
+ zero_mask(0), mark(false), dead(false) {
+ }
+};
+
+static std::atomic<bool> usb_inited_flag;
+
+static auto& g_usb_handles_mutex = *new std::mutex();
+static auto& g_usb_handles = *new std::vector<std::unique_ptr<usb_handle>>();
+
+static bool IsKnownDevice(const std::string& devpath) {
+ std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
+ for (auto& usb : g_usb_handles) {
+ if (usb->devpath == devpath) {
+ // Set mark flag to indicate this device is still alive.
+ usb->mark = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+static void usb_kick_locked(usb_handle* handle);
+
+static void KickDisconnectedDevices() {
+ std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
+ for (auto& usb : g_usb_handles) {
+ if (!usb->mark) {
+ usb_kick_locked(usb.get());
+ } else {
+ usb->mark = false;
+ }
+ }
+}
+
+static void AddDevice(std::unique_ptr<usb_handle> handle) {
+ handle->mark = true;
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ g_usb_handles.push_back(std::move(handle));
+}
+
+static void AndroidInterfaceAdded(io_iterator_t iterator);
+static std::unique_ptr<usb_handle> CheckInterface(IOUSBInterfaceInterface190 **iface,
+ UInt16 vendor, UInt16 product);
+
+static bool FindUSBDevices() {
+ // Create the matching dictionary to find the Android device's adb interface.
+ CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
+ if (!matchingDict) {
+ LOG(ERROR) << "couldn't create USB matching dictionary";
+ return false;
+ }
+ // Create an iterator for all I/O Registry objects that match the dictionary.
+ io_iterator_t iter = 0;
+ kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
+ if (kr != KERN_SUCCESS) {
+ LOG(ERROR) << "failed to get matching services";
+ return false;
+ }
+ // Iterate over all matching objects.
+ AndroidInterfaceAdded(iter);
+ IOObjectRelease(iter);
+ return true;
+}
+
+static void
+AndroidInterfaceAdded(io_iterator_t iterator)
+{
+ kern_return_t kr;
+ io_service_t usbDevice;
+ io_service_t usbInterface;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ IOUSBInterfaceInterface220 **iface = NULL;
+ IOUSBDeviceInterface197 **dev = NULL;
+ HRESULT result;
+ SInt32 score;
+ uint32_t locationId;
+ UInt8 if_class, subclass, protocol;
+ UInt16 vendor;
+ UInt16 product;
+ UInt8 serialIndex;
+ char serial[256];
+ std::string devpath;
+
+ while ((usbInterface = IOIteratorNext(iterator))) {
+ //* Create an intermediate interface plugin
+ kr = IOCreatePlugInInterfaceForService(usbInterface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ IOObjectRelease(usbInterface);
+ if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+ LOG(ERROR) << "Unable to create an interface plug-in (" << std::hex << kr << ")";
+ continue;
+ }
+
+ //* This gets us the interface object
+ result = (*plugInInterface)->QueryInterface(
+ plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID*)&iface);
+ //* We only needed the plugin to get the interface, so discard it
+ (*plugInInterface)->Release(plugInInterface);
+ if (result || !iface) {
+ LOG(ERROR) << "Couldn't query the interface (" << std::hex << result << ")";
+ continue;
+ }
+
+ kr = (*iface)->GetInterfaceClass(iface, &if_class);
+ kr = (*iface)->GetInterfaceSubClass(iface, &subclass);
+ kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
+ if(if_class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
+ // Ignore non-ADB devices.
+ LOG(DEBUG) << "Ignoring interface with incorrect class/subclass/protocol - " << if_class
+ << ", " << subclass << ", " << protocol;
+ (*iface)->Release(iface);
+ continue;
+ }
+
+ //* this gets us an ioservice, with which we will find the actual
+ //* device; after getting a plugin, and querying the interface, of
+ //* course.
+ //* Gotta love OS X
+ kr = (*iface)->GetDevice(iface, &usbDevice);
+ if (kIOReturnSuccess != kr || !usbDevice) {
+ LOG(ERROR) << "Couldn't grab device from interface (" << std::hex << kr << ")";
+ (*iface)->Release(iface);
+ continue;
+ }
+
+ plugInInterface = NULL;
+ score = 0;
+ //* create an intermediate device plugin
+ kr = IOCreatePlugInInterfaceForService(usbDevice,
+ kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ //* only needed this to find the plugin
+ (void)IOObjectRelease(usbDevice);
+ if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+ LOG(ERROR) << "Unable to create a device plug-in (" << std::hex << kr << ")";
+ (*iface)->Release(iface);
+ continue;
+ }
+
+ result = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&dev);
+ //* only needed this to query the plugin
+ (*plugInInterface)->Release(plugInInterface);
+ if (result || !dev) {
+ LOG(ERROR) << "Couldn't create a device interface (" << std::hex << result << ")";
+ (*iface)->Release(iface);
+ continue;
+ }
+
+ //* Now after all that, we actually have a ref to the device and
+ //* the interface that matched our criteria
+ kr = (*dev)->GetDeviceVendor(dev, &vendor);
+ kr = (*dev)->GetDeviceProduct(dev, &product);
+ kr = (*dev)->GetLocationID(dev, &locationId);
+ if (kr == KERN_SUCCESS) {
+ devpath = android::base::StringPrintf("usb:%" PRIu32 "X", locationId);
+ if (IsKnownDevice(devpath)) {
+ (*dev)->Release(dev);
+ (*iface)->Release(iface);
+ continue;
+ }
+ }
+ kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
+
+ if (serialIndex > 0) {
+ IOUSBDevRequest req;
+ UInt16 buffer[256];
+ UInt16 languages[128];
+
+ memset(languages, 0, sizeof(languages));
+
+ req.bmRequestType =
+ USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = (kUSBStringDesc << 8) | 0;
+ req.wIndex = 0;
+ req.pData = languages;
+ req.wLength = sizeof(languages);
+ kr = (*dev)->DeviceRequest(dev, &req);
+
+ if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+
+ int langCount = (req.wLenDone - 2) / 2, lang;
+
+ for (lang = 1; lang <= langCount; lang++) {
+
+ memset(buffer, 0, sizeof(buffer));
+ memset(&req, 0, sizeof(req));
+
+ req.bmRequestType =
+ USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = (kUSBStringDesc << 8) | serialIndex;
+ req.wIndex = languages[lang];
+ req.pData = buffer;
+ req.wLength = sizeof(buffer);
+ kr = (*dev)->DeviceRequest(dev, &req);
+
+ if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+ int i, count;
+
+ // skip first word, and copy the rest to the serial string,
+ // changing shorts to bytes.
+ count = (req.wLenDone - 1) / 2;
+ for (i = 0; i < count; i++)
+ serial[i] = buffer[i + 1];
+ serial[i] = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ (*dev)->Release(dev);
+
+ VLOG(USB) << android::base::StringPrintf("Found vid=%04x pid=%04x serial=%s\n",
+ vendor, product, serial);
+ if (devpath.empty()) {
+ devpath = serial;
+ }
+ if (IsKnownDevice(devpath)) {
+ (*iface)->USBInterfaceClose(iface);
+ (*iface)->Release(iface);
+ continue;
+ }
+
+ std::unique_ptr<usb_handle> handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
+ vendor, product);
+ if (handle == nullptr) {
+ LOG(ERROR) << "Could not find device interface";
+ (*iface)->Release(iface);
+ continue;
+ }
+ handle->devpath = devpath;
+ usb_handle* handle_p = handle.get();
+ VLOG(USB) << "Add usb device " << serial;
+ AddDevice(std::move(handle));
+ register_usb_transport(reinterpret_cast<::usb_handle*>(handle_p), serial, devpath.c_str(),
+ 1);
+ }
+}
+
+// Used to clear both the endpoints before starting.
+// When adb quits, we might clear the host endpoint but not the device.
+// So we make sure both sides are clear before starting up.
+static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface190** interface, UInt8 bulkEp) {
+ IOReturn rc = (*interface)->ClearPipeStallBothEnds(interface, bulkEp);
+ if (rc != kIOReturnSuccess) {
+ LOG(ERROR) << "Could not clear pipe stall both ends: " << std::hex << rc;
+ return false;
+ }
+ return true;
+}
+
+//* TODO: simplify this further since we only register to get ADB interface
+//* subclass+protocol events
+static std::unique_ptr<usb_handle>
+CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product)
+{
+ std::unique_ptr<usb_handle> handle;
+ IOReturn kr;
+ UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
+ UInt8 endpoint;
+
+ //* Now open the interface. This will cause the pipes associated with
+ //* the endpoints in the interface descriptor to be instantiated
+ kr = (*interface)->USBInterfaceOpen(interface);
+ if (kr != kIOReturnSuccess) {
+ LOG(ERROR) << "Could not open interface: " << std::hex << kr;
+ return NULL;
+ }
+
+ //* Get the number of endpoints associated with this interface
+ kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
+ if (kr != kIOReturnSuccess) {
+ LOG(ERROR) << "Unable to get number of endpoints: " << std::hex << kr;
+ goto err_get_num_ep;
+ }
+
+ //* Get interface class, subclass and protocol
+ if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
+ (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
+ (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess) {
+ LOG(ERROR) << "Unable to get interface class, subclass and protocol";
+ goto err_get_interface_class;
+ }
+
+ //* check to make sure interface class, subclass and protocol match ADB
+ //* avoid opening mass storage endpoints
+ if (!is_adb_interface(interfaceClass, interfaceSubClass, interfaceProtocol)) {
+ goto err_bad_adb_interface;
+ }
+
+ handle.reset(new usb_handle);
+ if (handle == nullptr) {
+ goto err_bad_adb_interface;
+ }
+
+ //* Iterate over the endpoints for this interface and find the first
+ //* bulk in/out pipes available. These will be our read/write pipes.
+ for (endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
+ UInt8 transferType;
+ UInt16 maxPacketSize;
+ UInt8 interval;
+ UInt8 number;
+ UInt8 direction;
+
+ kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
+ &number, &transferType, &maxPacketSize, &interval);
+ if (kr != kIOReturnSuccess) {
+ LOG(ERROR) << "FindDeviceInterface - could not get pipe properties: "
+ << std::hex << kr;
+ goto err_get_pipe_props;
+ }
+
+ if (kUSBBulk != transferType) continue;
+
+ if (kUSBIn == direction) {
+ handle->bulkIn = endpoint;
+ if (!ClearPipeStallBothEnds(interface, handle->bulkIn)) goto err_get_pipe_props;
+ }
+
+ if (kUSBOut == direction) {
+ handle->bulkOut = endpoint;
+ if (!ClearPipeStallBothEnds(interface, handle->bulkOut)) goto err_get_pipe_props;
+ }
+
+ handle->zero_mask = maxPacketSize - 1;
+ }
+
+ handle->interface = interface;
+ return handle;
+
+err_get_pipe_props:
+err_bad_adb_interface:
+err_get_interface_class:
+err_get_num_ep:
+ (*interface)->USBInterfaceClose(interface);
+ return nullptr;
+}
+
+std::mutex& operate_device_lock = *new std::mutex();
+
+static void RunLoopThread(void* unused) {
+ adb_thread_setname("RunLoop");
+
+ VLOG(USB) << "RunLoopThread started";
+ while (true) {
+ {
+ std::lock_guard<std::mutex> lock_guard(operate_device_lock);
+ FindUSBDevices();
+ KickDisconnectedDevices();
+ }
+ // Signal the parent that we are running
+ usb_inited_flag = true;
+ std::this_thread::sleep_for(1s);
+ }
+ VLOG(USB) << "RunLoopThread done";
+}
+
+static void usb_cleanup() {
+ VLOG(USB) << "usb_cleanup";
+ // Wait until usb operations in RunLoopThread finish, and prevent further operations.
+ operate_device_lock.lock();
+ close_usb_devices();
+}
+
+void usb_init() {
+ static bool initialized = false;
+ if (!initialized) {
+ atexit(usb_cleanup);
+
+ usb_inited_flag = false;
+
+ if (!adb_thread_create(RunLoopThread, nullptr)) {
+ fatal_errno("cannot create RunLoop thread");
+ }
+
+ // Wait for initialization to finish
+ while (!usb_inited_flag) {
+ std::this_thread::sleep_for(100ms);
+ }
+
+ initialized = true;
+ }
+}
+
+int usb_write(usb_handle *handle, const void *buf, int len)
+{
+ IOReturn result;
+
+ if (!len)
+ return 0;
+
+ if (!handle || handle->dead)
+ return -1;
+
+ if (NULL == handle->interface) {
+ LOG(ERROR) << "usb_write interface was null";
+ return -1;
+ }
+
+ if (0 == handle->bulkOut) {
+ LOG(ERROR) << "bulkOut endpoint not assigned";
+ return -1;
+ }
+
+ result =
+ (*handle->interface)->WritePipe(handle->interface, handle->bulkOut, (void *)buf, len);
+
+ if ((result == 0) && (handle->zero_mask)) {
+ /* we need 0-markers and our transfer */
+ if(!(len & handle->zero_mask)) {
+ result =
+ (*handle->interface)->WritePipe(
+ handle->interface, handle->bulkOut, (void *)buf, 0);
+ }
+ }
+
+ if (0 == result)
+ return 0;
+
+ LOG(ERROR) << "usb_write failed with status: " << std::hex << result;
+ return -1;
+}
+
+int usb_read(usb_handle *handle, void *buf, int len)
+{
+ IOReturn result;
+ UInt32 numBytes = len;
+
+ if (!len) {
+ return 0;
+ }
+
+ if (!handle || handle->dead) {
+ return -1;
+ }
+
+ if (NULL == handle->interface) {
+ LOG(ERROR) << "usb_read interface was null";
+ return -1;
+ }
+
+ if (0 == handle->bulkIn) {
+ LOG(ERROR) << "bulkIn endpoint not assigned";
+ return -1;
+ }
+
+ result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
+
+ if (kIOUSBPipeStalled == result) {
+ LOG(ERROR) << "Pipe stalled, clearing stall.\n";
+ (*handle->interface)->ClearPipeStall(handle->interface, handle->bulkIn);
+ result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
+ }
+
+ if (kIOReturnSuccess == result)
+ return 0;
+ else {
+ LOG(ERROR) << "usb_read failed with status: " << std::hex << result;
+ }
+
+ return -1;
+}
+
+int usb_close(usb_handle *handle)
+{
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ for (auto it = g_usb_handles.begin(); it != g_usb_handles.end(); ++it) {
+ if ((*it).get() == handle) {
+ g_usb_handles.erase(it);
+ break;
+ }
+ }
+ return 0;
+}
+
+static void usb_kick_locked(usb_handle *handle)
+{
+ LOG(INFO) << "Kicking handle";
+ /* release the interface */
+ if (!handle)
+ return;
+
+ if (!handle->dead)
+ {
+ handle->dead = true;
+ (*handle->interface)->USBInterfaceClose(handle->interface);
+ (*handle->interface)->Release(handle->interface);
+ }
+}
+
+void usb_kick(usb_handle *handle) {
+ // Use the lock to avoid multiple thread kicking the device at the same time.
+ std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
+ usb_kick_locked(handle);
+}
+} // namespace native
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
new file mode 100644
index 0000000..640e91e
--- /dev/null
+++ b/adb/client/usb_windows.cpp
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG USB
+
+#include "sysdeps.h"
+
+#include <winsock2.h> // winsock.h *must* be included before windows.h.
+#include <windows.h>
+#include <usb100.h>
+#include <winerror.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <mutex>
+#include <thread>
+
+#include <adb_api.h>
+
+#include <android-base/errors.h>
+
+#include "adb.h"
+#include "sysdeps/chrono.h"
+#include "transport.h"
+
+/** Structure usb_handle describes our connection to the usb device via
+ AdbWinApi.dll. This structure is returned from usb_open() routine and
+ is expected in each subsequent call that is accessing the device.
+
+ Most members are protected by usb_lock, except for adb_{read,write}_pipe which
+ rely on AdbWinApi.dll's handle validation and AdbCloseHandle(endpoint)'s
+ ability to break a thread out of pipe IO.
+*/
+struct usb_handle {
+ /// Previous entry in the list of opened usb handles
+ usb_handle *prev;
+
+ /// Next entry in the list of opened usb handles
+ usb_handle *next;
+
+ /// Handle to USB interface
+ ADBAPIHANDLE adb_interface;
+
+ /// Handle to USB read pipe (endpoint)
+ ADBAPIHANDLE adb_read_pipe;
+
+ /// Handle to USB write pipe (endpoint)
+ ADBAPIHANDLE adb_write_pipe;
+
+ /// Interface name
+ wchar_t* interface_name;
+
+ /// Mask for determining when to use zero length packets
+ unsigned zero_mask;
+};
+
+/// Class ID assigned to the device by androidusb.sys
+static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
+
+/// List of opened usb handles
+static usb_handle handle_list = {
+ .prev = &handle_list,
+ .next = &handle_list,
+};
+
+/// Locker for the list of opened usb handles
+static std::mutex& usb_lock = *new std::mutex();
+
+/// Checks if there is opened usb handle in handle_list for this device.
+int known_device(const wchar_t* dev_name);
+
+/// Checks if there is opened usb handle in handle_list for this device.
+/// usb_lock mutex must be held before calling this routine.
+int known_device_locked(const wchar_t* dev_name);
+
+/// Registers opened usb handle (adds it to handle_list).
+int register_new_device(usb_handle* handle);
+
+/// Checks if interface (device) matches certain criteria
+int recognized_device(usb_handle* handle);
+
+/// Enumerates present and available interfaces (devices), opens new ones and
+/// registers usb transport for them.
+void find_devices();
+
+/// Kicks all USB devices
+static void kick_devices();
+
+/// Entry point for thread that polls (every second) for new usb interfaces.
+/// This routine calls find_devices in infinite loop.
+static void device_poll_thread(void*);
+
+/// Initializes this module
+void usb_init();
+
+/// Opens usb interface (device) by interface (device) name.
+usb_handle* do_usb_open(const wchar_t* interface_name);
+
+/// Writes data to the opened usb handle
+int usb_write(usb_handle* handle, const void* data, int len);
+
+/// Reads data using the opened usb handle
+int usb_read(usb_handle *handle, void* data, int len);
+
+/// Cleans up opened usb handle
+void usb_cleanup_handle(usb_handle* handle);
+
+/// Cleans up (but don't close) opened usb handle
+void usb_kick(usb_handle* handle);
+
+/// Closes opened usb handle
+int usb_close(usb_handle* handle);
+
+int known_device_locked(const wchar_t* dev_name) {
+ usb_handle* usb;
+
+ if (NULL != dev_name) {
+ // Iterate through the list looking for the name match.
+ for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+ // In Windows names are not case sensetive!
+ if((NULL != usb->interface_name) &&
+ (0 == wcsicmp(usb->interface_name, dev_name))) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int known_device(const wchar_t* dev_name) {
+ int ret = 0;
+
+ if (NULL != dev_name) {
+ std::lock_guard<std::mutex> lock(usb_lock);
+ ret = known_device_locked(dev_name);
+ }
+
+ return ret;
+}
+
+int register_new_device(usb_handle* handle) {
+ if (NULL == handle)
+ return 0;
+
+ std::lock_guard<std::mutex> lock(usb_lock);
+
+ // Check if device is already in the list
+ if (known_device_locked(handle->interface_name)) {
+ return 0;
+ }
+
+ // Not in the list. Add this handle to the list.
+ handle->next = &handle_list;
+ handle->prev = handle_list.prev;
+ handle->prev->next = handle;
+ handle->next->prev = handle;
+
+ return 1;
+}
+
+void device_poll_thread(void*) {
+ adb_thread_setname("Device Poll");
+ D("Created device thread");
+
+ while (true) {
+ find_devices();
+ std::this_thread::sleep_for(1s);
+ }
+}
+
+static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam) {
+ switch (uMsg) {
+ case WM_POWERBROADCAST:
+ switch (wParam) {
+ case PBT_APMRESUMEAUTOMATIC:
+ // Resuming from sleep or hibernation, so kick all existing USB devices
+ // and then allow the device_poll_thread to redetect USB devices from
+ // scratch. If we don't do this, existing USB devices will never respond
+ // to us because they'll be waiting for the connect/auth handshake.
+ D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
+ "so kicking all USB devices\n");
+ kick_devices();
+ return TRUE;
+ }
+ }
+ return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+}
+
+static void _power_notification_thread(void*) {
+ // This uses a thread with its own window message pump to get power
+ // notifications. If adb runs from a non-interactive service account, this
+ // might not work (not sure). If that happens to not work, we could use
+ // heavyweight WMI APIs to get power notifications. But for the common case
+ // of a developer's interactive session, a window message pump is more
+ // appropriate.
+ D("Created power notification thread");
+ adb_thread_setname("Power Notifier");
+
+ // Window class names are process specific.
+ static const WCHAR kPowerNotificationWindowClassName[] =
+ L"PowerNotificationWindow";
+
+ // Get the HINSTANCE corresponding to the module that _power_window_proc
+ // is in (the main module).
+ const HINSTANCE instance = GetModuleHandleW(NULL);
+ if (!instance) {
+ // This is such a common API call that this should never fail.
+ fatal("GetModuleHandleW failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+
+ WNDCLASSEXW wndclass;
+ memset(&wndclass, 0, sizeof(wndclass));
+ wndclass.cbSize = sizeof(wndclass);
+ wndclass.lpfnWndProc = _power_window_proc;
+ wndclass.hInstance = instance;
+ wndclass.lpszClassName = kPowerNotificationWindowClassName;
+ if (!RegisterClassExW(&wndclass)) {
+ fatal("RegisterClassExW failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+
+ if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
+ L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0,
+ NULL, NULL, instance, NULL)) {
+ fatal("CreateWindowExW failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+
+ MSG msg;
+ while (GetMessageW(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+
+ // GetMessageW() will return false if a quit message is posted. We don't
+ // do that, but it might be possible for that to occur when logging off or
+ // shutting down. Not a big deal since the whole process will be going away
+ // soon anyway.
+ D("Power notification thread exiting");
+}
+
+void usb_init() {
+ if (!adb_thread_create(device_poll_thread, nullptr)) {
+ fatal_errno("cannot create device poll thread");
+ }
+ if (!adb_thread_create(_power_notification_thread, nullptr)) {
+ fatal_errno("cannot create power notification thread");
+ }
+}
+
+usb_handle* do_usb_open(const wchar_t* interface_name) {
+ unsigned long name_len = 0;
+
+ // Allocate our handle
+ usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
+ if (NULL == ret) {
+ D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle),
+ strerror(errno));
+ goto fail;
+ }
+
+ // Set linkers back to the handle
+ ret->next = ret;
+ ret->prev = ret;
+
+ // Create interface.
+ ret->adb_interface = AdbCreateInterfaceByName(interface_name);
+ if (NULL == ret->adb_interface) {
+ D("AdbCreateInterfaceByName failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
+ }
+
+ // Open read pipe (endpoint)
+ ret->adb_read_pipe =
+ AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
+ AdbOpenAccessTypeReadWrite,
+ AdbOpenSharingModeReadWrite);
+ if (NULL == ret->adb_read_pipe) {
+ D("AdbOpenDefaultBulkReadEndpoint failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
+ }
+
+ // Open write pipe (endpoint)
+ ret->adb_write_pipe =
+ AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
+ AdbOpenAccessTypeReadWrite,
+ AdbOpenSharingModeReadWrite);
+ if (NULL == ret->adb_write_pipe) {
+ D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
+ }
+
+ // Save interface name
+ // First get expected name length
+ AdbGetInterfaceName(ret->adb_interface,
+ NULL,
+ &name_len,
+ false);
+ if (0 == name_len) {
+ D("AdbGetInterfaceName returned name length of zero: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
+ }
+
+ ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
+ if (NULL == ret->interface_name) {
+ D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
+ goto fail;
+ }
+
+ // Now save the name
+ if (!AdbGetInterfaceName(ret->adb_interface,
+ ret->interface_name,
+ &name_len,
+ false)) {
+ D("AdbGetInterfaceName failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
+ }
+
+ // We're done at this point
+ return ret;
+
+fail:
+ if (NULL != ret) {
+ usb_cleanup_handle(ret);
+ free(ret);
+ }
+
+ return NULL;
+}
+
+int usb_write(usb_handle* handle, const void* data, int len) {
+ unsigned long time_out = 5000;
+ unsigned long written = 0;
+ int err = 0;
+
+ D("usb_write %d", len);
+ if (NULL == handle) {
+ D("usb_write was passed NULL handle");
+ err = EINVAL;
+ goto fail;
+ }
+
+ // Perform write
+ if (!AdbWriteEndpointSync(handle->adb_write_pipe,
+ (void*)data,
+ (unsigned long)len,
+ &written,
+ time_out)) {
+ D("AdbWriteEndpointSync failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ err = EIO;
+ goto fail;
+ }
+
+ // Make sure that we've written what we were asked to write
+ D("usb_write got: %ld, expected: %d", written, len);
+ if (written != (unsigned long)len) {
+ // If this occurs, this code should be changed to repeatedly call
+ // AdbWriteEndpointSync() until all bytes are written.
+ D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld",
+ len, written);
+ err = EIO;
+ goto fail;
+ }
+
+ if (handle->zero_mask && (len & handle->zero_mask) == 0) {
+ // Send a zero length packet
+ if (!AdbWriteEndpointSync(handle->adb_write_pipe,
+ (void*)data,
+ 0,
+ &written,
+ time_out)) {
+ D("AdbWriteEndpointSync of zero length packet failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ err = EIO;
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ // Any failure should cause us to kick the device instead of leaving it a
+ // zombie state with potential to hang.
+ if (NULL != handle) {
+ D("Kicking device due to error in usb_write");
+ usb_kick(handle);
+ }
+
+ D("usb_write failed");
+ errno = err;
+ return -1;
+}
+
+int usb_read(usb_handle *handle, void* data, int len) {
+ unsigned long time_out = 0;
+ unsigned long read = 0;
+ int err = 0;
+
+ D("usb_read %d", len);
+ if (NULL == handle) {
+ D("usb_read was passed NULL handle");
+ err = EINVAL;
+ goto fail;
+ }
+
+ while (len > 0) {
+ if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
+ time_out)) {
+ D("AdbReadEndpointSync failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ err = EIO;
+ goto fail;
+ }
+ D("usb_read got: %ld, expected: %d", read, len);
+
+ data = (char *)data + read;
+ len -= read;
+ }
+
+ return 0;
+
+fail:
+ // Any failure should cause us to kick the device instead of leaving it a
+ // zombie state with potential to hang.
+ if (NULL != handle) {
+ D("Kicking device due to error in usb_read");
+ usb_kick(handle);
+ }
+
+ D("usb_read failed");
+ errno = err;
+ return -1;
+}
+
+// Wrapper around AdbCloseHandle() that logs diagnostics.
+static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
+ if (!AdbCloseHandle(adb_handle)) {
+ D("AdbCloseHandle(%p) failed: %s", adb_handle,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+}
+
+void usb_cleanup_handle(usb_handle* handle) {
+ D("usb_cleanup_handle");
+ if (NULL != handle) {
+ if (NULL != handle->interface_name)
+ free(handle->interface_name);
+ // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
+ // wait until the pipe no longer uses the interface. Then we can
+ // AdbCloseHandle() the interface.
+ if (NULL != handle->adb_write_pipe)
+ _adb_close_handle(handle->adb_write_pipe);
+ if (NULL != handle->adb_read_pipe)
+ _adb_close_handle(handle->adb_read_pipe);
+ if (NULL != handle->adb_interface)
+ _adb_close_handle(handle->adb_interface);
+
+ handle->interface_name = NULL;
+ handle->adb_write_pipe = NULL;
+ handle->adb_read_pipe = NULL;
+ handle->adb_interface = NULL;
+ }
+}
+
+static void usb_kick_locked(usb_handle* handle) {
+ // The reason the lock must be acquired before calling this function is in
+ // case multiple threads are trying to kick the same device at the same time.
+ usb_cleanup_handle(handle);
+}
+
+void usb_kick(usb_handle* handle) {
+ D("usb_kick");
+ if (NULL != handle) {
+ std::lock_guard<std::mutex> lock(usb_lock);
+ usb_kick_locked(handle);
+ } else {
+ errno = EINVAL;
+ }
+}
+
+int usb_close(usb_handle* handle) {
+ D("usb_close");
+
+ if (NULL != handle) {
+ // Remove handle from the list
+ {
+ std::lock_guard<std::mutex> lock(usb_lock);
+
+ if ((handle->next != handle) && (handle->prev != handle)) {
+ handle->next->prev = handle->prev;
+ handle->prev->next = handle->next;
+ handle->prev = handle;
+ handle->next = handle;
+ }
+ }
+
+ // Cleanup handle
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+
+ return 0;
+}
+
+int recognized_device(usb_handle* handle) {
+ if (NULL == handle)
+ return 0;
+
+ // Check vendor and product id first
+ USB_DEVICE_DESCRIPTOR device_desc;
+
+ if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
+ &device_desc)) {
+ D("AdbGetUsbDeviceDescriptor failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ return 0;
+ }
+
+ // Then check interface properties
+ USB_INTERFACE_DESCRIPTOR interf_desc;
+
+ if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
+ &interf_desc)) {
+ D("AdbGetUsbInterfaceDescriptor failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ return 0;
+ }
+
+ // Must have two endpoints
+ if (2 != interf_desc.bNumEndpoints) {
+ return 0;
+ }
+
+ if (is_adb_interface(interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass,
+ interf_desc.bInterfaceProtocol)) {
+ if (interf_desc.bInterfaceProtocol == 0x01) {
+ AdbEndpointInformation endpoint_info;
+ // assuming zero is a valid bulk endpoint ID
+ if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
+ handle->zero_mask = endpoint_info.max_packet_size - 1;
+ D("device zero_mask: 0x%x", handle->zero_mask);
+ } else {
+ D("AdbGetEndpointInformation failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void find_devices() {
+ usb_handle* handle = NULL;
+ char entry_buffer[2048];
+ AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
+ unsigned long entry_buffer_size = sizeof(entry_buffer);
+
+ // Enumerate all present and active interfaces.
+ ADBAPIHANDLE enum_handle =
+ AdbEnumInterfaces(usb_class_id, true, true, true);
+
+ if (NULL == enum_handle) {
+ D("AdbEnumInterfaces failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ return;
+ }
+
+ while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
+ // Lets see if we already have this device in the list
+ if (!known_device(next_interface->device_name)) {
+ // This seems to be a new device. Open it!
+ handle = do_usb_open(next_interface->device_name);
+ if (NULL != handle) {
+ // Lets see if this interface (device) belongs to us
+ if (recognized_device(handle)) {
+ D("adding a new device %ls", next_interface->device_name);
+
+ // We don't request a wchar_t string from AdbGetSerialNumber() because of a bug in
+ // adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString, bytes_written) where the
+ // last parameter should be (str_len * sizeof(wchar_t)). The bug reads 2 bytes past the
+ // end of a stack buffer in the best case, and in the unlikely case of a long serial
+ // number, it will read 2 bytes past the end of a heap allocation. This doesn't affect the
+ // resulting string, but we should avoid the bad reads in the first place.
+ char serial_number[512];
+ unsigned long serial_number_len = sizeof(serial_number);
+ if (AdbGetSerialNumber(handle->adb_interface,
+ serial_number,
+ &serial_number_len,
+ true)) {
+ // Lets make sure that we don't duplicate this device
+ if (register_new_device(handle)) {
+ register_usb_transport(handle, serial_number, NULL, 1);
+ } else {
+ D("register_new_device failed for %ls", next_interface->device_name);
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+ } else {
+ D("cannot get serial number: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+ } else {
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+ }
+ }
+
+ entry_buffer_size = sizeof(entry_buffer);
+ }
+
+ if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+ // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
+ D("AdbNextInterface failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+
+ _adb_close_handle(enum_handle);
+}
+
+static void kick_devices() {
+ // Need to acquire lock to safely walk the list which might be modified
+ // by another thread.
+ std::lock_guard<std::mutex> lock(usb_lock);
+ for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+ usb_kick_locked(usb);
+ }
+}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index c7b7675..2befa0c 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
#include "sysdeps.h"
@@ -31,11 +31,20 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <memory>
#include <string>
+#include <thread>
+#include <vector>
-#include <base/stringprintf.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#if !defined(_WIN32)
+#include <signal.h>
+#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#endif
@@ -44,16 +53,26 @@
#include "adb_auth.h"
#include "adb_client.h"
#include "adb_io.h"
+#include "adb_unique_fd.h"
#include "adb_utils.h"
+#include "bugreport.h"
+#include "commandline.h"
#include "file_sync_service.h"
+#include "services.h"
+#include "shell_service.h"
+#include "sysdeps/chrono.h"
static int install_app(TransportType t, const char* serial, int argc, const char** argv);
static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
+static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
+static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
-static std::string gProductOutPath;
+static auto& gProductOutPath = *new std::string();
extern int gListenAll;
+DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
+
static std::string product_file(const char *extra) {
if (gProductOutPath.empty()) {
fprintf(stderr, "adb: Product directory not specified; "
@@ -66,163 +85,153 @@
}
static void help() {
- fprintf(stderr, "%s\n", adb_version().c_str());
- fprintf(stderr,
- " -a - directs adb to listen on all interfaces for a connection\n"
- " -d - directs command to the only connected USB device\n"
- " returns an error if more than one USB device is present.\n"
- " -e - directs command to the only running emulator.\n"
- " returns an error if more than one emulator is running.\n"
- " -s <specific device> - directs command to the device or emulator with the given\n"
- " serial number or qualifier. Overrides ANDROID_SERIAL\n"
- " environment variable.\n"
- " -p <product name or path> - simple product name like 'sooner', or\n"
- " a relative/absolute path to a product\n"
- " out directory like 'out/target/product/sooner'.\n"
- " If -p is not specified, the ANDROID_PRODUCT_OUT\n"
- " environment variable is used, which must\n"
- " be an absolute path.\n"
- " -H - Name of adb server host (default: localhost)\n"
- " -P - Port of adb server (default: 5037)\n"
- " devices [-l] - list all connected devices\n"
- " ('-l' will also list device qualifiers)\n"
- " connect <host>[:<port>] - connect to a device via TCP/IP\n"
- " Port 5555 is used by default if no port number is specified.\n"
- " disconnect [<host>[:<port>]] - disconnect from a TCP/IP device.\n"
- " Port 5555 is used by default if no port number is specified.\n"
- " Using this command with no additional arguments\n"
- " will disconnect from all connected TCP/IP devices.\n"
+ fprintf(stdout, "%s\n", adb_version().c_str());
+ // clang-format off
+ fprintf(stdout,
+ "global options:\n"
+ " -a listen on all network interfaces, not just localhost\n"
+ " -d use USB device (error if multiple devices connected)\n"
+ " -e use TCP/IP device (error if multiple TCP/IP devices available)\n"
+ " -s SERIAL\n"
+ " use device with given serial number (overrides $ANDROID_SERIAL)\n"
+ " -p PRODUCT\n"
+ " name or path ('angler'/'out/target/product/angler');\n"
+ " default $ANDROID_PRODUCT_OUT\n"
+ " -H name of adb server host [default=localhost]\n"
+ " -P port of adb server [default=5037]\n"
+ " -L SOCKET listen on given socket for adb server [default=tcp:localhost:5037]\n"
"\n"
- "device commands:\n"
- " adb push [-p] <local> <remote>\n"
- " - copy file/dir to device\n"
- " ('-p' to display the transfer progress)\n"
- " adb pull [-p] [-a] <remote> [<local>]\n"
- " - copy file/dir from device\n"
- " ('-p' to display the transfer progress)\n"
- " ('-a' means copy timestamp and mode)\n"
- " adb sync [ <directory> ] - copy host->device only if changed\n"
- " (-l means list but don't copy)\n"
- " adb shell - run remote shell interactively\n"
- " adb shell <command> - run remote shell command\n"
- " adb emu <command> - run emulator console command\n"
- " adb logcat [ <filter-spec> ] - View device log\n"
- " adb forward --list - list all forward socket connections.\n"
- " the format is a list of lines with the following format:\n"
- " <serial> \" \" <local> \" \" <remote> \"\\n\"\n"
- " adb forward <local> <remote> - forward socket connections\n"
- " forward specs are one of: \n"
- " tcp:<port>\n"
- " localabstract:<unix domain socket name>\n"
- " localreserved:<unix domain socket name>\n"
- " localfilesystem:<unix domain socket name>\n"
- " dev:<character device name>\n"
- " jdwp:<process pid> (remote only)\n"
- " adb forward --no-rebind <local> <remote>\n"
- " - same as 'adb forward <local> <remote>' but fails\n"
- " if <local> is already forwarded\n"
- " adb forward --remove <local> - remove a specific forward socket connection\n"
- " adb forward --remove-all - remove all forward socket connections\n"
- " adb reverse --list - list all reverse socket connections from device\n"
- " adb reverse <remote> <local> - reverse socket connections\n"
- " reverse specs are one of:\n"
- " tcp:<port>\n"
- " localabstract:<unix domain socket name>\n"
- " localreserved:<unix domain socket name>\n"
- " localfilesystem:<unix domain socket name>\n"
- " adb reverse --norebind <remote> <local>\n"
- " - same as 'adb reverse <remote> <local>' but fails\n"
- " if <remote> is already reversed.\n"
- " adb reverse --remove <remote>\n"
- " - remove a specific reversed socket connection\n"
- " adb reverse --remove-all - remove all reversed socket connections from device\n"
- " adb jdwp - list PIDs of processes hosting a JDWP transport\n"
- " adb install [-lrtsd] <file>\n"
- " adb install-multiple [-lrtsdp] <file...>\n"
- " - push this package file to the device and install it\n"
- " (-l: forward lock application)\n"
- " (-r: replace existing application)\n"
- " (-t: allow test packages)\n"
- " (-s: install application on sdcard)\n"
- " (-d: allow version code downgrade)\n"
- " (-p: partial application install)\n"
- " adb uninstall [-k] <package> - remove this app package from the device\n"
- " ('-k' means keep the data and cache directories)\n"
- " adb bugreport - return all information from the device\n"
- " that should be included in a bug report.\n"
- "\n"
- " adb backup [-f <file>] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [<packages...>]\n"
- " - write an archive of the device's data to <file>.\n"
- " If no -f option is supplied then the data is written\n"
- " to \"backup.ab\" in the current directory.\n"
- " (-apk|-noapk enable/disable backup of the .apks themselves\n"
- " in the archive; the default is noapk.)\n"
- " (-obb|-noobb enable/disable backup of any installed apk expansion\n"
- " (aka .obb) files associated with each application; the default\n"
- " is noobb.)\n"
- " (-shared|-noshared enable/disable backup of the device's\n"
- " shared storage / SD card contents; the default is noshared.)\n"
- " (-all means to back up all installed applications)\n"
- " (-system|-nosystem toggles whether -all automatically includes\n"
- " system applications; the default is to include system apps)\n"
- " (<packages...> is the list of applications to be backed up. If\n"
- " the -all or -shared flags are passed, then the package\n"
- " list is optional. Applications explicitly given on the\n"
- " command line will be included even if -nosystem would\n"
- " ordinarily cause them to be omitted.)\n"
- "\n"
- " adb restore <file> - restore device contents from the <file> backup archive\n"
- "\n"
- " adb disable-verity - disable dm-verity checking on USERDEBUG builds\n"
- " adb enable-verity - re-enable dm-verity checking on USERDEBUG builds\n"
- " adb keygen <file> - generate adb public/private key. The private key is stored in <file>,\n"
- " and the public key is stored in <file>.pub. Any existing files\n"
- " are overwritten.\n"
- " adb help - show this help message\n"
- " adb version - show version num\n"
- "\n"
- "scripting:\n"
- " adb wait-for-device - block until device is online\n"
- " adb start-server - ensure that there is a server running\n"
- " adb kill-server - kill the server if it is running\n"
- " adb get-state - prints: offline | bootloader | device\n"
- " adb get-serialno - prints: <serial-number>\n"
- " adb get-devpath - prints: <device-path>\n"
- " adb remount - remounts the /system, /vendor (if present) and /oem (if present) partitions on the device read-write\n"
- " adb reboot [bootloader|recovery]\n"
- " - reboots the device, optionally into the bootloader or recovery program.\n"
- " adb reboot sideload - reboots the device into the sideload mode in recovery program (adb root required).\n"
- " adb reboot sideload-auto-reboot\n"
- " - reboots into the sideload mode, then reboots automatically after the sideload regardless of the result.\n"
- " adb sideload <file> - sideloads the given package\n"
- " adb root - restarts the adbd daemon with root permissions\n"
- " adb unroot - restarts the adbd daemon without root permissions\n"
- " adb usb - restarts the adbd daemon listening on USB\n"
- " adb tcpip <port> - restarts the adbd daemon listening on TCP on the specified port\n"
+ "general commands:\n"
+ " devices [-l] list connected devices (-l for long output)\n"
+ " help show this help message\n"
+ " version show version num\n"
"\n"
"networking:\n"
- " adb ppp <tty> [parameters] - Run PPP over USB.\n"
- " Note: you should not automatically start a PPP connection.\n"
- " <tty> refers to the tty for PPP stream. Eg. dev:/dev/omap_csmi_tty1\n"
- " [parameters] - Eg. defaultroute debug dump local notty usepeerdns\n"
+ " connect HOST[:PORT] connect to a device via TCP/IP [default port=5555]\n"
+ " disconnect [HOST[:PORT]]\n"
+ " disconnect from given TCP/IP device [default port=5555], or all\n"
+ " forward --list list all forward socket connections\n"
+ " forward [--no-rebind] LOCAL REMOTE\n"
+ " forward socket connection using:\n"
+ " tcp:<port> (<local> may be \"tcp:0\" to pick any open port)\n"
+ " localabstract:<unix domain socket name>\n"
+ " localreserved:<unix domain socket name>\n"
+ " localfilesystem:<unix domain socket name>\n"
+ " dev:<character device name>\n"
+ " jdwp:<process pid> (remote only)\n"
+ " forward --remove LOCAL remove specific forward socket connection\n"
+ " forward --remove-all remove all forward socket connections\n"
+ " ppp TTY [PARAMETER...] run PPP over USB\n"
+ " reverse --list list all reverse socket connections from device\n"
+ " reverse [--no-rebind] REMOTE LOCAL\n"
+ " reverse socket connection using:\n"
+ " tcp:<port> (<remote> may be \"tcp:0\" to pick any open port)\n"
+ " localabstract:<unix domain socket name>\n"
+ " localreserved:<unix domain socket name>\n"
+ " localfilesystem:<unix domain socket name>\n"
+ " reverse --remove REMOTE remove specific reverse socket connection\n"
+ " reverse --remove-all remove all reverse socket connections from device\n"
"\n"
- "adb sync notes: adb sync [ <directory> ]\n"
- " <localdir> can be interpreted in several ways:\n"
+ "file transfer:\n"
+ " push LOCAL... REMOTE\n"
+ " copy local files/directories to device\n"
+ " pull [-a] REMOTE... LOCAL\n"
+ " copy files/dirs from device\n"
+ " -a: preserve file timestamp and mode\n"
+ " sync [DIR]\n"
+ " copy all changed files to device; if DIR is \"system\", \"vendor\", \"oem\",\n"
+ " or \"data\", only sync that partition (default all)\n"
+ " -l: list but don't copy\n"
"\n"
- " - If <directory> is not specified, /system, /vendor (if present), /oem (if present) and /data partitions will be updated.\n"
+ "shell:\n"
+ " shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
+ " run remote shell command (interactive shell if no command given)\n"
+ " -e: choose escape character, or \"none\"; default '~'\n"
+ " -n: don't read from stdin\n"
+ " -T: disable PTY allocation\n"
+ " -t: force PTY allocation\n"
+ " -x: disable remote exit codes and stdout/stderr separation\n"
+ " emu COMMAND run emulator console command\n"
"\n"
- " - If it is \"system\", \"vendor\", \"oem\" or \"data\", only the corresponding partition\n"
- " is updated.\n"
+ "app installation:\n"
+ " install [-lrtsdg] PACKAGE\n"
+ " install-multiple [-lrtsdpg] PACKAGE...\n"
+ " push package(s) to the device and install them\n"
+ " -l: forward lock application\n"
+ " -r: replace existing application\n"
+ " -t: allow test packages\n"
+ " -s: install application on sdcard\n"
+ " -d: allow version code downgrade (debuggable packages only)\n"
+ " -p: partial application install (install-multiple only)\n"
+ " -g: grant all runtime permissions\n"
+ " uninstall [-k] PACKAGE\n"
+ " remove this app package from the device\n"
+ " '-k': keep the data and cache directories\n"
+ "\n"
+ "backup/restore:\n"
+ " backup [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [PACKAGE...]\n"
+ " write an archive of the device's data to FILE [default=backup.adb]\n"
+ " package list optional if -all/-shared are supplied\n"
+ " -apk/-noapk: do/don't back up .apk files (default -noapk)\n"
+ " -obb/-noobb: do/don't back up .obb files (default -noobb)\n"
+ " -shared|-noshared: do/don't back up shared storage (default -noshared)\n"
+ " -all: back up all installed applications\n"
+ " -system|-nosystem: include system apps in -all (default -system)\n"
+ " restore FILE restore device contents from FILE\n"
+ "\n"
+ "debugging:\n"
+ " bugreport [PATH]\n"
+ " write bugreport to given PATH [default=bugreport.zip];\n"
+ " if PATH is a directory, the bug report is saved in that directory.\n"
+ " devices that don't support zipped bug reports output to stdout.\n"
+ " jdwp list pids of processes hosting a JDWP transport\n"
+ " logcat show device log (logcat --help for more)\n"
+ "\n"
+ "security:\n"
+ " disable-verity disable dm-verity checking on userdebug builds\n"
+ " enable-verity re-enable dm-verity checking on userdebug builds\n"
+ " keygen FILE\n"
+ " generate adb public/private key; private key stored in FILE,\n"
+ " public key stored in FILE.pub (existing files overwritten)\n"
+ "\n"
+ "scripting:\n"
+ " wait-for[-TRANSPORT]-STATE\n"
+ " wait for device to be in the given state\n"
+ " State: device, recovery, sideload, or bootloader\n"
+ " Transport: usb, local, or any [default=any]\n"
+ " get-state print offline | bootloader | device\n"
+ " get-serialno print <serial-number>\n"
+ " get-devpath print <device-path>\n"
+ " remount\n"
+ " remount /system, /vendor, and /oem partitions read-write\n"
+ " reboot [bootloader|recovery|sideload|sideload-auto-reboot]\n"
+ " reboot the device; defaults to booting system image but\n"
+ " supports bootloader and recovery too. sideload reboots\n"
+ " into recovery and automatically starts sideload mode,\n"
+ " sideload-auto-reboot is the same but reboots after sideloading.\n"
+ " sideload OTAPACKAGE sideload the given full OTA package\n"
+ " root restart adbd with root permissions\n"
+ " unroot restart adbd without root permissions\n"
+ " usb restart adb server listening on USB\n"
+ " tcpip PORT restart adb server listening on TCP on PORT\n"
+ "\n"
+ "internal debugging:\n"
+ " start-server ensure that there is a server running\n"
+ " kill-server kill the server if it is running\n"
+ " reconnect kick connection from host side to force reconnect\n"
+ " reconnect device kick connection from device side to force reconnect\n"
"\n"
"environment variables:\n"
- " ADB_TRACE - Print debug information. A comma separated list of the following values\n"
- " 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
- " ANDROID_SERIAL - The serial number to connect to. -s takes priority over this if given.\n"
- " ANDROID_LOG_TAGS - When used with the logcat option, only these debug tags are printed.\n"
- );
+ " $ADB_TRACE\n"
+ " comma-separated list of debug info to log:\n"
+ " all,adb,sockets,packets,rwx,usb,sync,sysdeps,transport,jdwp\n"
+ " $ADB_VENDOR_KEYS colon-separated list of keys (files or directories)\n"
+ " $ANDROID_SERIAL serial number to connect to (see -s)\n"
+ " $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n");
+ // clang-format on
}
-static int usage() {
+int usage() {
help();
return 1;
}
@@ -230,17 +239,17 @@
#if defined(_WIN32)
// Implemented in sysdeps_win32.cpp.
-void stdin_raw_init(int fd);
-void stdin_raw_restore(int fd);
+void stdin_raw_init();
+void stdin_raw_restore();
#else
static termios g_saved_terminal_state;
-static void stdin_raw_init(int fd) {
- if (tcgetattr(fd, &g_saved_terminal_state)) return;
+static void stdin_raw_init() {
+ if (tcgetattr(STDIN_FILENO, &g_saved_terminal_state)) return;
termios tio;
- if (tcgetattr(fd, &tio)) return;
+ if (tcgetattr(STDIN_FILENO, &tio)) return;
cfmakeraw(&tio);
@@ -248,27 +257,70 @@
tio.c_cc[VTIME] = 0;
tio.c_cc[VMIN] = 1;
- tcsetattr(fd, TCSAFLUSH, &tio);
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
}
-static void stdin_raw_restore(int fd) {
- tcsetattr(fd, TCSAFLUSH, &g_saved_terminal_state);
+static void stdin_raw_restore() {
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_saved_terminal_state);
}
#endif
-static void read_and_dump(int fd) {
- while (fd >= 0) {
- D("read_and_dump(): pre adb_read(fd=%d)\n", fd);
- char buf[BUFSIZ];
- int len = adb_read(fd, buf, sizeof(buf));
- D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len);
- if (len <= 0) {
- break;
- }
+// Reads from |fd| and prints received data. If |use_shell_protocol| is true
+// this expects that incoming data will use the shell protocol, in which case
+// stdout/stderr are routed independently and the remote exit code will be
+// returned.
+// if |callback| is non-null, stdout/stderr output will be handled by it.
+int read_and_dump(int fd, bool use_shell_protocol = false,
+ StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK) {
+ int exit_code = 0;
+ if (fd < 0) return exit_code;
- fwrite(buf, 1, len, stdout);
- fflush(stdout);
+ std::unique_ptr<ShellProtocol> protocol;
+ int length = 0;
+
+ char raw_buffer[BUFSIZ];
+ char* buffer_ptr = raw_buffer;
+ if (use_shell_protocol) {
+ protocol.reset(new ShellProtocol(fd));
+ if (!protocol) {
+ LOG(ERROR) << "failed to allocate memory for ShellProtocol object";
+ return 1;
+ }
+ buffer_ptr = protocol->data();
}
+
+ while (true) {
+ if (use_shell_protocol) {
+ if (!protocol->Read()) {
+ break;
+ }
+ length = protocol->data_length();
+ switch (protocol->id()) {
+ case ShellProtocol::kIdStdout:
+ callback->OnStdout(buffer_ptr, length);
+ break;
+ case ShellProtocol::kIdStderr:
+ callback->OnStderr(buffer_ptr, length);
+ break;
+ case ShellProtocol::kIdExit:
+ exit_code = protocol->data()[0];
+ continue;
+ default:
+ continue;
+ }
+ length = protocol->data_length();
+ } else {
+ D("read_and_dump(): pre adb_read(fd=%d)", fd);
+ length = adb_read(fd, raw_buffer, sizeof(raw_buffer));
+ D("read_and_dump(): post adb_read(fd=%d): length=%d", fd, length);
+ if (length <= 0) {
+ break;
+ }
+ callback->OnStdout(buffer_ptr, length);
+ }
+ }
+
+ return callback->Done(exit_code);
}
static void read_status_line(int fd, char* buf, size_t count)
@@ -276,10 +328,7 @@
count--;
while (count > 0) {
int len = adb_read(fd, buf, count);
- if (len == 0) {
- break;
- } else if (len < 0) {
- if (errno == EINTR) continue;
+ if (len <= 0) {
break;
}
@@ -289,21 +338,9 @@
*buf = '\0';
}
-static void copy_to_file(int inFd, int outFd) {
- const size_t BUFSIZE = 32 * 1024;
- char* buf = (char*) malloc(BUFSIZE);
- if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
- int len;
- long total = 0;
-#ifdef _WIN32
- int old_stdin_mode = -1;
- int old_stdout_mode = -1;
-#endif
-
- D("copy_to_file(%d -> %d)\n", inFd, outFd);
-
+static void stdinout_raw_prologue(int inFd, int outFd, int& old_stdin_mode, int& old_stdout_mode) {
if (inFd == STDIN_FILENO) {
- stdin_raw_init(STDIN_FILENO);
+ stdin_raw_init();
#ifdef _WIN32
old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
if (old_stdin_mode == -1) {
@@ -320,36 +357,11 @@
}
}
#endif
+}
- while (true) {
- if (inFd == STDIN_FILENO) {
- len = unix_read(inFd, buf, BUFSIZE);
- } else {
- len = adb_read(inFd, buf, BUFSIZE);
- }
- if (len == 0) {
- D("copy_to_file() : read 0 bytes; exiting\n");
- break;
- }
- if (len < 0) {
- if (errno == EINTR) {
- D("copy_to_file() : EINTR, retrying\n");
- continue;
- }
- D("copy_to_file() : error %d\n", errno);
- break;
- }
- if (outFd == STDOUT_FILENO) {
- fwrite(buf, 1, len, stdout);
- fflush(stdout);
- } else {
- adb_write(outFd, buf, len);
- }
- total += len;
- }
-
+static void stdinout_raw_epilogue(int inFd, int outFd, int old_stdin_mode, int old_stdout_mode) {
if (inFd == STDIN_FILENO) {
- stdin_raw_restore(STDIN_FILENO);
+ stdin_raw_restore();
#ifdef _WIN32
if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
fatal_errno("could not restore stdin mode");
@@ -364,103 +376,415 @@
}
}
#endif
+}
- D("copy_to_file() finished after %lu bytes\n", total);
+static void copy_to_file(int inFd, int outFd) {
+ const size_t BUFSIZE = 32 * 1024;
+ char* buf = (char*) malloc(BUFSIZE);
+ if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
+ int len;
+ long total = 0;
+ int old_stdin_mode = -1;
+ int old_stdout_mode = -1;
+
+ D("copy_to_file(%d -> %d)", inFd, outFd);
+
+ stdinout_raw_prologue(inFd, outFd, old_stdin_mode, old_stdout_mode);
+
+ while (true) {
+ if (inFd == STDIN_FILENO) {
+ len = unix_read(inFd, buf, BUFSIZE);
+ } else {
+ len = adb_read(inFd, buf, BUFSIZE);
+ }
+ if (len == 0) {
+ D("copy_to_file() : read 0 bytes; exiting");
+ break;
+ }
+ if (len < 0) {
+ D("copy_to_file(): read failed: %s", strerror(errno));
+ break;
+ }
+ if (outFd == STDOUT_FILENO) {
+ fwrite(buf, 1, len, stdout);
+ fflush(stdout);
+ } else {
+ adb_write(outFd, buf, len);
+ }
+ total += len;
+ }
+
+ stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
+
+ D("copy_to_file() finished after %lu bytes", total);
free(buf);
}
-static void *stdin_read_thread(void *x)
-{
- int fd, fdi;
- unsigned char buf[1024];
- int r, n;
- int state = 0;
+static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
+ // Old devices can't handle window size changes.
+ if (shell == nullptr) return;
- int *fds = (int*) x;
- fd = fds[0];
- fdi = fds[1];
- free(fds);
+#if defined(_WIN32)
+ struct winsize {
+ unsigned short ws_row;
+ unsigned short ws_col;
+ unsigned short ws_xpixel;
+ unsigned short ws_ypixel;
+ };
+#endif
- for(;;) {
- /* fdi is really the client's stdin, so use read, not adb_read here */
- D("stdin_read_thread(): pre unix_read(fdi=%d,...)\n", fdi);
- r = unix_read(fdi, buf, 1024);
- D("stdin_read_thread(): post unix_read(fdi=%d,...)\n", fdi);
- if(r == 0) break;
- if(r < 0) {
- if(errno == EINTR) continue;
- break;
- }
- for(n = 0; n < r; n++){
- switch(buf[n]) {
- case '\n':
- state = 1;
- break;
- case '\r':
- state = 1;
- break;
- case '~':
- if(state == 1) state++;
- break;
- case '.':
- if(state == 2) {
- fprintf(stderr,"\n* disconnect *\n");
- stdin_raw_restore(fdi);
- exit(0);
- }
- default:
- state = 0;
- }
- }
- r = adb_write(fd, buf, r);
- if(r <= 0) {
- break;
- }
- }
- return 0;
+ winsize ws;
+
+#if defined(_WIN32)
+ // If stdout is redirected to a non-console, we won't be able to get the
+ // console size, but that makes sense.
+ const intptr_t intptr_handle = _get_osfhandle(STDOUT_FILENO);
+ if (intptr_handle == -1) return;
+
+ const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ memset(&info, 0, sizeof(info));
+ if (!GetConsoleScreenBufferInfo(handle, &info)) return;
+
+ memset(&ws, 0, sizeof(ws));
+ // The number of visible rows, excluding offscreen scroll-back rows which are in info.dwSize.Y.
+ ws.ws_row = info.srWindow.Bottom - info.srWindow.Top + 1;
+ // If the user has disabled "Wrap text output on resize", they can make the screen buffer wider
+ // than the window, in which case we should use the width of the buffer.
+ ws.ws_col = info.dwSize.X;
+#else
+ if (ioctl(fd, TIOCGWINSZ, &ws) == -1) return;
+#endif
+
+ // Send the new window size as human-readable ASCII for debugging convenience.
+ size_t l = snprintf(shell->data(), shell->data_capacity(), "%dx%d,%dx%d",
+ ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
+ shell->Write(ShellProtocol::kIdWindowSizeChange, l + 1);
}
-static int interactive_shell() {
- int fdi;
+// Used to pass multiple values to the stdin read thread.
+struct StdinReadArgs {
+ int stdin_fd, write_fd;
+ bool raw_stdin;
+ std::unique_ptr<ShellProtocol> protocol;
+ char escape_char;
+};
+
+// Loops to read from stdin and push the data to the given FD.
+// The argument should be a pointer to a StdinReadArgs object. This function
+// will take ownership of the object and delete it when finished.
+static void stdin_read_thread_loop(void* x) {
+ std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
+
+#if !defined(_WIN32)
+ // Mask SIGTTIN in case we're in a backgrounded process.
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGTTIN);
+ pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
+#endif
+
+#if defined(_WIN32)
+ // _get_interesting_input_record_uncached() causes unix_read_interruptible()
+ // to return -1 with errno == EINTR if the window size changes.
+#else
+ // Unblock SIGWINCH for this thread, so our read(2) below will be
+ // interrupted if the window size changes.
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGWINCH);
+ pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
+#endif
+
+ // Set up the initial window size.
+ send_window_size_change(args->stdin_fd, args->protocol);
+
+ char raw_buffer[BUFSIZ];
+ char* buffer_ptr = raw_buffer;
+ size_t buffer_size = sizeof(raw_buffer);
+ if (args->protocol != nullptr) {
+ buffer_ptr = args->protocol->data();
+ buffer_size = args->protocol->data_capacity();
+ }
+
+ // If we need to parse escape sequences, make life easy.
+ if (args->raw_stdin && args->escape_char != '\0') {
+ buffer_size = 1;
+ }
+
+ enum EscapeState { kMidFlow, kStartOfLine, kInEscape };
+ EscapeState state = kStartOfLine;
+
+ while (true) {
+ // Use unix_read_interruptible() rather than adb_read() for stdin.
+ D("stdin_read_thread_loop(): pre unix_read_interruptible(fdi=%d,...)", args->stdin_fd);
+ int r = unix_read_interruptible(args->stdin_fd, buffer_ptr,
+ buffer_size);
+ if (r == -1 && errno == EINTR) {
+ send_window_size_change(args->stdin_fd, args->protocol);
+ continue;
+ }
+ D("stdin_read_thread_loop(): post unix_read_interruptible(fdi=%d,...)", args->stdin_fd);
+ if (r <= 0) {
+ // Only devices using the shell protocol know to close subprocess
+ // stdin. For older devices we want to just leave the connection
+ // open, otherwise an unpredictable amount of return data could
+ // be lost due to the FD closing before all data has been received.
+ if (args->protocol) {
+ args->protocol->Write(ShellProtocol::kIdCloseStdin, 0);
+ }
+ break;
+ }
+ // If we made stdin raw, check input for escape sequences. In
+ // this situation signals like Ctrl+C are sent remotely rather than
+ // interpreted locally so this provides an emergency out if the remote
+ // process starts ignoring the signal. SSH also does this, see the
+ // "escape characters" section on the ssh man page for more info.
+ if (args->raw_stdin && args->escape_char != '\0') {
+ char ch = buffer_ptr[0];
+ if (ch == args->escape_char) {
+ if (state == kStartOfLine) {
+ state = kInEscape;
+ // Swallow the escape character.
+ continue;
+ } else {
+ state = kMidFlow;
+ }
+ } else {
+ if (state == kInEscape) {
+ if (ch == '.') {
+ fprintf(stderr,"\r\n[ disconnected ]\r\n");
+ stdin_raw_restore();
+ exit(0);
+ } else {
+ // We swallowed an escape character that wasn't part of
+ // a valid escape sequence; time to cough it up.
+ buffer_ptr[0] = args->escape_char;
+ buffer_ptr[1] = ch;
+ ++r;
+ }
+ }
+ state = (ch == '\n' || ch == '\r') ? kStartOfLine : kMidFlow;
+ }
+ }
+ if (args->protocol) {
+ if (!args->protocol->Write(ShellProtocol::kIdStdin, r)) {
+ break;
+ }
+ } else {
+ if (!WriteFdExactly(args->write_fd, buffer_ptr, r)) {
+ break;
+ }
+ }
+ }
+}
+
+// Returns a shell service string with the indicated arguments and command.
+static std::string ShellServiceString(bool use_shell_protocol,
+ const std::string& type_arg,
+ const std::string& command) {
+ std::vector<std::string> args;
+ if (use_shell_protocol) {
+ args.push_back(kShellServiceArgShellProtocol);
+
+ const char* terminal_type = getenv("TERM");
+ if (terminal_type != nullptr) {
+ args.push_back(std::string("TERM=") + terminal_type);
+ }
+ }
+ if (!type_arg.empty()) {
+ args.push_back(type_arg);
+ }
+
+ // Shell service string can look like: shell[,arg1,arg2,...]:[command].
+ return android::base::StringPrintf("shell%s%s:%s",
+ args.empty() ? "" : ",",
+ android::base::Join(args, ',').c_str(),
+ command.c_str());
+}
+
+// Connects to a shell on the device and read/writes data.
+//
+// Note: currently this function doesn't properly clean up resources; the
+// FD connected to the adb server is never closed and the stdin read thread
+// may never exit.
+//
+// On success returns the remote exit code if |use_shell_protocol| is true,
+// 0 otherwise. On failure returns 1.
+static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
+ char escape_char,
+ const std::string& command) {
+ std::string service_string = ShellServiceString(use_shell_protocol,
+ type_arg, command);
+
+ // Make local stdin raw if the device allocates a PTY, which happens if:
+ // 1. We are explicitly asking for a PTY shell, or
+ // 2. We don't specify shell type and are starting an interactive session.
+ bool raw_stdin = (type_arg == kShellServiceArgPty ||
+ (type_arg.empty() && command.empty()));
std::string error;
- int fd = adb_connect("shell:", &error);
+ int fd = adb_connect(service_string, &error);
if (fd < 0) {
fprintf(stderr,"error: %s\n", error.c_str());
return 1;
}
- fdi = 0; //dup(0);
- int* fds = reinterpret_cast<int*>(malloc(sizeof(int) * 2));
- if (fds == nullptr) {
- fprintf(stderr, "couldn't allocate fds array: %s\n", strerror(errno));
+ StdinReadArgs* args = new StdinReadArgs;
+ if (!args) {
+ LOG(ERROR) << "couldn't allocate StdinReadArgs object";
+ return 1;
+ }
+ args->stdin_fd = STDIN_FILENO;
+ args->write_fd = fd;
+ args->raw_stdin = raw_stdin;
+ args->escape_char = escape_char;
+ if (use_shell_protocol) {
+ args->protocol.reset(new ShellProtocol(args->write_fd));
+ }
+
+ if (raw_stdin) stdin_raw_init();
+
+#if !defined(_WIN32)
+ // Ensure our process is notified if the local window size changes.
+ // We use sigaction(2) to ensure that the SA_RESTART flag is not set,
+ // because the whole reason we're sending signals is to unblock the read(2)!
+ // That also means we don't need to do anything in the signal handler:
+ // the side effect of delivering the signal is all we need.
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = [](int) {};
+ sa.sa_flags = 0;
+ sigaction(SIGWINCH, &sa, nullptr);
+
+ // Now block SIGWINCH in this thread (the main thread) and all threads spawned
+ // from it. The stdin read thread will unblock this signal to ensure that it's
+ // the thread that receives the signal.
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGWINCH);
+ pthread_sigmask(SIG_BLOCK, &mask, nullptr);
+#endif
+
+ // TODO: combine read_and_dump with stdin_read_thread to make life simpler?
+ int exit_code = 1;
+ if (!adb_thread_create(stdin_read_thread_loop, args)) {
+ PLOG(ERROR) << "error starting stdin read thread";
+ delete args;
+ } else {
+ exit_code = read_and_dump(fd, use_shell_protocol);
+ }
+
+ // TODO: properly exit stdin_read_thread_loop and close |fd|.
+
+ // TODO: we should probably install signal handlers for this.
+ // TODO: can we use atexit? even on Windows?
+ if (raw_stdin) stdin_raw_restore();
+
+ return exit_code;
+}
+
+static int adb_shell(int argc, const char** argv) {
+ FeatureSet features;
+ std::string error;
+ if (!adb_get_feature_set(&features, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
return 1;
}
- fds[0] = fd;
- fds[1] = fdi;
+ enum PtyAllocationMode { kPtyAuto, kPtyNo, kPtyYes, kPtyDefinitely };
- stdin_raw_init(fdi);
+ // Defaults.
+ char escape_char = '~'; // -e
+ bool use_shell_protocol = CanUseFeature(features, kFeatureShell2); // -x
+ PtyAllocationMode tty = use_shell_protocol ? kPtyAuto : kPtyDefinitely; // -t/-T
- adb_thread_create(stdin_read_thread, fds);
- read_and_dump(fd);
- stdin_raw_restore(fdi);
- return 0;
-}
-
-
-static std::string format_host_command(const char* command, TransportType type, const char* serial) {
- if (serial) {
- return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+ // Parse shell-specific command-line options.
+ argv[0] = "adb shell"; // So getopt(3) error messages start "adb shell".
+ optind = 1; // argv[0] is always "shell", so set `optind` appropriately.
+ int opt;
+ while ((opt = getopt(argc, const_cast<char**>(argv), "+e:ntTx")) != -1) {
+ switch (opt) {
+ case 'e':
+ if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
+ fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
+ return 1;
+ }
+ escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
+ break;
+ case 'n':
+ close_stdin();
+ break;
+ case 'x':
+ // This option basically asks for historical behavior, so set options that
+ // correspond to the historical defaults. This is slightly weird in that -Tx
+ // is fine (because we'll undo the -T) but -xT isn't, but that does seem to
+ // be our least worst choice...
+ use_shell_protocol = false;
+ tty = kPtyDefinitely;
+ escape_char = '~';
+ break;
+ case 't':
+ // Like ssh, -t arguments are cumulative so that multiple -t's
+ // are needed to force a PTY.
+ tty = (tty >= kPtyYes) ? kPtyDefinitely : kPtyYes;
+ break;
+ case 'T':
+ tty = kPtyNo;
+ break;
+ default:
+ // getopt(3) already printed an error message for us.
+ return 1;
+ }
}
- const char* prefix = "host";
- if (type == kTransportUsb) {
- prefix = "host-usb";
- } else if (type == kTransportLocal) {
- prefix = "host-local";
+ bool is_interactive = (optind == argc);
+
+ std::string shell_type_arg = kShellServiceArgPty;
+ if (tty == kPtyNo) {
+ shell_type_arg = kShellServiceArgRaw;
+ } else if (tty == kPtyAuto) {
+ // If stdin isn't a TTY, default to a raw shell; this lets
+ // things like `adb shell < my_script.sh` work as expected.
+ // Non-interactive shells should also not have a pty.
+ if (!unix_isatty(STDIN_FILENO) || !is_interactive) {
+ shell_type_arg = kShellServiceArgRaw;
+ }
+ } else if (tty == kPtyYes) {
+ // A single -t arg isn't enough to override implicit -T.
+ if (!unix_isatty(STDIN_FILENO)) {
+ fprintf(stderr,
+ "Remote PTY will not be allocated because stdin is not a terminal.\n"
+ "Use multiple -t options to force remote PTY allocation.\n");
+ shell_type_arg = kShellServiceArgRaw;
+ }
}
- return android::base::StringPrintf("%s:%s", prefix, command);
+
+ D("shell -e 0x%x t=%d use_shell_protocol=%s shell_type_arg=%s\n",
+ escape_char, tty,
+ use_shell_protocol ? "true" : "false",
+ (shell_type_arg == kShellServiceArgPty) ? "pty" : "raw");
+
+ // Raw mode is only supported when talking to a new device *and* using the shell protocol.
+ if (!use_shell_protocol) {
+ if (shell_type_arg != kShellServiceArgPty) {
+ fprintf(stderr, "error: %s only supports allocating a pty\n",
+ !CanUseFeature(features, kFeatureShell2) ? "device" : "-x");
+ return 1;
+ } else {
+ // If we're not using the shell protocol, the type argument must be empty.
+ shell_type_arg = "";
+ }
+ }
+
+ std::string command;
+ if (optind < argc) {
+ // We don't escape here, just like ssh(1). http://b/20564385.
+ command = android::base::Join(std::vector<const char*>(argv + optind, argv + argc), ' ');
+ }
+
+ return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
}
static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
@@ -490,6 +814,7 @@
std::string error;
adb_status(fd, &error);
fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+ adb_close(fd);
return -1;
}
sz -= xfer;
@@ -505,6 +830,7 @@
if (!adb_status(fd, &error)) {
fprintf(stderr,"* error response '%s' *\n", error.c_str());
+ adb_close(fd);
return -1;
}
@@ -534,47 +860,46 @@
* we hang up.
*/
static int adb_sideload_host(const char* fn) {
- unsigned sz;
- size_t xfer = 0;
- int status;
- int last_percent = -1;
- int opt = SIDELOAD_HOST_BLOCK_SIZE;
+ fprintf(stderr, "loading: '%s'...\n", fn);
- printf("loading: '%s'", fn);
- fflush(stdout);
- uint8_t* data = reinterpret_cast<uint8_t*>(load_file(fn, &sz));
- if (data == 0) {
- printf("\n");
- fprintf(stderr, "* cannot read '%s' *\n", fn);
+ std::string content;
+ if (!android::base::ReadFileToString(fn, &content)) {
+ fprintf(stderr, "failed: %s\n", strerror(errno));
return -1;
}
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
+ unsigned sz = content.size();
+
+ fprintf(stderr, "connecting...\n");
std::string service =
android::base::StringPrintf("sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE);
std::string error;
- int fd = adb_connect(service, &error);
+ unique_fd fd(adb_connect(service, &error));
if (fd < 0) {
// Try falling back to the older sideload method. Maybe this
// is an older device that doesn't support sideload-host.
- printf("\n");
- status = adb_download_buffer("sideload", fn, data, sz, true);
- goto done;
+ fprintf(stderr, "falling back to older sideload method...\n");
+ return adb_download_buffer("sideload", fn, data, sz, true);
}
- opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt));
+ int opt = SIDELOAD_HOST_BLOCK_SIZE;
+ adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+ size_t xfer = 0;
+ int last_percent = -1;
while (true) {
char buf[9];
if (!ReadFdExactly(fd, buf, 8)) {
fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
- status = -1;
- goto done;
+ return -1;
}
buf[8] = '\0';
if (strcmp("DONEDONE", buf) == 0) {
- status = 0;
- break;
+ printf("\rTotal xfer: %.2fx%*s\n",
+ (double)xfer / (sz ? sz : 1), (int)strlen(fn)+10, "");
+ return 0;
}
int block = strtol(buf, NULL, 10);
@@ -582,21 +907,19 @@
size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
if (offset >= sz) {
fprintf(stderr, "* attempt to read block %d past end\n", block);
- status = -1;
- goto done;
+ return -1;
}
- uint8_t* start = data + offset;
+ const uint8_t* start = data + offset;
size_t offset_end = offset + SIDELOAD_HOST_BLOCK_SIZE;
size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
if (offset_end > sz) {
to_write = sz - offset;
}
- if(!WriteFdExactly(fd, start, to_write)) {
+ if (!WriteFdExactly(fd, start, to_write)) {
adb_status(fd, &error);
fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
- status = -1;
- goto done;
+ return -1;
}
xfer += to_write;
@@ -613,13 +936,6 @@
last_percent = percent;
}
}
-
- printf("\rTotal xfer: %.2fx%*s\n", (double)xfer / (sz ? sz : 1), (int)strlen(fn)+10, "");
-
- done:
- if (fd >= 0) adb_close(fd);
- free(data);
- return status;
}
/**
@@ -692,49 +1008,135 @@
}
static bool wait_for_device(const char* service, TransportType t, const char* serial) {
- // Was the caller vague about what they'd like us to wait for?
- // If so, check they weren't more specific in their choice of transport type.
- if (strcmp(service, "wait-for-device") == 0) {
- if (t == kTransportUsb) {
- service = "wait-for-usb";
- } else if (t == kTransportLocal) {
- service = "wait-for-local";
- } else {
- service = "wait-for-any";
- }
+ std::vector<std::string> components = android::base::Split(service, "-");
+ if (components.size() < 3 || components.size() > 4) {
+ fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
+ return false;
}
- std::string cmd = format_host_command(service, t, serial);
+ // Was the caller vague about what they'd like us to wait for?
+ // If so, check they weren't more specific in their choice of transport type.
+ if (components.size() == 3) {
+ auto it = components.begin() + 2;
+ if (t == kTransportUsb) {
+ components.insert(it, "usb");
+ } else if (t == kTransportLocal) {
+ components.insert(it, "local");
+ } else {
+ components.insert(it, "any");
+ }
+ } else if (components[2] != "any" && components[2] != "local" && components[2] != "usb") {
+ fprintf(stderr, "adb: unknown type %s; expected 'any', 'local', or 'usb'\n",
+ components[2].c_str());
+ return false;
+ }
+
+ if (components[3] != "any" && components[3] != "bootloader" && components[3] != "device" &&
+ components[3] != "recovery" && components[3] != "sideload") {
+ fprintf(stderr,
+ "adb: unknown state %s; "
+ "expected 'any', 'bootloader', 'device', 'recovery', or 'sideload'\n",
+ components[3].c_str());
+ return false;
+ }
+
+ std::string cmd = format_host_command(android::base::Join(components, "-").c_str(), t, serial);
return adb_command(cmd);
}
-static int send_shell_command(TransportType transport_type, const char* serial,
- const std::string& command) {
- int fd;
- while (true) {
- std::string error;
- fd = adb_connect(command, &error);
- if (fd >= 0) {
- break;
- }
- fprintf(stderr,"- waiting for device -\n");
- adb_sleep_ms(1000);
- wait_for_device("wait-for-device", transport_type, serial);
+static bool adb_root(const char* command) {
+ std::string error;
+
+ unique_fd fd(adb_connect(android::base::StringPrintf("%s:", command), &error));
+ if (fd < 0) {
+ fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
+ return false;
}
- read_and_dump(fd);
- int rc = adb_close(fd);
- if (rc) {
- perror("close");
+ // Figure out whether we actually did anything.
+ char buf[256];
+ char* cur = buf;
+ ssize_t bytes_left = sizeof(buf);
+ while (bytes_left > 0) {
+ ssize_t bytes_read = adb_read(fd, cur, bytes_left);
+ if (bytes_read == 0) {
+ break;
+ } else if (bytes_read < 0) {
+ fprintf(stderr, "adb: error while reading for %s: %s\n", command, strerror(errno));
+ return false;
+ }
+ cur += bytes_read;
+ bytes_left -= bytes_read;
}
- return rc;
+
+ if (bytes_left == 0) {
+ fprintf(stderr, "adb: unexpected output length for %s\n", command);
+ return false;
+ }
+
+ fflush(stdout);
+ WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
+ if (cur != buf && strstr(buf, "restarting") == nullptr) {
+ return true;
+ }
+
+ // Give adbd some time to kill itself and come back up.
+ // We can't use wait-for-device because devices (e.g. adb over network) might not come back.
+ std::this_thread::sleep_for(3s);
+ return true;
+}
+
+int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
+ bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+ int fd;
+ bool use_shell_protocol = false;
+
+ while (true) {
+ bool attempt_connection = true;
+
+ // Use shell protocol if it's supported and the caller doesn't explicitly
+ // disable it.
+ if (!disable_shell_protocol) {
+ FeatureSet features;
+ std::string error;
+ if (adb_get_feature_set(&features, &error)) {
+ use_shell_protocol = CanUseFeature(features, kFeatureShell2);
+ } else {
+ // Device was unreachable.
+ attempt_connection = false;
+ }
+ }
+
+ if (attempt_connection) {
+ std::string error;
+ std::string service_string = ShellServiceString(use_shell_protocol, "", command);
+
+ fd = adb_connect(service_string, &error);
+ if (fd >= 0) {
+ break;
+ }
+ }
+
+ fprintf(stderr, "- waiting for device -\n");
+ if (!wait_for_device("wait-for-device", transport_type, serial)) {
+ return 1;
+ }
+ }
+
+ int exit_code = read_and_dump(fd, use_shell_protocol, callback);
+
+ if (adb_close(fd) < 0) {
+ PLOG(ERROR) << "failure closing FD " << fd;
+ }
+
+ return exit_code;
}
static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
char* log_tags = getenv("ANDROID_LOG_TAGS");
std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
- std::string cmd = "shell:export ANDROID_LOG_TAGS=\"" + quoted + "\"; exec logcat";
+ std::string cmd = "export ANDROID_LOG_TAGS=\"" + quoted + "\"; exec logcat";
if (!strcmp(argv[0], "longcat")) {
cmd += " -v long";
@@ -746,18 +1148,42 @@
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(transport, serial, cmd);
+ // No need for shell protocol with logcat, always disable for simplicity.
+ return send_shell_command(transport, serial, cmd, true);
+}
+
+static void write_zeros(int bytes, int fd) {
+ int old_stdin_mode = -1;
+ int old_stdout_mode = -1;
+ char* buf = (char*) calloc(1, bytes);
+ if (buf == nullptr) fatal("couldn't allocate buffer for write_zeros");
+
+ D("write_zeros(%d) -> %d", bytes, fd);
+
+ stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+
+ if (fd == STDOUT_FILENO) {
+ fwrite(buf, 1, bytes, stdout);
+ fflush(stdout);
+ } else {
+ adb_write(fd, buf, bytes);
+ }
+
+ stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+
+ D("write_zeros() finished");
+ free(buf);
}
static int backup(int argc, const char** argv) {
- const char* filename = "./backup.ab";
+ const char* filename = "backup.ab";
/* find, extract, and use any -f argument */
for (int i = 1; i < argc; i++) {
if (!strcmp("-f", argv[i])) {
if (i == argc-1) {
- fprintf(stderr, "adb: -f passed with no filename\n");
- return usage();
+ fprintf(stderr, "adb: backup -f passed with no filename.\n");
+ return EXIT_FAILURE;
}
filename = argv[i+1];
for (int j = i+2; j <= argc; ) {
@@ -768,15 +1194,18 @@
}
}
- /* bare "adb backup" or "adb backup -f filename" are not valid invocations */
- if (argc < 2) return usage();
+ // Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
+ // a list of packages is required.
+ if (argc < 2) {
+ fprintf(stderr, "adb: backup either needs a list of packages or -all/-shared.\n");
+ return EXIT_FAILURE;
+ }
adb_unlink(filename);
- mkdirs(filename);
int outFd = adb_creat(filename, 0640);
if (outFd < 0) {
- fprintf(stderr, "adb: unable to open file %s\n", filename);
- return -1;
+ fprintf(stderr, "adb: backup unable to create file '%s': %s\n", filename, strerror(errno));
+ return EXIT_FAILURE;
}
std::string cmd = "backup:";
@@ -786,21 +1215,23 @@
cmd += " " + escape_arg(*argv++);
}
- D("backup. filename=%s cmd=%s\n", filename, cmd.c_str());
+ D("backup. filename=%s cmd=%s", filename, cmd.c_str());
std::string error;
int fd = adb_connect(cmd, &error);
if (fd < 0) {
fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str());
adb_close(outFd);
- return -1;
+ return EXIT_FAILURE;
}
- printf("Now unlock your device and confirm the backup operation.\n");
+ printf("Now unlock your device and confirm the backup operation...\n");
+ fflush(stdout);
+
copy_to_file(fd, outFd);
adb_close(fd);
adb_close(outFd);
- return 0;
+ return EXIT_SUCCESS;
}
static int restore(int argc, const char** argv) {
@@ -824,6 +1255,12 @@
printf("Now unlock your device and confirm the restore operation.\n");
copy_to_file(tarFd, fd);
+ // Provide an in-band EOD marker in case the archive file is malformed
+ write_zeros(512*2, fd);
+
+ // Wait until the other side finishes, or it'll get sent SIGHUP.
+ copy_to_file(fd, STDOUT_FILENO);
+
adb_close(fd);
adb_close(tarFd);
return 0;
@@ -850,12 +1287,13 @@
return hint;
}
- // If there are any slashes in it, assume it's a relative path;
+ // If any of the OS_PATH_SEPARATORS is found, assume it's a relative path;
// make it absolute.
- if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) {
+ // NOLINT: Do not complain if OS_PATH_SEPARATORS has only one character.
+ if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) { // NOLINT
std::string cwd;
if (!getcwd(&cwd)) {
- fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
+ perror("adb: getcwd failed");
return "";
}
return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
@@ -888,32 +1326,35 @@
return path;
}
-static void parse_push_pull_args(const char **arg, int narg, char const **path1,
- char const **path2, int *show_progress,
- int *copy_attrs) {
- *show_progress = 0;
- *copy_attrs = 0;
+static void parse_push_pull_args(const char** arg, int narg,
+ std::vector<const char*>* srcs,
+ const char** dst, bool* copy_attrs) {
+ *copy_attrs = false;
+ srcs->clear();
+ bool ignore_flags = false;
while (narg > 0) {
- if (!strcmp(*arg, "-p")) {
- *show_progress = 1;
- } else if (!strcmp(*arg, "-a")) {
- *copy_attrs = 1;
+ if (ignore_flags || *arg[0] != '-') {
+ srcs->push_back(*arg);
} else {
- break;
+ if (!strcmp(*arg, "-p")) {
+ // Silently ignore for backwards compatibility.
+ } else if (!strcmp(*arg, "-a")) {
+ *copy_attrs = true;
+ } else if (!strcmp(*arg, "--")) {
+ ignore_flags = true;
+ } else {
+ fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+ exit(1);
+ }
}
++arg;
--narg;
}
- if (narg > 0) {
- *path1 = *arg;
- ++arg;
- --narg;
- }
-
- if (narg > 0) {
- *path2 = *arg;
+ if (srcs->size() > 1) {
+ *dst = srcs->back();
+ srcs->pop_back();
}
}
@@ -940,7 +1381,29 @@
return 0;
}
-int adb_commandline(int argc, const char **argv) {
+// Disallow stdin, stdout, and stderr.
+static bool _is_valid_ack_reply_fd(const int ack_reply_fd) {
+#ifdef _WIN32
+ const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+ return (GetStdHandle(STD_INPUT_HANDLE) != ack_reply_handle) &&
+ (GetStdHandle(STD_OUTPUT_HANDLE) != ack_reply_handle) &&
+ (GetStdHandle(STD_ERROR_HANDLE) != ack_reply_handle);
+#else
+ return ack_reply_fd > 2;
+#endif
+}
+
+static bool _use_legacy_install() {
+ FeatureSet features;
+ std::string error;
+ if (!adb_get_feature_set(&features, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return true;
+ }
+ return !CanUseFeature(features, kFeatureCmd);
+}
+
+int adb_commandline(int argc, const char** argv) {
int no_daemon = 0;
int is_daemon = 0;
int is_server = 0;
@@ -948,6 +1411,11 @@
TransportType transport_type = kTransportAny;
int ack_reply_fd = -1;
+#if !defined(_WIN32)
+ // We'd rather have EPIPE than SIGPIPE.
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
// If defined, this should be an absolute path to
// the directory containing all of the various system images
// for a particular product. If not defined, and the adb
@@ -959,22 +1427,13 @@
}
// TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
- const char* serial = getenv("ANDROID_SERIAL");
+ const char* server_host_str = nullptr;
+ const char* server_port_str = nullptr;
+ const char* server_socket_str = nullptr;
- /* Validate and assign the server port */
- const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
- int server_port = DEFAULT_ADB_PORT;
- if (server_port_str && strlen(server_port_str) > 0) {
- server_port = strtol(server_port_str, nullptr, 0);
- if (server_port <= 0 || server_port > 65535) {
- fprintf(stderr,
- "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65536. Got \"%s\"\n",
- server_port_str);
- return usage();
- }
- }
+ // We need to check for -d and -e before we look at $ANDROID_SERIAL.
+ const char* serial = nullptr;
- /* modifiers and flags */
while (argc > 0) {
if (!strcmp(argv[0],"server")) {
is_server = 1;
@@ -989,14 +1448,7 @@
argc--;
argv++;
ack_reply_fd = strtol(reply_fd_str, nullptr, 10);
-#ifdef _WIN32
- const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
- if ((GetStdHandle(STD_INPUT_HANDLE) == ack_reply_handle) ||
- (GetStdHandle(STD_OUTPUT_HANDLE) == ack_reply_handle) ||
- (GetStdHandle(STD_ERROR_HANDLE) == ack_reply_handle)) {
-#else
- if (ack_reply_fd <= 2) { // Disallow stdin, stdout, and stderr.
-#endif
+ if (!_is_valid_ack_reply_fd(ack_reply_fd)) {
fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
return usage();
}
@@ -1031,17 +1483,14 @@
} else if (!strcmp(argv[0],"-a")) {
gListenAll = 1;
} else if (!strncmp(argv[0], "-H", 2)) {
- const char *hostname = NULL;
if (argv[0][2] == '\0') {
if (argc < 2) return usage();
- hostname = argv[1];
+ server_host_str = argv[1];
argc--;
argv++;
} else {
- hostname = argv[0] + 2;
+ server_host_str = argv[0] + 2;
}
- adb_set_tcp_name(hostname);
-
} else if (!strncmp(argv[0], "-P", 2)) {
if (argv[0][2] == '\0') {
if (argc < 2) return usage();
@@ -1051,29 +1500,67 @@
} else {
server_port_str = argv[0] + 2;
}
- if (strlen(server_port_str) > 0) {
- server_port = (int) strtol(server_port_str, NULL, 0);
- if (server_port <= 0 || server_port > 65535) {
- fprintf(stderr,
- "adb: port number must be a positive number less than 65536. Got \"%s\"\n",
- server_port_str);
- return usage();
- }
- } else {
- fprintf(stderr,
- "adb: port number must be a positive number less than 65536. Got empty string.\n");
- return usage();
- }
+ } else if (!strcmp(argv[0], "-L")) {
+ if (argc < 2) return usage();
+ server_socket_str = argv[1];
+ argc--;
+ argv++;
} else {
- /* out of recognized modifiers and flags */
+ /* out of recognized modifiers and flags */
break;
}
argc--;
argv++;
}
+ if ((server_host_str || server_port_str) && server_socket_str) {
+ fprintf(stderr, "adb: -L is incompatible with -H or -P\n");
+ exit(1);
+ }
+
+ // If -L, -H, or -P are specified, ignore environment variables.
+ // Otherwise, prefer ADB_SERVER_SOCKET over ANDROID_ADB_SERVER_ADDRESS/PORT.
+ if (!server_host_str && !server_port_str && !server_socket_str) {
+ server_socket_str = getenv("ADB_SERVER_SOCKET");
+ }
+
+ if (!server_socket_str) {
+ // tcp:1234 and tcp:localhost:1234 are different with -a, so don't default to localhost
+ server_host_str = server_host_str ? server_host_str : getenv("ANDROID_ADB_SERVER_ADDRESS");
+
+ int server_port = DEFAULT_ADB_PORT;
+ server_port_str = server_port_str ? server_port_str : getenv("ANDROID_ADB_SERVER_PORT");
+ if (server_port_str && strlen(server_port_str) > 0) {
+ if (!android::base::ParseInt(server_port_str, &server_port, 1, 65535)) {
+ fprintf(stderr,
+ "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive"
+ " number less than 65535. Got \"%s\"\n",
+ server_port_str);
+ exit(1);
+ }
+ }
+
+ int rc;
+ char* temp;
+ if (server_host_str) {
+ rc = asprintf(&temp, "tcp:%s:%d", server_host_str, server_port);
+ } else {
+ rc = asprintf(&temp, "tcp:%d", server_port);
+ }
+ if (rc < 0) {
+ fatal("failed to allocate server socket specification");
+ }
+ server_socket_str = temp;
+ }
+
+ adb_set_socket_spec(server_socket_str);
+
+ // If none of -d, -e, or -s were specified, try $ANDROID_SERIAL.
+ if (transport_type == kTransportAny && serial == nullptr) {
+ serial = getenv("ANDROID_SERIAL");
+ }
+
adb_set_transport(transport_type, serial);
- adb_set_tcp_specifics(server_port);
if (is_server) {
if (no_daemon || is_daemon) {
@@ -1081,9 +1568,9 @@
fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
return usage();
}
- r = adb_main(is_daemon, server_port, ack_reply_fd);
+ r = adb_server_main(is_daemon, server_socket_str, ack_reply_fd);
} else {
- r = launch_server(server_port);
+ r = launch_server(server_socket_str);
}
if (r) {
fprintf(stderr,"* could not start server *\n");
@@ -1152,60 +1639,17 @@
else if (!strcmp(argv[0], "emu")) {
return adb_send_emulator_command(argc, argv, serial);
}
- else if (!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) {
- char h = (argv[0][0] == 'h');
-
- if (h) {
- printf("\x1b[41;33m");
- fflush(stdout);
- }
-
- if (argc < 2) {
- D("starting interactive shell\n");
- r = interactive_shell();
- if (h) {
- printf("\x1b[0m");
- fflush(stdout);
- }
- return r;
- }
-
- std::string cmd = "shell:";
- --argc;
- ++argv;
- while (argc-- > 0) {
- // We don't escape here, just like ssh(1). http://b/20564385.
- cmd += *argv++;
- if (*argv) cmd += " ";
- }
-
- while (true) {
- D("interactive shell loop. cmd=%s\n", cmd.c_str());
- std::string error;
- int fd = adb_connect(cmd, &error);
- int r;
- if (fd >= 0) {
- D("about to read_and_dump(fd=%d)\n", fd);
- read_and_dump(fd);
- D("read_and_dump() done.\n");
- adb_close(fd);
- r = 0;
- } else {
- fprintf(stderr,"error: %s\n", error.c_str());
- r = -1;
- }
-
- if (h) {
- printf("\x1b[0m");
- fflush(stdout);
- }
- D("interactive shell loop. return r=%d\n", r);
- return r;
- }
+ else if (!strcmp(argv[0], "shell")) {
+ return adb_shell(argc, argv);
}
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
+ if (argc < 2) {
+ fprintf(stderr, "Usage: adb %s command\n", argv[0]);
+ return 1;
+ }
+
std::string cmd = "exec:";
cmd += argv[1];
argc -= 2;
@@ -1246,6 +1690,7 @@
} else {
// Successfully connected, kill command sent, okay status came back.
// Server should exit() in a moment, if not already.
+ ReadOrderlyShutdown(fd);
adb_close(fd);
return 0;
}
@@ -1265,8 +1710,6 @@
!strcmp(argv[0], "reboot") ||
!strcmp(argv[0], "reboot-bootloader") ||
!strcmp(argv[0], "usb") ||
- !strcmp(argv[0], "root") ||
- !strcmp(argv[0], "unroot") ||
!strcmp(argv[0], "disable-verity") ||
!strcmp(argv[0], "enable-verity")) {
std::string command;
@@ -1278,12 +1721,12 @@
command = android::base::StringPrintf("%s:", argv[0]);
}
return adb_connect_command(command);
- }
- else if (!strcmp(argv[0], "bugreport")) {
- if (argc != 1) return usage();
- return send_shell_command(transport_type, serial, "shell:bugreport");
- }
- else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
+ } else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
+ return adb_root(argv[0]) ? 0 : 1;
+ } else if (!strcmp(argv[0], "bugreport")) {
+ Bugreport bugreport;
+ return bugreport.DoIt(transport_type, serial, argc, argv);
+ } else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
bool reverse = !strcmp(argv[0], "reverse");
++argv;
--argc;
@@ -1305,7 +1748,7 @@
}
}
- std::string cmd;
+ std::string cmd, error;
if (strcmp(argv[0], "--list") == 0) {
if (argc != 1) return usage();
return adb_query_command(host_prefix + ":list-forward");
@@ -1319,48 +1762,66 @@
} else if (strcmp(argv[0], "--no-rebind") == 0) {
// forward --no-rebind <local> <remote>
if (argc != 3) return usage();
- cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
+ if (forward_targets_are_valid(argv[1], argv[2], &error)) {
+ cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
+ }
} else {
// forward <local> <remote>
if (argc != 2) return usage();
- cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
+ if (forward_targets_are_valid(argv[0], argv[1], &error)) {
+ cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
+ }
}
- return adb_command(cmd) ? 0 : 1;
+ if (!error.empty()) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+
+ int fd = adb_connect(cmd, &error);
+ if (fd < 0 || !adb_status(fd, &error)) {
+ adb_close(fd);
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+
+ // Server or device may optionally return a resolved TCP port number.
+ std::string resolved_port;
+ if (ReadProtocolString(fd, &resolved_port, &error) && !resolved_port.empty()) {
+ printf("%s\n", resolved_port.c_str());
+ }
+
+ ReadOrderlyShutdown(fd);
+ return 0;
}
/* do_sync_*() commands */
else if (!strcmp(argv[0], "ls")) {
if (argc != 2) return usage();
- return do_sync_ls(argv[1]);
+ return do_sync_ls(argv[1]) ? 0 : 1;
}
else if (!strcmp(argv[0], "push")) {
- int show_progress = 0;
- int copy_attrs = 0; // unused
- const char* lpath = NULL, *rpath = NULL;
+ bool copy_attrs = false;
+ std::vector<const char*> srcs;
+ const char* dst = nullptr;
- parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, ©_attrs);
-
- if ((lpath != NULL) && (rpath != NULL)) {
- return do_sync_push(lpath, rpath, show_progress);
- }
-
- return usage();
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
+ if (srcs.empty() || !dst) return usage();
+ return do_sync_push(srcs, dst) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
- int show_progress = 0;
- int copy_attrs = 0;
- const char* rpath = NULL, *lpath = ".";
+ bool copy_attrs = false;
+ std::vector<const char*> srcs;
+ const char* dst = ".";
- parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, ©_attrs);
-
- if (rpath != NULL) {
- return do_sync_pull(rpath, lpath, show_progress, copy_attrs);
- }
-
- return usage();
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
+ if (srcs.empty()) return usage();
+ return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
}
else if (!strcmp(argv[0], "install")) {
if (argc < 2) return usage();
+ if (_use_legacy_install()) {
+ return install_app_legacy(transport_type, serial, argc, argv);
+ }
return install_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
@@ -1369,6 +1830,9 @@
}
else if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) return usage();
+ if (_use_legacy_install()) {
+ return uninstall_app_legacy(transport_type, serial, argc, argv);
+ }
return uninstall_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "sync")) {
@@ -1401,20 +1865,20 @@
std::string vendor_src_path = product_file("vendor");
std::string oem_src_path = product_file("oem");
- int rc = 0;
- if (rc == 0 && (src.empty() || src == "system")) {
- rc = do_sync_sync(system_src_path, "/system", list_only);
+ bool okay = true;
+ if (okay && (src.empty() || src == "system")) {
+ okay = do_sync_sync(system_src_path, "/system", list_only);
}
- if (rc == 0 && (src.empty() || src == "vendor") && directory_exists(vendor_src_path)) {
- rc = do_sync_sync(vendor_src_path, "/vendor", list_only);
+ if (okay && (src.empty() || src == "vendor") && directory_exists(vendor_src_path)) {
+ okay = do_sync_sync(vendor_src_path, "/vendor", list_only);
}
- if (rc == 0 && (src.empty() || src == "oem") && directory_exists(oem_src_path)) {
- rc = do_sync_sync(oem_src_path, "/oem", list_only);
+ if (okay && (src.empty() || src == "oem") && directory_exists(oem_src_path)) {
+ okay = do_sync_sync(oem_src_path, "/oem", list_only);
}
- if (rc == 0 && (src.empty() || src == "data")) {
- rc = do_sync_sync(data_src_path, "/data", list_only);
+ if (okay && (src.empty() || src == "data")) {
+ okay = do_sync_sync(data_src_path, "/data", list_only);
}
- return rc;
+ return okay ? 0 : 1;
}
/* passthrough commands */
else if (!strcmp(argv[0],"get-state") ||
@@ -1446,11 +1910,21 @@
}
else if (!strcmp(argv[0], "keygen")) {
if (argc < 2) return usage();
+ // Always print key generation information for keygen command.
+ adb_trace_enable(AUTH);
return adb_auth_keygen(argv[1]);
}
else if (!strcmp(argv[0], "jdwp")) {
return adb_connect_command("jdwp");
}
+ else if (!strcmp(argv[0], "track-jdwp")) {
+ return adb_connect_command("track-jdwp");
+ }
+ else if (!strcmp(argv[0], "track-devices")) {
+ return adb_connect_command("host:track-devices");
+ }
+
+
/* "adb /?" is a common idiom under Windows */
else if (!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
help();
@@ -1461,116 +1935,127 @@
return 0;
}
else if (!strcmp(argv[0], "features")) {
- return adb_query_command("host:features");
+ // Only list the features common to both the adb client and the device.
+ FeatureSet features;
+ std::string error;
+ if (!adb_get_feature_set(&features, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+
+ for (const std::string& name : features) {
+ if (CanUseFeature(features, name)) {
+ printf("%s\n", name.c_str());
+ }
+ }
+ return 0;
+ } else if (!strcmp(argv[0], "reconnect")) {
+ if (argc == 1) {
+ return adb_query_command("host:reconnect");
+ } else if (argc == 2) {
+ if (!strcmp(argv[1], "device")) {
+ std::string err;
+ adb_connect("reconnect", &err);
+ return 0;
+ } else if (!strcmp(argv[1], "offline")) {
+ std::string err;
+ return adb_query_command("host:reconnect-offline");
+ } else {
+ return usage();
+ }
+ }
}
usage();
return 1;
}
-static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
- std::string cmd = "shell:pm";
-
+static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
+ // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
+ std::string cmd = "cmd package";
while (argc-- > 0) {
+ // deny the '-k' option until the remaining data/cache can be removed with adb/UI
+ if (strcmp(*argv, "-k") == 0) {
+ printf(
+ "The -k option uninstalls the application while retaining the data/cache.\n"
+ "At the moment, there is no way to remove the remaining data.\n"
+ "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+ "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n");
+ return EXIT_FAILURE;
+ }
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(transport, serial, cmd);
-}
-
-static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
- /* if the user choose the -k option, we refuse to do it until devices are
- out with the option to uninstall the remaining data somehow (adb/ui) */
- if (argc == 3 && strcmp(argv[1], "-k") == 0)
- {
- printf(
- "The -k option uninstalls the application while retaining the data/cache.\n"
- "At the moment, there is no way to remove the remaining data.\n"
- "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
- "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]);
- return -1;
- }
-
- /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
- return pm_command(transport, serial, argc, argv);
-}
-
-static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
- std::string cmd = "shell:rm -f " + escape_arg(filename);
- return send_shell_command(transport, serial, cmd);
+ return send_shell_command(transport, serial, cmd, false);
}
static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
- static const char *const DATA_DEST = "/data/local/tmp/%s";
- static const char *const SD_DEST = "/sdcard/tmp/%s";
- const char* where = DATA_DEST;
- int i;
+ // The last argument must be the APK file
+ const char* file = argv[argc - 1];
+ if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
+ fprintf(stderr, "Filename doesn't end .apk: %s\n", file);
+ return EXIT_FAILURE;
+ }
+
struct stat sb;
-
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-s")) {
- where = SD_DEST;
- }
+ if (stat(file, &sb) == -1) {
+ fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+ return 1;
}
- // Find last APK argument.
- // All other arguments passed through verbatim.
- int last_apk = -1;
- for (i = argc - 1; i >= 0; i--) {
- const char* file = argv[i];
- const char* dot = strrchr(file, '.');
- if (dot && !strcasecmp(dot, ".apk")) {
- if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
- fprintf(stderr, "Invalid APK file: %s\n", file);
- return -1;
- }
-
- last_apk = i;
- break;
- }
+ int localFd = adb_open(file, O_RDONLY);
+ if (localFd < 0) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ return 1;
}
- if (last_apk == -1) {
- fprintf(stderr, "Missing APK file\n");
- return -1;
+ std::string error;
+ std::string cmd = "exec:cmd package";
+
+ // don't copy the APK name, but, copy the rest of the arguments as-is
+ while (argc-- > 1) {
+ cmd += " " + escape_arg(std::string(*argv++));
}
- const char* apk_file = argv[last_apk];
- std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str());
- int err = do_sync_push(apk_file, apk_dest.c_str(), 0 /* no show progress */);
- if (err) {
- goto cleanup_apk;
- } else {
- argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
+ // add size parameter [required for streaming installs]
+ // do last to override any user specified value
+ cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
+
+ int remoteFd = adb_connect(cmd, &error);
+ if (remoteFd < 0) {
+ fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+ adb_close(localFd);
+ return 1;
}
- err = pm_command(transport, serial, argc, argv);
+ char buf[BUFSIZ];
+ copy_to_file(localFd, remoteFd);
+ read_status_line(remoteFd, buf, sizeof(buf));
-cleanup_apk:
- delete_file(transport, serial, apk_dest);
- return err;
+ adb_close(localFd);
+ adb_close(remoteFd);
+
+ if (strncmp("Success", buf, 7)) {
+ fprintf(stderr, "Failed to install %s: %s", file, buf);
+ return 1;
+ }
+ fputs(buf, stderr);
+ return 0;
}
static int install_multiple_app(TransportType transport, const char* serial, int argc,
const char** argv)
{
- int i;
- struct stat sb;
- uint64_t total_size = 0;
-
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
int first_apk = -1;
- for (i = argc - 1; i >= 0; i--) {
+ uint64_t total_size = 0;
+ for (int i = argc - 1; i >= 0; i--) {
const char* file = argv[i];
- const char* dot = strrchr(file, '.');
- if (dot && !strcasecmp(dot, ".apk")) {
- if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
- fprintf(stderr, "Invalid APK file: %s\n", file);
- return -1;
- }
- total_size += sb.st_size;
+ if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+ struct stat sb;
+ if (stat(file, &sb) != -1) total_size += sb.st_size;
first_apk = i;
} else {
break;
@@ -1578,12 +2063,19 @@
}
if (first_apk == -1) {
- fprintf(stderr, "Missing APK file\n");
+ fprintf(stderr, "No APK file on command line\n");
return 1;
}
- std::string cmd = android::base::StringPrintf("exec:pm install-create -S %" PRIu64, total_size);
- for (i = 1; i < first_apk; i++) {
+ std::string install_cmd;
+ if (_use_legacy_install()) {
+ install_cmd = "exec:pm";
+ } else {
+ install_cmd = "exec:cmd package";
+ }
+
+ std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
+ for (int i = 1; i < first_apk; i++) {
cmd += " " + escape_arg(argv[i]);
}
@@ -1592,7 +2084,7 @@
int fd = adb_connect(cmd, &error);
if (fd < 0) {
fprintf(stderr, "Connect error for create: %s\n", error.c_str());
- return -1;
+ return EXIT_FAILURE;
}
char buf[BUFSIZ];
read_status_line(fd, buf, sizeof(buf));
@@ -1610,22 +2102,23 @@
if (session_id < 0) {
fprintf(stderr, "Failed to create session\n");
fputs(buf, stderr);
- return -1;
+ return EXIT_FAILURE;
}
// Valid session, now stream the APKs
int success = 1;
- for (i = first_apk; i < argc; i++) {
+ for (int i = first_apk; i < argc; i++) {
const char* file = argv[i];
+ struct stat sb;
if (stat(file, &sb) == -1) {
- fprintf(stderr, "Failed to stat %s\n", file);
+ fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
success = 0;
goto finalize_session;
}
std::string cmd = android::base::StringPrintf(
- "exec:pm install-write -S %" PRIu64 " %d %d_%s -",
- static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
+ "%s install-write -S %" PRIu64 " %d %d_%s -",
+ install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
@@ -1660,12 +2153,12 @@
finalize_session:
// Commit session if we streamed everything okay; otherwise abandon
std::string service =
- android::base::StringPrintf("exec:pm install-%s %d",
- success ? "commit" : "abandon", session_id);
+ android::base::StringPrintf("%s install-%s %d",
+ install_cmd.c_str(), success ? "commit" : "abandon", session_id);
fd = adb_connect(service, &error);
if (fd < 0) {
fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
- return -1;
+ return EXIT_FAILURE;
}
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
@@ -1676,6 +2169,79 @@
} else {
fprintf(stderr, "Failed to finalize session\n");
fputs(buf, stderr);
- return -1;
+ return EXIT_FAILURE;
}
}
+
+static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+ std::string cmd = "pm";
+
+ while (argc-- > 0) {
+ cmd += " " + escape_arg(*argv++);
+ }
+
+ return send_shell_command(transport, serial, cmd, false);
+}
+
+static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+ /* if the user choose the -k option, we refuse to do it until devices are
+ out with the option to uninstall the remaining data somehow (adb/ui) */
+ int i;
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-k")) {
+ printf(
+ "The -k option uninstalls the application while retaining the data/cache.\n"
+ "At the moment, there is no way to remove the remaining data.\n"
+ "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+ "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n.");
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+ return pm_command(transport, serial, argc, argv);
+}
+
+static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
+ std::string cmd = "rm -f " + escape_arg(filename);
+ return send_shell_command(transport, serial, cmd, false);
+}
+
+static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+ static const char *const DATA_DEST = "/data/local/tmp/%s";
+ static const char *const SD_DEST = "/sdcard/tmp/%s";
+ const char* where = DATA_DEST;
+
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-s")) {
+ where = SD_DEST;
+ }
+ }
+
+ // Find last APK argument.
+ // All other arguments passed through verbatim.
+ int last_apk = -1;
+ for (int i = argc - 1; i >= 0; i--) {
+ if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
+ last_apk = i;
+ break;
+ }
+ }
+
+ if (last_apk == -1) {
+ fprintf(stderr, "No APK file on command line\n");
+ return EXIT_FAILURE;
+ }
+
+ int result = -1;
+ std::vector<const char*> apk_file = {argv[last_apk]};
+ std::string apk_dest = android::base::StringPrintf(
+ where, adb_basename(argv[last_apk]).c_str());
+ if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
+ argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
+ result = pm_command(transport, serial, argc, argv);
+
+cleanup_apk:
+ delete_file(transport, serial, apk_dest);
+ return result;
+}
diff --git a/adb/commandline.h b/adb/commandline.h
new file mode 100644
index 0000000..0cf655c
--- /dev/null
+++ b/adb/commandline.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COMMANDLINE_H
+#define COMMANDLINE_H
+
+#include "adb.h"
+
+// Callback used to handle the standard streams (stdout and stderr) sent by the
+// device's upon receiving a command.
+//
+class StandardStreamsCallbackInterface {
+ public:
+ StandardStreamsCallbackInterface() {
+ }
+ // Handles the stdout output from devices supporting the Shell protocol.
+ virtual void OnStdout(const char* buffer, int length) = 0;
+
+ // Handles the stderr output from devices supporting the Shell protocol.
+ virtual void OnStderr(const char* buffer, int length) = 0;
+
+ // Indicates the communication is finished and returns the appropriate error
+ // code.
+ //
+ // |status| has the status code returning by the underlying communication
+ // channels
+ virtual int Done(int status) = 0;
+
+ protected:
+ static void OnStream(std::string* string, FILE* stream, const char* buffer, int length) {
+ if (string != nullptr) {
+ string->append(buffer, length);
+ } else {
+ fwrite(buffer, 1, length, stream);
+ fflush(stream);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StandardStreamsCallbackInterface);
+};
+
+// Default implementation that redirects the streams to the equilavent host
+// stream or to a string
+// passed to the constructor.
+class DefaultStandardStreamsCallback : public StandardStreamsCallbackInterface {
+ public:
+ // If |stdout_str| is non-null, OnStdout will append to it.
+ // If |stderr_str| is non-null, OnStderr will append to it.
+ DefaultStandardStreamsCallback(std::string* stdout_str, std::string* stderr_str)
+ : stdout_str_(stdout_str), stderr_str_(stderr_str) {
+ }
+
+ void OnStdout(const char* buffer, int length) {
+ OnStream(stdout_str_, stdout, buffer, length);
+ }
+
+ void OnStderr(const char* buffer, int length) {
+ OnStream(stderr_str_, stderr, buffer, length);
+ }
+
+ int Done(int status) {
+ return status;
+ }
+
+ private:
+ std::string* stdout_str_;
+ std::string* stderr_str_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback);
+};
+
+// Singleton.
+extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
+
+int adb_commandline(int argc, const char** argv);
+int usage();
+
+// Connects to the device "shell" service with |command| and prints the
+// resulting output.
+// if |callback| is non-null, stdout/stderr output will be handled by it.
+int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
+ bool disable_shell_protocol, StandardStreamsCallbackInterface* callback =
+ &DEFAULT_STANDARD_STREAMS_CALLBACK);
+
+#endif // COMMANDLINE_H
diff --git a/adb/console.cpp b/adb/console.cpp
index ba5a72b..9563eac 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -18,13 +18,39 @@
#include <stdio.h>
-#include <base/file.h>
-#include <base/logging.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <cutils/sockets.h>
#include "adb.h"
#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+
+// Return the console authentication command for the emulator, if needed
+static std::string adb_construct_auth_command() {
+ static const char auth_token_filename[] = ".emulator_console_auth_token";
+
+ std::string auth_token_path = adb_get_homedir_path();
+ auth_token_path += OS_PATH_SEPARATOR;
+ auth_token_path += auth_token_filename;
+
+ // read the token
+ std::string token;
+ if (!android::base::ReadFileToString(auth_token_path, &token)
+ || token.empty()) {
+ // we either can't read the file, or it doesn't exist, or it's empty -
+ // either way we won't add any authentication command.
+ return {};
+ }
+
+ // now construct and return the actual command: "auth <token>\n"
+ std::string command = "auth ";
+ command += token;
+ command += '\n';
+ return command;
+}
// Return the console port of the currently connected emulator (if any) or -1 if
// there is no emulator, and -2 if there is more than one.
@@ -87,14 +113,20 @@
return 1;
}
+ std::string commands = adb_construct_auth_command();
+
for (int i = 1; i < argc; i++) {
- adb_write(fd, argv[i], strlen(argv[i]));
- adb_write(fd, i == argc - 1 ? "\n" : " ", 1);
+ commands.append(argv[i]);
+ commands.push_back(i == argc - 1 ? '\n' : ' ');
}
- const char disconnect_command[] = "quit\n";
- if (adb_write(fd, disconnect_command, sizeof(disconnect_command) - 1) == -1) {
- LOG(FATAL) << "Could not finalize emulator command";
+ commands.append("quit\n");
+
+ if (!WriteFdExactly(fd, commands)) {
+ fprintf(stderr, "error: cannot write to emulator: %s\n",
+ strerror(errno));
+ adb_close(fd);
+ return 1;
}
// Drain output that the emulator console has sent us to prevent a problem
@@ -106,11 +138,14 @@
do {
char buf[BUFSIZ];
result = adb_read(fd, buf, sizeof(buf));
- // Keep reading until zero bytes (EOF) or an error. If 'adb emu kill'
- // is executed, the emulator calls exit() which causes adb to get
- // ECONNRESET. Any other emu command is followed by the quit command
- // that we sent above, and that causes the emulator to close the socket
- // which should cause zero bytes (EOF) to be returned.
+ // Keep reading until zero bytes (orderly/graceful shutdown) or an
+ // error. If 'adb emu kill' is executed, the emulator calls exit() with
+ // the socket open (and shutdown(SD_SEND) was not called), which causes
+ // Windows to send a TCP RST segment which causes adb to get ECONNRESET.
+ // Any other emu command is followed by the quit command that we
+ // appended above, and that causes the emulator to close the socket
+ // which should cause zero bytes (orderly/graceful shutdown) to be
+ // returned.
} while (result > 0);
adb_close(fd);
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index dc89639..6382b67 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
#include "sysdeps.h"
@@ -25,49 +25,39 @@
#include <getopt.h>
#include <sys/prctl.h>
-#include "base/logging.h"
-#include "base/stringprintf.h"
-#include "cutils/properties.h"
-#include "private/android_filesystem_config.h"
-#include "selinux/selinux.h"
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <libminijail.h>
+#include <scoped_minijail.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include "debuggerd/handler.h"
+#include "selinux/android.h"
#include "adb.h"
#include "adb_auth.h"
#include "adb_listeners.h"
+#include "adb_utils.h"
#include "transport.h"
-#include "qemu_tracing.h"
static const char* root_seclabel = nullptr;
-static void drop_capabilities_bounding_set_if_needed() {
-#ifdef ALLOW_ADBD_ROOT
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.debuggable", value, "");
- if (strcmp(value, "1") == 0) {
+static void drop_capabilities_bounding_set_if_needed(struct minijail *j) {
+#if defined(ALLOW_ADBD_ROOT)
+ if (__android_log_is_debuggable()) {
return;
}
#endif
- for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
- if (i == CAP_SETUID || i == CAP_SETGID) {
- // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
- continue;
- }
-
- int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
-
- // Some kernels don't have file capabilities compiled in, and
- // prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
- // die when we see such misconfigured kernels.
- if ((err < 0) && (errno != EINVAL)) {
- PLOG(FATAL) << "Could not drop capabilities";
- }
- }
+ minijail_capbset_drop(j, CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
}
static bool should_drop_privileges() {
#if defined(ALLOW_ADBD_ROOT)
- char value[PROPERTY_VALUE_MAX];
-
// The properties that affect `adb root` and `adb unroot` are ro.secure and
// ro.debuggable. In this context the names don't make the expected behavior
// particularly obvious.
@@ -78,25 +68,20 @@
//
// ro.secure:
// Drop privileges by default. Set to 1 on userdebug and user builds.
- property_get("ro.secure", value, "1");
- bool ro_secure = (strcmp(value, "1") == 0);
-
- property_get("ro.debuggable", value, "");
- bool ro_debuggable = (strcmp(value, "1") == 0);
+ bool ro_secure = android::base::GetBoolProperty("ro.secure", true);
+ bool ro_debuggable = __android_log_is_debuggable();
// Drop privileges if ro.secure is set...
bool drop = ro_secure;
- property_get("service.adb.root", value, "");
- bool adb_root = (strcmp(value, "1") == 0);
- bool adb_unroot = (strcmp(value, "0") == 0);
-
- // ...except "adb root" lets you keep privileges in a debuggable build.
+ // ... except "adb root" lets you keep privileges in a debuggable build.
+ std::string prop = android::base::GetProperty("service.adb.root", "");
+ bool adb_root = (prop == "1");
+ bool adb_unroot = (prop == "0");
if (ro_debuggable && adb_root) {
drop = false;
}
-
- // ...and "adb unroot" lets you explicitly drop privileges.
+ // ... and "adb unroot" lets you explicitly drop privileges.
if (adb_unroot) {
drop = true;
}
@@ -107,6 +92,54 @@
#endif // ALLOW_ADBD_ROOT
}
+static void drop_privileges(int server_port) {
+ ScopedMinijail jail(minijail_new());
+
+ // Add extra groups:
+ // AID_ADB to access the USB driver
+ // AID_LOG to read system logs (adb logcat)
+ // AID_INPUT to diagnose input issues (getevent)
+ // AID_INET to diagnose network issues (ping)
+ // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
+ // AID_SDCARD_R to allow reading from the SD card
+ // AID_SDCARD_RW to allow writing to the SD card
+ // AID_NET_BW_STATS to read out qtaguid statistics
+ // AID_READPROC for reading /proc entries across UID boundaries
+ gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT,
+ AID_INET, AID_NET_BT, AID_NET_BT_ADMIN,
+ AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
+ AID_READPROC};
+ minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
+
+ // Don't listen on a port (default 5037) if running in secure mode.
+ // Don't run as root if running in secure mode.
+ if (should_drop_privileges()) {
+ drop_capabilities_bounding_set_if_needed(jail.get());
+
+ minijail_change_gid(jail.get(), AID_SHELL);
+ minijail_change_uid(jail.get(), AID_SHELL);
+ // minijail_enter() will abort if any priv-dropping step fails.
+ minijail_enter(jail.get());
+
+ D("Local port disabled");
+ } else {
+ // minijail_enter() will abort if any priv-dropping step fails.
+ minijail_enter(jail.get());
+
+ if (root_seclabel != nullptr) {
+ if (selinux_android_setcon(root_seclabel) < 0) {
+ LOG(FATAL) << "Could not set SELinux context";
+ }
+ }
+ std::string error;
+ std::string local_name =
+ android::base::StringPrintf("tcp:%d", server_port);
+ if (install_listener(local_name, "*smartsocket*", nullptr, 0, nullptr, &error)) {
+ LOG(FATAL) << "Could not install *smartsocket* listener: " << error;
+ }
+ }
+}
+
int adbd_main(int server_port) {
umask(0);
@@ -118,7 +151,7 @@
// descriptor will always be open.
adbd_cloexec_auth_socket();
- if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) {
+ if (ALLOW_ADBD_NO_AUTH && !android::base::GetBoolProperty("ro.adb.secure", false)) {
auth_required = false;
}
@@ -134,54 +167,10 @@
" unchanged.\n");
}
- // Add extra groups:
- // AID_ADB to access the USB driver
- // AID_LOG to read system logs (adb logcat)
- // AID_INPUT to diagnose input issues (getevent)
- // AID_INET to diagnose network issues (ping)
- // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
- // AID_SDCARD_R to allow reading from the SD card
- // AID_SDCARD_RW to allow writing to the SD card
- // AID_NET_BW_STATS to read out qtaguid statistics
- gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT,
- AID_INET, AID_NET_BT, AID_NET_BT_ADMIN,
- AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS};
- if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
- PLOG(FATAL) << "Could not set supplental groups";
- }
-
- /* don't listen on a port (default 5037) if running in secure mode */
- /* don't run as root if we are running in secure mode */
- if (should_drop_privileges()) {
- drop_capabilities_bounding_set_if_needed();
-
- /* then switch user and group to "shell" */
- if (setgid(AID_SHELL) != 0) {
- PLOG(FATAL) << "Could not setgid";
- }
- if (setuid(AID_SHELL) != 0) {
- PLOG(FATAL) << "Could not setuid";
- }
-
- D("Local port disabled\n");
- } else {
- if (root_seclabel != nullptr) {
- if (setcon(root_seclabel) < 0) {
- LOG(FATAL) << "Could not set selinux context";
- }
- }
- std::string error;
- std::string local_name =
- android::base::StringPrintf("tcp:%d", server_port);
- if (install_listener(local_name, "*smartsocket*", nullptr, 0,
- &error)) {
- LOG(FATAL) << "Could not install *smartsocket* listener: "
- << error;
- }
- }
+ drop_privileges(server_port);
bool is_usb = false;
- if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
+ if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
// Listen on USB.
usb_init();
is_usb = true;
@@ -190,15 +179,14 @@
// If one of these properties is set, also listen on that port.
// If one of the properties isn't set and we couldn't listen on usb, listen
// on the default port.
- char prop_port[PROPERTY_VALUE_MAX];
- property_get("service.adb.tcp.port", prop_port, "");
- if (prop_port[0] == '\0') {
- property_get("persist.adb.tcp.port", prop_port, "");
+ std::string prop_port = android::base::GetProperty("service.adb.tcp.port", "");
+ if (prop_port.empty()) {
+ prop_port = android::base::GetProperty("persist.adb.tcp.port", "");
}
int port;
- if (sscanf(prop_port, "%d", &port) == 1 && port > 0) {
- printf("using port=%d\n", port);
+ if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) {
+ D("using port=%d", port);
// Listen on TCP port specified by service.adb.tcp.port property.
local_init(port);
} else if (!is_usb) {
@@ -206,26 +194,16 @@
local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
}
- D("adbd_main(): pre init_jdwp()\n");
+ D("adbd_main(): pre init_jdwp()");
init_jdwp();
- D("adbd_main(): post init_jdwp()\n");
+ D("adbd_main(): post init_jdwp()");
- D("Event loop starting\n");
+ D("Event loop starting");
fdevent_loop();
return 0;
}
-static void close_stdin() {
- int fd = unix_open("/dev/null", O_RDONLY);
- if (fd == -1) {
- perror("failed to open /dev/null, stdin will remain open");
- return;
- }
- dup2(fd, STDIN_FILENO);
- unix_close(fd);
-}
-
int main(int argc, char** argv) {
while (true) {
static struct option opts[] = {
@@ -260,12 +238,9 @@
close_stdin();
+ debuggerd_init(nullptr);
adb_trace_init(argv);
- /* If adbd runs inside the emulator this will enable adb tracing via
- * adb-debug qemud service in the emulator. */
- adb_qemu_trace_init();
-
- D("Handling main()\n");
+ D("Handling main()");
return adbd_main(DEFAULT_ADB_PORT);
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
new file mode 100644
index 0000000..a35c210
--- /dev/null
+++ b/adb/daemon/usb.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG USB
+
+#include "sysdeps.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include "adb.h"
+#include "daemon/usb.h"
+#include "transport.h"
+
+using namespace std::chrono_literals;
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+// Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
+#define USB_FFS_MAX_WRITE 16384
+
+// The kernel allocates a contiguous buffer for reads, which can fail for large ones due to
+// fragmentation. 16k chosen arbitrarily to match the write limit.
+#define USB_FFS_MAX_READ 16384
+
+#define cpu_to_le16(x) htole16(x)
+#define cpu_to_le32(x) htole32(x)
+
+static int dummy_fd = -1;
+
+struct func_desc {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio source;
+ struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct ss_func_desc {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio source;
+ struct usb_ss_ep_comp_descriptor source_comp;
+ struct usb_endpoint_descriptor_no_audio sink;
+ struct usb_ss_ep_comp_descriptor sink_comp;
+} __attribute__((packed));
+
+struct desc_v1 {
+ struct usb_functionfs_descs_head_v1 {
+ __le32 magic;
+ __le32 length;
+ __le32 fs_count;
+ __le32 hs_count;
+ } __attribute__((packed)) header;
+ struct func_desc fs_descs, hs_descs;
+} __attribute__((packed));
+
+struct desc_v2 {
+ struct usb_functionfs_descs_head_v2 header;
+ // The rest of the structure depends on the flags in the header.
+ __le32 fs_count;
+ __le32 hs_count;
+ __le32 ss_count;
+ __le32 os_count;
+ struct func_desc fs_descs, hs_descs;
+ struct ss_func_desc ss_descs;
+ struct usb_os_desc_header os_header;
+ struct usb_ext_compat_desc os_desc;
+} __attribute__((packed));
+
+static struct func_desc fs_descriptors = {
+ .intf = {
+ .bLength = sizeof(fs_descriptors.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = ADB_CLASS,
+ .bInterfaceSubClass = ADB_SUBCLASS,
+ .bInterfaceProtocol = ADB_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(fs_descriptors.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+ },
+ .sink = {
+ .bLength = sizeof(fs_descriptors.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+ },
+};
+
+static struct func_desc hs_descriptors = {
+ .intf = {
+ .bLength = sizeof(hs_descriptors.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = ADB_CLASS,
+ .bInterfaceSubClass = ADB_SUBCLASS,
+ .bInterfaceProtocol = ADB_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(hs_descriptors.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+ },
+ .sink = {
+ .bLength = sizeof(hs_descriptors.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+ },
+};
+
+static struct ss_func_desc ss_descriptors = {
+ .intf = {
+ .bLength = sizeof(ss_descriptors.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = ADB_CLASS,
+ .bInterfaceSubClass = ADB_SUBCLASS,
+ .bInterfaceProtocol = ADB_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(ss_descriptors.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+ },
+ .source_comp = {
+ .bLength = sizeof(ss_descriptors.source_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ },
+ .sink = {
+ .bLength = sizeof(ss_descriptors.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+ },
+ .sink_comp = {
+ .bLength = sizeof(ss_descriptors.sink_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ },
+};
+
+struct usb_ext_compat_desc os_desc_compat = {
+ .bFirstInterfaceNumber = 0,
+ .Reserved1 = cpu_to_le32(1),
+ .CompatibleID = {0},
+ .SubCompatibleID = {0},
+ .Reserved2 = {0},
+};
+
+static struct usb_os_desc_header os_desc_header = {
+ .interface = cpu_to_le32(1),
+ .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
+ .bcdVersion = cpu_to_le32(1),
+ .wIndex = cpu_to_le32(4),
+ .bCount = cpu_to_le32(1),
+ .Reserved = cpu_to_le32(0),
+};
+
+#define STR_INTERFACE_ "ADB Interface"
+
+static const struct {
+ struct usb_functionfs_strings_head header;
+ struct {
+ __le16 code;
+ const char str1[sizeof(STR_INTERFACE_)];
+ } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+ .header = {
+ .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+ .length = cpu_to_le32(sizeof(strings)),
+ .str_count = cpu_to_le32(1),
+ .lang_count = cpu_to_le32(1),
+ },
+ .lang0 = {
+ cpu_to_le16(0x0409), /* en-us */
+ STR_INTERFACE_,
+ },
+};
+
+bool init_functionfs(struct usb_handle* h) {
+ ssize_t ret;
+ struct desc_v1 v1_descriptor;
+ struct desc_v2 v2_descriptor;
+
+ v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
+ v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
+ v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+ FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
+ v2_descriptor.fs_count = 3;
+ v2_descriptor.hs_count = 3;
+ v2_descriptor.ss_count = 5;
+ v2_descriptor.os_count = 1;
+ v2_descriptor.fs_descs = fs_descriptors;
+ v2_descriptor.hs_descs = hs_descriptors;
+ v2_descriptor.ss_descs = ss_descriptors;
+ v2_descriptor.os_header = os_desc_header;
+ v2_descriptor.os_desc = os_desc_compat;
+
+ if (h->control < 0) { // might have already done this before
+ D("OPENING %s", USB_FFS_ADB_EP0);
+ h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
+ if (h->control < 0) {
+ D("[ %s: cannot open control endpoint: errno=%d]", USB_FFS_ADB_EP0, errno);
+ goto err;
+ }
+
+ ret = adb_write(h->control, &v2_descriptor, sizeof(v2_descriptor));
+ if (ret < 0) {
+ v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+ v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
+ v1_descriptor.header.fs_count = 3;
+ v1_descriptor.header.hs_count = 3;
+ v1_descriptor.fs_descs = fs_descriptors;
+ v1_descriptor.hs_descs = hs_descriptors;
+ D("[ %s: Switching to V1_descriptor format errno=%d ]", USB_FFS_ADB_EP0, errno);
+ ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor));
+ if (ret < 0) {
+ D("[ %s: write descriptors failed: errno=%d ]", USB_FFS_ADB_EP0, errno);
+ goto err;
+ }
+ }
+
+ ret = adb_write(h->control, &strings, sizeof(strings));
+ if (ret < 0) {
+ D("[ %s: writing strings failed: errno=%d]", USB_FFS_ADB_EP0, errno);
+ goto err;
+ }
+ }
+
+ h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
+ if (h->bulk_out < 0) {
+ D("[ %s: cannot open bulk-out ep: errno=%d ]", USB_FFS_ADB_OUT, errno);
+ goto err;
+ }
+
+ h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
+ if (h->bulk_in < 0) {
+ D("[ %s: cannot open bulk-in ep: errno=%d ]", USB_FFS_ADB_IN, errno);
+ goto err;
+ }
+
+ return true;
+
+err:
+ if (h->bulk_in > 0) {
+ adb_close(h->bulk_in);
+ h->bulk_in = -1;
+ }
+ if (h->bulk_out > 0) {
+ adb_close(h->bulk_out);
+ h->bulk_out = -1;
+ }
+ if (h->control > 0) {
+ adb_close(h->control);
+ h->control = -1;
+ }
+ return false;
+}
+
+static void usb_ffs_open_thread(void* x) {
+ struct usb_handle* usb = (struct usb_handle*)x;
+
+ adb_thread_setname("usb ffs open");
+
+ while (true) {
+ // wait until the USB device needs opening
+ std::unique_lock<std::mutex> lock(usb->lock);
+ while (!usb->open_new_connection) {
+ usb->notify.wait(lock);
+ }
+ usb->open_new_connection = false;
+ lock.unlock();
+
+ while (true) {
+ if (init_functionfs(usb)) {
+ break;
+ }
+ std::this_thread::sleep_for(1s);
+ }
+ android::base::SetProperty("sys.usb.ffs.ready", "1");
+
+ D("[ usb_thread - registering device ]");
+ register_usb_transport(usb, 0, 0, 1);
+ }
+
+ // never gets here
+ abort();
+}
+
+static int usb_ffs_write(usb_handle* h, const void* data, int len) {
+ D("about to write (fd=%d, len=%d)", h->bulk_in, len);
+
+ const char* buf = static_cast<const char*>(data);
+ while (len > 0) {
+ int write_len = std::min(USB_FFS_MAX_WRITE, len);
+ int n = adb_write(h->bulk_in, buf, write_len);
+ if (n < 0) {
+ D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
+ return -1;
+ }
+ buf += n;
+ len -= n;
+ }
+
+ D("[ done fd=%d ]", h->bulk_in);
+ return 0;
+}
+
+static int usb_ffs_read(usb_handle* h, void* data, int len) {
+ D("about to read (fd=%d, len=%d)", h->bulk_out, len);
+
+ char* buf = static_cast<char*>(data);
+ while (len > 0) {
+ int read_len = std::min(USB_FFS_MAX_READ, len);
+ int n = adb_read(h->bulk_out, buf, read_len);
+ if (n < 0) {
+ D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
+ return -1;
+ }
+ buf += n;
+ len -= n;
+ }
+
+ D("[ done fd=%d ]", h->bulk_out);
+ return 0;
+}
+
+static void usb_ffs_kick(usb_handle* h) {
+ int err;
+
+ err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
+ if (err < 0) {
+ D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno);
+ }
+
+ err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
+ if (err < 0) {
+ D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
+ }
+
+ // don't close ep0 here, since we may not need to reinitialize it with
+ // the same descriptors again. if however ep1/ep2 fail to re-open in
+ // init_functionfs, only then would we close and open ep0 again.
+ // Ditto the comment in usb_adb_kick.
+ h->kicked = true;
+ TEMP_FAILURE_RETRY(dup2(dummy_fd, h->bulk_out));
+ TEMP_FAILURE_RETRY(dup2(dummy_fd, h->bulk_in));
+}
+
+static void usb_ffs_close(usb_handle* h) {
+ h->kicked = false;
+ adb_close(h->bulk_out);
+ adb_close(h->bulk_in);
+ // Notify usb_adb_open_thread to open a new connection.
+ h->lock.lock();
+ h->open_new_connection = true;
+ h->lock.unlock();
+ h->notify.notify_one();
+}
+
+static void usb_ffs_init() {
+ D("[ usb_init - using FunctionFS ]");
+
+ usb_handle* h = new usb_handle();
+
+ h->write = usb_ffs_write;
+ h->read = usb_ffs_read;
+ h->kick = usb_ffs_kick;
+ h->close = usb_ffs_close;
+
+ D("[ usb_init - starting thread ]");
+ if (!adb_thread_create(usb_ffs_open_thread, h)) {
+ fatal_errno("[ cannot create usb thread ]\n");
+ }
+}
+
+void usb_init() {
+ dummy_fd = adb_open("/dev/null", O_WRONLY);
+ CHECK_NE(dummy_fd, -1);
+ usb_ffs_init();
+}
+
+int usb_write(usb_handle* h, const void* data, int len) {
+ return h->write(h, data, len);
+}
+
+int usb_read(usb_handle* h, void* data, int len) {
+ return h->read(h, data, len);
+}
+
+int usb_close(usb_handle* h) {
+ h->close(h);
+ return 0;
+}
+
+void usb_kick(usb_handle* h) {
+ h->kick(h);
+}
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
new file mode 100644
index 0000000..1d85405
--- /dev/null
+++ b/adb/daemon/usb.h
@@ -0,0 +1,50 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+
+// Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
+#define USB_FFS_MAX_WRITE 16384
+
+// The kernel allocates a contiguous buffer for reads, which can fail for large ones due to
+// fragmentation. 16k chosen arbitrarily to match the write limit.
+#define USB_FFS_MAX_READ 16384
+
+struct usb_handle {
+ usb_handle() : kicked(false) {
+ }
+
+ std::condition_variable notify;
+ std::mutex lock;
+ std::atomic<bool> kicked;
+ bool open_new_connection = true;
+
+ int (*write)(usb_handle* h, const void* data, int len);
+ int (*read)(usb_handle* h, void* data, int len);
+ void (*kick)(usb_handle* h);
+ void (*close)(usb_handle* h);
+
+ // FunctionFS
+ int control = -1;
+ int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
+ int bulk_in = -1; /* "in" from the host's perspective => sink for adbd */
+};
+
+bool init_functionfs(struct usb_handle* h);
diff --git a/adb/device.py b/adb/device.py
deleted file mode 100644
index 5b33ff2..0000000
--- a/adb/device.py
+++ /dev/null
@@ -1,263 +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.
-#
-import logging
-import os
-import re
-import subprocess
-
-
-class FindDeviceError(RuntimeError):
- pass
-
-
-class DeviceNotFoundError(FindDeviceError):
- def __init__(self, serial):
- self.serial = serial
- super(DeviceNotFoundError, self).__init__(
- 'No device with serial {}'.format(serial))
-
-
-class NoUniqueDeviceError(FindDeviceError):
- def __init__(self):
- super(NoUniqueDeviceError, self).__init__('No unique device')
-
-
-def get_devices():
- with open(os.devnull, 'wb') as devnull:
- subprocess.check_call(['adb', 'start-server'], stdout=devnull,
- stderr=devnull)
- out = subprocess.check_output(['adb', 'devices']).splitlines()
-
- # The first line of `adb devices` just says "List of attached devices", so
- # skip that.
- devices = []
- for line in out[1:]:
- if not line.strip():
- continue
- if 'offline' in line:
- continue
-
- serial, _ = re.split(r'\s+', line, maxsplit=1)
- devices.append(serial)
- return devices
-
-
-def _get_unique_device(product=None):
- devices = get_devices()
- if len(devices) != 1:
- raise NoUniqueDeviceError()
- return AndroidDevice(devices[0], product)
-
-
-def _get_device_by_serial(serial, product=None):
- for device in get_devices():
- if device == serial:
- return AndroidDevice(serial, product)
- raise DeviceNotFoundError(serial)
-
-
-def get_device(serial=None, product=None):
- """Get a uniquely identified AndroidDevice if one is available.
-
- Raises:
- DeviceNotFoundError:
- The serial specified by `serial` or $ANDROID_SERIAL is not
- connected.
-
- NoUniqueDeviceError:
- Neither `serial` nor $ANDROID_SERIAL was set, and the number of
- devices connected to the system is not 1. Having 0 connected
- devices will also result in this error.
-
- Returns:
- An AndroidDevice associated with the first non-None identifier in the
- following order of preference:
-
- 1) The `serial` argument.
- 2) The environment variable $ANDROID_SERIAL.
- 3) The single device connnected to the system.
- """
- if serial is not None:
- return _get_device_by_serial(serial, product)
-
- android_serial = os.getenv('ANDROID_SERIAL')
- if android_serial is not None:
- return _get_device_by_serial(android_serial, product)
-
- return _get_unique_device(product)
-
-
-class AndroidDevice(object):
- # Delimiter string to indicate the start of the exit code.
- _RETURN_CODE_DELIMITER = 'x'
-
- # Follow any shell command with this string to get the exit
- # status of a program since this isn't propagated by adb.
- #
- # The delimiter is needed because `printf 1; echo $?` would print
- # "10", and we wouldn't be able to distinguish the exit code.
- _RETURN_CODE_PROBE_STRING = 'echo "{0}$?"'.format(_RETURN_CODE_DELIMITER)
-
- # Maximum search distance from the output end to find the delimiter.
- # adb on Windows returns \r\n even if adbd returns \n.
- _RETURN_CODE_SEARCH_LENGTH = len('{0}255\r\n'.format(_RETURN_CODE_DELIMITER))
-
- def __init__(self, serial, product=None):
- self.serial = serial
- self.product = product
- self.adb_cmd = ['adb']
- if self.serial is not None:
- self.adb_cmd.extend(['-s', serial])
- if self.product is not None:
- self.adb_cmd.extend(['-p', product])
- self._linesep = None
-
- @property
- def linesep(self):
- if self._linesep is None:
- self._linesep = subprocess.check_output(self.adb_cmd +
- ['shell', 'echo'])
- return self._linesep
-
- def _make_shell_cmd(self, user_cmd):
- return (self.adb_cmd + ['shell'] + user_cmd +
- ['; ' + self._RETURN_CODE_PROBE_STRING])
-
- def _parse_shell_output(self, out):
- """Finds the exit code string from shell output.
-
- Args:
- out: Shell output string.
-
- Returns:
- An (exit_code, output_string) tuple. The output string is
- cleaned of any additional stuff we appended to find the
- exit code.
-
- Raises:
- RuntimeError: Could not find the exit code in |out|.
- """
- search_text = out
- if len(search_text) > self._RETURN_CODE_SEARCH_LENGTH:
- # We don't want to search over massive amounts of data when we know
- # the part we want is right at the end.
- search_text = search_text[-self._RETURN_CODE_SEARCH_LENGTH:]
- partition = search_text.rpartition(self._RETURN_CODE_DELIMITER)
- if partition[1] == '':
- raise RuntimeError('Could not find exit status in shell output.')
- result = int(partition[2])
- # partition[0] won't contain the full text if search_text was truncated,
- # pull from the original string instead.
- out = out[:-len(partition[1]) - len(partition[2])]
- return result, out
-
- def _simple_call(self, cmd):
- logging.info(' '.join(self.adb_cmd + cmd))
- return subprocess.check_output(
- self.adb_cmd + cmd, stderr=subprocess.STDOUT)
-
- def shell(self, cmd):
- logging.info(' '.join(self.adb_cmd + ['shell'] + cmd))
- cmd = self._make_shell_cmd(cmd)
- out = subprocess.check_output(cmd)
- rc, out = self._parse_shell_output(out)
- if rc != 0:
- error = subprocess.CalledProcessError(rc, cmd)
- error.out = out
- raise error
- return out
-
- def shell_nocheck(self, cmd):
- cmd = self._make_shell_cmd(cmd)
- logging.info(' '.join(cmd))
- p = subprocess.Popen(
- cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- out, _ = p.communicate()
- return self._parse_shell_output(out)
-
- def install(self, filename, replace=False):
- cmd = ['install']
- if replace:
- cmd.append('-r')
- cmd.append(filename)
- return self._simple_call(cmd)
-
- def push(self, local, remote):
- return self._simple_call(['push', local, remote])
-
- def pull(self, remote, local):
- return self._simple_call(['pull', remote, local])
-
- def sync(self, directory=None):
- cmd = ['sync']
- if directory is not None:
- cmd.append(directory)
- return self._simple_call(cmd)
-
- def forward(self, local, remote):
- return self._simple_call(['forward', local, remote])
-
- def tcpip(self, port):
- return self._simple_call(['tcpip', port])
-
- def usb(self):
- return self._simple_call(['usb'])
-
- def reboot(self):
- return self._simple_call(['reboot'])
-
- def root(self):
- return self._simple_call(['root'])
-
- def unroot(self):
- return self._simple_call(['unroot'])
-
- def forward_remove(self, local):
- return self._simple_call(['forward', '--remove', local])
-
- def forward_remove_all(self):
- return self._simple_call(['forward', '--remove-all'])
-
- def connect(self, host):
- return self._simple_call(['connect', host])
-
- def disconnect(self, host):
- return self._simple_call(['disconnect', host])
-
- def reverse(self, remote, local):
- return self._simple_call(['reverse', remote, local])
-
- def reverse_remove_all(self):
- return self._simple_call(['reverse', '--remove-all'])
-
- def reverse_remove(self, remote):
- return self._simple_call(['reverse', '--remove', remote])
-
- def wait(self):
- return self._simple_call(['wait-for-device'])
-
- def get_prop(self, prop_name):
- output = self.shell(['getprop', prop_name]).splitlines()
- if len(output) != 1:
- raise RuntimeError('Too many lines in getprop output:\n' +
- '\n'.join(output))
- value = output[0]
- if not value.strip():
- return None
- return value
-
- def set_prop(self, prop_name, value):
- self.shell(['setprop', prop_name, value])
diff --git a/adb/diagnose_usb.cpp b/adb/diagnose_usb.cpp
new file mode 100644
index 0000000..0f067b0
--- /dev/null
+++ b/adb/diagnose_usb.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "diagnose_usb.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#if defined(__linux__)
+#include <grp.h>
+#endif
+
+static const char kPermissionsHelpUrl[] = "http://developer.android.com/tools/device.html";
+
+// Returns a message describing any potential problems we find with udev, or nullptr if we can't
+// find plugdev information (i.e. udev is not installed).
+static const char* GetUdevProblem() {
+#if defined(__linux__)
+ errno = 0;
+ group* plugdev_group = getgrnam("plugdev");
+
+ if (plugdev_group == nullptr) {
+ if (errno != 0) {
+ perror("failed to read plugdev group info");
+ }
+ // We can't give any generally useful advice here, just let the caller print the help URL.
+ return nullptr;
+ }
+
+ // getgroups(2) indicates that the group_member() may not check the egid so we check it
+ // additionally just to be sure.
+ if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
+ // The user is in plugdev so the problem is likely with the udev rules.
+ return "verify udev rules";
+ }
+ return "udev requires plugdev group membership";
+#else
+ return nullptr;
+#endif
+}
+
+// Short help text must be a single line, and will look something like:
+// no permissions (reason); see <URL>
+std::string UsbNoPermissionsShortHelpText() {
+ std::string help_text = "no permissions";
+
+ const char* problem = GetUdevProblem();
+ if (problem != nullptr) {
+ help_text += android::base::StringPrintf(" (%s)", problem);
+ }
+
+ return android::base::StringPrintf("%s; see [%s]", help_text.c_str(), kPermissionsHelpUrl);
+}
+
+// Long help text can span multiple lines and should provide more detailed information.
+std::string UsbNoPermissionsLongHelpText() {
+ std::string header = "insufficient permissions for device";
+
+ const char* problem = GetUdevProblem();
+ if (problem != nullptr) {
+ header += android::base::StringPrintf(": %s", problem);
+ }
+
+ return android::base::StringPrintf("%s.\nSee [%s] for more information.",
+ header.c_str(), kPermissionsHelpUrl);
+}
diff --git a/adb/diagnose_usb.h b/adb/diagnose_usb.h
new file mode 100644
index 0000000..325b2e3
--- /dev/null
+++ b/adb/diagnose_usb.h
@@ -0,0 +1,27 @@
+/*
+ * 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 __DIAGNOSE_LINUX_USB_H
+#define __DIAGNOSE_LINUX_USB_H
+
+#include <string>
+
+// USB permission error help text. The short version will be one line, long may be multi-line.
+// Returns a string message to print, or an empty string if no problems could be found.
+std::string UsbNoPermissionsShortHelpText();
+std::string UsbNoPermissionsLongHelpText();
+
+#endif
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index a8abade..04cd865 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -2,77 +2,48 @@
**
** Copyright 2006, Brian Swetland <swetland@frotz.net>
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-#define TRACE_TAG TRACE_FDEVENT
+#define TRACE_TAG FDEVENT
#include "sysdeps.h"
#include "fdevent.h"
-#include <errno.h>
#include <fcntl.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ioctl.h>
#include <unistd.h>
+#include <atomic>
+#include <list>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
#include "adb_io.h"
#include "adb_trace.h"
+#include "adb_utils.h"
-/* !!! Do not enable DEBUG for the adb that will run as the server:
-** both stdout and stderr are used to communicate between the client
-** and server. Any extra output will cause failures.
-*/
-#define DEBUG 0 /* non-0 will break adb server */
-
+#if !ADB_HOST
// This socket is used when a subproc shell service exists.
// It wakes up the fdevent_loop() and cause the correct handling
// of the shell's pseudo-tty master. I.e. force close it.
-#if !ADB_HOST
int SHELL_EXIT_NOTIFY_FD = -1;
#endif // !ADB_HOST
-static void fatal(const char *fn, const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- fprintf(stderr, "%s:", fn);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- abort();
-}
-
-#define FATAL(x...) fatal(__FUNCTION__, x)
-
-#if DEBUG
-static void dump_fde(fdevent *fde, const char *info)
-{
- adb_mutex_lock(&D_lock);
- fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
- fde->state & FDE_READ ? 'R' : ' ',
- fde->state & FDE_WRITE ? 'W' : ' ',
- fde->state & FDE_ERROR ? 'E' : ' ',
- info);
- adb_mutex_unlock(&D_lock);
-}
-#else
-#define dump_fde(fde, info) do { } while(0)
-#endif
-
#define FDE_EVENTMASK 0x00ff
#define FDE_STATEMASK 0xff00
@@ -80,517 +51,70 @@
#define FDE_PENDING 0x0200
#define FDE_CREATED 0x0400
-static void fdevent_plist_enqueue(fdevent *node);
-static void fdevent_plist_remove(fdevent *node);
-static fdevent *fdevent_plist_dequeue(void);
+struct PollNode {
+ fdevent* fde;
+ adb_pollfd pollfd;
-static fdevent list_pending = {
- .next = &list_pending,
- .prev = &list_pending,
- .fd = -1,
- .force_eof = 0,
- .state = 0,
- .events = 0,
- .func = nullptr,
- .arg = nullptr,
+ explicit PollNode(fdevent* fde) : fde(fde) {
+ memset(&pollfd, 0, sizeof(pollfd));
+ pollfd.fd = fde->fd;
+
+#if defined(__linux__)
+ // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
+ // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
+ pollfd.events = POLLRDHUP;
+#endif
+ }
};
-static fdevent **fd_table = 0;
-static int fd_table_max = 0;
+// All operations to fdevent should happen only in the main thread.
+// That's why we don't need a lock for fdevent.
+static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
+static auto& g_pending_list = *new std::list<fdevent*>();
+static std::atomic<bool> terminate_loop(false);
+static bool main_thread_valid;
+static unsigned long main_thread_id;
-#ifdef CRAPTASTIC
-//HAVE_EPOLL
-
-#include <sys/epoll.h>
-
-static int epoll_fd = -1;
-
-static void fdevent_init()
-{
- /* XXX: what's a good size for the passed in hint? */
- epoll_fd = epoll_create(256);
-
- if(epoll_fd < 0) {
- perror("epoll_create() failed");
- exit(1);
- }
-
- /* mark for close-on-exec */
- fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
-}
-
-static void fdevent_connect(fdevent *fde)
-{
- struct epoll_event ev;
-
- memset(&ev, 0, sizeof(ev));
- ev.events = 0;
- ev.data.ptr = fde;
-
-#if 0
- if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
-#endif
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- struct epoll_event ev;
-
- memset(&ev, 0, sizeof(ev));
- ev.events = 0;
- ev.data.ptr = fde;
-
- /* technically we only need to delete if we
- ** were actively monitoring events, but let's
- ** be aggressive and do it anyway, just in case
- ** something's out of sync
- */
- epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- struct epoll_event ev;
- int active;
-
- active = (fde->state & FDE_EVENTMASK) != 0;
-
- memset(&ev, 0, sizeof(ev));
- ev.events = 0;
- ev.data.ptr = fde;
-
- if(events & FDE_READ) ev.events |= EPOLLIN;
- if(events & FDE_WRITE) ev.events |= EPOLLOUT;
- if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-
- if(active) {
- /* we're already active. if we're changing to *no*
- ** events being monitored, we need to delete, otherwise
- ** we need to just modify
- */
- if(ev.events) {
- if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
- } else {
- if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
- }
- } else {
- /* we're not active. if we're watching events, we need
- ** to add, otherwise we can just do nothing
- */
- if(ev.events) {
- if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
- }
+static void check_main_thread() {
+ if (main_thread_valid) {
+ CHECK_EQ(main_thread_id, adb_thread_id());
}
}
-static void fdevent_process()
-{
- struct epoll_event events[256];
- fdevent *fde;
- int i, n;
-
- n = epoll_wait(epoll_fd, events, 256, -1);
-
- if(n < 0) {
- if(errno == EINTR) return;
- perror("epoll_wait");
- exit(1);
- }
-
- for(i = 0; i < n; i++) {
- struct epoll_event *ev = events + i;
- fde = ev->data.ptr;
-
- if(ev->events & EPOLLIN) {
- fde->events |= FDE_READ;
- }
- if(ev->events & EPOLLOUT) {
- fde->events |= FDE_WRITE;
- }
- if(ev->events & (EPOLLERR | EPOLLHUP)) {
- fde->events |= FDE_ERROR;
- }
- if(fde->events) {
- if(fde->state & FDE_PENDING) continue;
- fde->state |= FDE_PENDING;
- fdevent_plist_enqueue(fde);
- }
- }
+static void set_main_thread() {
+ main_thread_valid = true;
+ main_thread_id = adb_thread_id();
}
-#else /* USE_SELECT */
-
-#if defined(_WIN32)
-#include <winsock2.h>
-#else
-#include <sys/select.h>
-#endif
-
-static fd_set read_fds;
-static fd_set write_fds;
-static fd_set error_fds;
-
-static int select_n = 0;
-
-static void fdevent_init(void)
-{
- FD_ZERO(&read_fds);
- FD_ZERO(&write_fds);
- FD_ZERO(&error_fds);
+static std::string dump_fde(const fdevent* fde) {
+ std::string state;
+ if (fde->state & FDE_ACTIVE) {
+ state += "A";
+ }
+ if (fde->state & FDE_PENDING) {
+ state += "P";
+ }
+ if (fde->state & FDE_CREATED) {
+ state += "C";
+ }
+ if (fde->state & FDE_READ) {
+ state += "R";
+ }
+ if (fde->state & FDE_WRITE) {
+ state += "W";
+ }
+ if (fde->state & FDE_ERROR) {
+ state += "E";
+ }
+ if (fde->state & FDE_DONT_CLOSE) {
+ state += "D";
+ }
+ return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
}
-static void fdevent_connect(fdevent *fde)
-{
- if(fde->fd >= select_n) {
- select_n = fde->fd + 1;
- }
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- int i, n;
-
- FD_CLR(fde->fd, &read_fds);
- FD_CLR(fde->fd, &write_fds);
- FD_CLR(fde->fd, &error_fds);
-
- for(n = 0, i = 0; i < select_n; i++) {
- if(fd_table[i] != 0) n = i;
- }
- select_n = n + 1;
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- if(events & FDE_READ) {
- FD_SET(fde->fd, &read_fds);
- } else {
- FD_CLR(fde->fd, &read_fds);
- }
- if(events & FDE_WRITE) {
- FD_SET(fde->fd, &write_fds);
- } else {
- FD_CLR(fde->fd, &write_fds);
- }
- if(events & FDE_ERROR) {
- FD_SET(fde->fd, &error_fds);
- } else {
- FD_CLR(fde->fd, &error_fds);
- }
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-}
-
-/* Looks at fd_table[] for bad FDs and sets bit in fds.
-** Returns the number of bad FDs.
-*/
-static int fdevent_fd_check(fd_set *fds)
-{
- int i, n = 0;
- fdevent *fde;
-
- for(i = 0; i < select_n; i++) {
- fde = fd_table[i];
- if(fde == 0) continue;
- if(fcntl(i, F_GETFL, NULL) < 0) {
- FD_SET(i, fds);
- n++;
- // fde->state |= FDE_DONT_CLOSE;
-
- }
- }
- return n;
-}
-
-#if !DEBUG
-static inline void dump_all_fds(const char* /* extra_msg */) {}
-#else
-static void dump_all_fds(const char *extra_msg)
-{
-int i;
- fdevent *fde;
- // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
- char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
- size_t max_chars = FD_SETSIZE * 6 + 1;
- int printed_out;
-#define SAFE_SPRINTF(...) \
- do { \
- printed_out = snprintf(pb, max_chars, __VA_ARGS__); \
- if (printed_out <= 0) { \
- D("... snprintf failed.\n"); \
- return; \
- } \
- if (max_chars < (unsigned int)printed_out) { \
- D("... snprintf out of space.\n"); \
- return; \
- } \
- pb += printed_out; \
- max_chars -= printed_out; \
- } while(0)
-
- for(i = 0; i < select_n; i++) {
- fde = fd_table[i];
- SAFE_SPRINTF("%d", i);
- if(fde == 0) {
- SAFE_SPRINTF("? ");
- continue;
- }
- if(fcntl(i, F_GETFL, NULL) < 0) {
- SAFE_SPRINTF("b");
- }
- SAFE_SPRINTF(" ");
- }
- D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
-}
-#endif
-
-static void fdevent_process()
-{
- int i, n;
- fdevent *fde;
- unsigned events;
- fd_set rfd, wfd, efd;
-
- memcpy(&rfd, &read_fds, sizeof(fd_set));
- memcpy(&wfd, &write_fds, sizeof(fd_set));
- memcpy(&efd, &error_fds, sizeof(fd_set));
-
- dump_all_fds("pre select()");
-
- n = select(select_n, &rfd, &wfd, &efd, NULL);
- int saved_errno = errno;
- D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0);
-
- dump_all_fds("post select()");
-
- if(n < 0) {
- switch(saved_errno) {
- case EINTR: return;
- case EBADF:
- // Can't trust the FD sets after an error.
- FD_ZERO(&wfd);
- FD_ZERO(&efd);
- FD_ZERO(&rfd);
- break;
- default:
- D("Unexpected select() error=%d\n", saved_errno);
- return;
- }
- }
- if(n <= 0) {
- // We fake a read, as the rest of the code assumes
- // that errors will be detected at that point.
- n = fdevent_fd_check(&rfd);
- }
-
- for(i = 0; (i < select_n) && (n > 0); i++) {
- events = 0;
- if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
- if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
- if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
-
- if(events) {
- fde = fd_table[i];
- if(fde == 0)
- FATAL("missing fde for fd %d\n", i);
-
- fde->events |= events;
-
- D("got events fde->fd=%d events=%04x, state=%04x\n",
- fde->fd, fde->events, fde->state);
- if(fde->state & FDE_PENDING) continue;
- fde->state |= FDE_PENDING;
- fdevent_plist_enqueue(fde);
- }
- }
-}
-
-#endif
-
-static void fdevent_register(fdevent *fde)
-{
- if(fde->fd < 0) {
- FATAL("bogus negative fd (%d)\n", fde->fd);
- }
-
- if(fde->fd >= fd_table_max) {
- int oldmax = fd_table_max;
- if(fde->fd > 32000) {
- FATAL("bogus huuuuge fd (%d)\n", fde->fd);
- }
- if(fd_table_max == 0) {
- fdevent_init();
- fd_table_max = 256;
- }
- while(fd_table_max <= fde->fd) {
- fd_table_max *= 2;
- }
- fd_table = reinterpret_cast<fdevent**>(
- realloc(fd_table, sizeof(fdevent*) * fd_table_max));
- if(fd_table == 0) {
- FATAL("could not expand fd_table to %d entries\n", fd_table_max);
- }
- memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
- }
-
- fd_table[fde->fd] = fde;
-}
-
-static void fdevent_unregister(fdevent *fde)
-{
- if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
- FATAL("fd out of range (%d)\n", fde->fd);
- }
-
- if(fd_table[fde->fd] != fde) {
- FATAL("fd_table out of sync [%d]\n", fde->fd);
- }
-
- fd_table[fde->fd] = 0;
-
- if(!(fde->state & FDE_DONT_CLOSE)) {
- dump_fde(fde, "close");
- adb_close(fde->fd);
- }
-}
-
-static void fdevent_plist_enqueue(fdevent *node)
-{
- fdevent *list = &list_pending;
-
- node->next = list;
- node->prev = list->prev;
- node->prev->next = node;
- list->prev = node;
-}
-
-static void fdevent_plist_remove(fdevent *node)
-{
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->next = 0;
- node->prev = 0;
-}
-
-static fdevent *fdevent_plist_dequeue(void)
-{
- fdevent *list = &list_pending;
- fdevent *node = list->next;
-
- if(node == list) return 0;
-
- list->next = node->next;
- list->next->prev = list;
- node->next = 0;
- node->prev = 0;
-
- return node;
-}
-
-static void fdevent_call_fdfunc(fdevent* fde)
-{
- unsigned events = fde->events;
- fde->events = 0;
- if(!(fde->state & FDE_PENDING)) return;
- fde->state &= (~FDE_PENDING);
- dump_fde(fde, "callback");
- fde->func(fde->fd, events, fde->arg);
-}
-
-#if !ADB_HOST
-static void fdevent_subproc_event_func(int fd, unsigned ev,
- void* /* userdata */)
-{
-
- D("subproc handling on fd=%d ev=%04x\n", fd, ev);
-
- // Hook oneself back into the fde's suitable for select() on read.
- if((fd < 0) || (fd >= fd_table_max)) {
- FATAL("fd %d out of range for fd_table \n", fd);
- }
- fdevent *fde = fd_table[fd];
- fdevent_add(fde, FDE_READ);
-
- if(ev & FDE_READ){
- int subproc_fd;
-
- if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
- FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
- }
- if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
- D("subproc_fd %d out of range 0, fd_table_max=%d\n",
- subproc_fd, fd_table_max);
- return;
- }
- fdevent *subproc_fde = fd_table[subproc_fd];
- if(!subproc_fde) {
- D("subproc_fd %d cleared from fd_table\n", subproc_fd);
- return;
- }
- if(subproc_fde->fd != subproc_fd) {
- // Already reallocated?
- D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
- return;
- }
-
- subproc_fde->force_eof = 1;
-
- int rcount = 0;
- ioctl(subproc_fd, FIONREAD, &rcount);
- D("subproc with fd=%d has rcount=%d err=%d\n",
- subproc_fd, rcount, errno);
-
- if(rcount) {
- // If there is data left, it will show up in the select().
- // This works because there is no other thread reading that
- // data when in this fd_func().
- return;
- }
-
- D("subproc_fde.state=%04x\n", subproc_fde->state);
- subproc_fde->events |= FDE_READ;
- if(subproc_fde->state & FDE_PENDING) {
- return;
- }
- subproc_fde->state |= FDE_PENDING;
- fdevent_call_fdfunc(subproc_fde);
- }
-}
-
-void fdevent_subproc_setup()
-{
- int s[2];
-
- if(adb_socketpair(s)) {
- FATAL("cannot create shell-exit socket-pair\n");
- }
- D("socketpair: (%d,%d)\n", s[0], s[1]);
-
- SHELL_EXIT_NOTIFY_FD = s[0];
- fdevent *fde;
- fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
- if(!fde)
- FATAL("cannot create fdevent for shell-exit handler\n");
- fdevent_add(fde, FDE_READ);
-}
-#endif // !ADB_HOST
-
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);
@@ -600,101 +124,275 @@
void fdevent_destroy(fdevent *fde)
{
+ check_main_thread();
if(fde == 0) return;
if(!(fde->state & FDE_CREATED)) {
- FATAL("fde %p not created by fdevent_create()\n", fde);
+ LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
}
fdevent_remove(fde);
free(fde);
}
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
-{
+void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
+ check_main_thread();
+ CHECK_GE(fd, 0);
memset(fde, 0, sizeof(fdevent));
fde->state = FDE_ACTIVE;
fde->fd = fd;
- fde->force_eof = 0;
fde->func = func;
fde->arg = arg;
-
-#if !defined(_WIN32)
- fcntl(fd, F_SETFL, O_NONBLOCK);
-#endif
- fdevent_register(fde);
- dump_fde(fde, "connect");
- fdevent_connect(fde);
- fde->state |= FDE_ACTIVE;
+ if (!set_file_block_mode(fd, false)) {
+ // Here is not proper to handle the error. If it fails here, some error is
+ // likely to be detected by poll(), then we can let the callback function
+ // to handle it.
+ LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
+ }
+ auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
+ CHECK(pair.second) << "install existing fd " << fd;
+ D("fdevent_install %s", dump_fde(fde).c_str());
}
-void fdevent_remove(fdevent *fde)
-{
- if(fde->state & FDE_PENDING) {
- fdevent_plist_remove(fde);
+void fdevent_remove(fdevent* fde) {
+ check_main_thread();
+ D("fdevent_remove %s", dump_fde(fde).c_str());
+ if (fde->state & FDE_ACTIVE) {
+ g_poll_node_map.erase(fde->fd);
+ if (fde->state & FDE_PENDING) {
+ g_pending_list.remove(fde);
+ }
+ if (!(fde->state & FDE_DONT_CLOSE)) {
+ adb_close(fde->fd);
+ fde->fd = -1;
+ }
+ fde->state = 0;
+ fde->events = 0;
}
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_disconnect(fde);
- dump_fde(fde, "disconnect");
- fdevent_unregister(fde);
- }
-
- fde->state = 0;
- fde->events = 0;
}
-
-void fdevent_set(fdevent *fde, unsigned events)
-{
- events &= FDE_EVENTMASK;
-
- if((fde->state & FDE_EVENTMASK) == events) return;
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_update(fde, events);
- dump_fde(fde, "update");
+static void fdevent_update(fdevent* fde, unsigned events) {
+ auto it = g_poll_node_map.find(fde->fd);
+ CHECK(it != g_poll_node_map.end());
+ PollNode& node = it->second;
+ if (events & FDE_READ) {
+ node.pollfd.events |= POLLIN;
+ } else {
+ node.pollfd.events &= ~POLLIN;
}
+ if (events & FDE_WRITE) {
+ node.pollfd.events |= POLLOUT;
+ } else {
+ node.pollfd.events &= ~POLLOUT;
+ }
fde->state = (fde->state & FDE_STATEMASK) | events;
+}
- if(fde->state & FDE_PENDING) {
- /* if we're pending, make sure
- ** we don't signal an event that
- ** is no longer wanted.
- */
- fde->events &= (~events);
- if(fde->events == 0) {
- fdevent_plist_remove(fde);
- fde->state &= (~FDE_PENDING);
+void fdevent_set(fdevent* fde, unsigned events) {
+ check_main_thread();
+ events &= FDE_EVENTMASK;
+ if ((fde->state & FDE_EVENTMASK) == events) {
+ return;
+ }
+ CHECK(fde->state & FDE_ACTIVE);
+ fdevent_update(fde, events);
+ D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
+
+ if (fde->state & FDE_PENDING) {
+ // If we are pending, make sure we don't signal an event that is no longer wanted.
+ fde->events &= events;
+ if (fde->events == 0) {
+ g_pending_list.remove(fde);
+ fde->state &= ~FDE_PENDING;
}
}
}
-void fdevent_add(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+void fdevent_add(fdevent* fde, unsigned events) {
+ check_main_thread();
+ fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
}
-void fdevent_del(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+void fdevent_del(fdevent* fde, unsigned events) {
+ check_main_thread();
+ fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
}
+static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
+ std::string result;
+ for (const auto& pollfd : pollfds) {
+ std::string op;
+ if (pollfd.events & POLLIN) {
+ op += "R";
+ }
+ if (pollfd.events & POLLOUT) {
+ op += "W";
+ }
+ android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
+ }
+ return result;
+}
+
+static void fdevent_process() {
+ std::vector<adb_pollfd> pollfds;
+ for (const auto& pair : g_poll_node_map) {
+ pollfds.push_back(pair.second.pollfd);
+ }
+ CHECK_GT(pollfds.size(), 0u);
+ D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+ int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
+ if (ret == -1) {
+ PLOG(ERROR) << "poll(), ret = " << ret;
+ return;
+ }
+ for (const auto& pollfd : pollfds) {
+ if (pollfd.revents != 0) {
+ D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
+ }
+ unsigned events = 0;
+ if (pollfd.revents & POLLIN) {
+ events |= FDE_READ;
+ }
+ if (pollfd.revents & POLLOUT) {
+ events |= FDE_WRITE;
+ }
+ if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ // We fake a read, as the rest of the code assumes that errors will
+ // be detected at that point.
+ events |= FDE_READ | FDE_ERROR;
+ }
+#if defined(__linux__)
+ if (pollfd.revents & POLLRDHUP) {
+ events |= FDE_READ | FDE_ERROR;
+ }
+#endif
+ if (events != 0) {
+ auto it = g_poll_node_map.find(pollfd.fd);
+ CHECK(it != g_poll_node_map.end());
+ fdevent* fde = it->second.fde;
+ CHECK_EQ(fde->fd, pollfd.fd);
+ fde->events |= events;
+ D("%s got events %x", dump_fde(fde).c_str(), events);
+ fde->state |= FDE_PENDING;
+ g_pending_list.push_back(fde);
+ }
+ }
+}
+
+static void fdevent_call_fdfunc(fdevent* fde)
+{
+ unsigned events = fde->events;
+ fde->events = 0;
+ CHECK(fde->state & FDE_PENDING);
+ fde->state &= (~FDE_PENDING);
+ D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
+ fde->func(fde->fd, events, fde->arg);
+}
+
+#if !ADB_HOST
+
+#include <sys/ioctl.h>
+
+static void fdevent_subproc_event_func(int fd, unsigned ev,
+ void* /* userdata */)
+{
+
+ D("subproc handling on fd = %d, ev = %x", fd, ev);
+
+ CHECK_GE(fd, 0);
+
+ if (ev & FDE_READ) {
+ int subproc_fd;
+
+ if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
+ LOG(FATAL) << "Failed to read the subproc's fd from " << fd;
+ }
+ auto it = g_poll_node_map.find(subproc_fd);
+ if (it == g_poll_node_map.end()) {
+ D("subproc_fd %d cleared from fd_table", subproc_fd);
+ adb_close(subproc_fd);
+ return;
+ }
+ fdevent* subproc_fde = it->second.fde;
+ if(subproc_fde->fd != subproc_fd) {
+ // Already reallocated?
+ LOG(FATAL) << "subproc_fd(" << subproc_fd << ") != subproc_fde->fd(" << subproc_fde->fd
+ << ")";
+ return;
+ }
+
+ subproc_fde->force_eof = 1;
+
+ int rcount = 0;
+ ioctl(subproc_fd, FIONREAD, &rcount);
+ D("subproc with fd %d has rcount=%d, err=%d", subproc_fd, rcount, errno);
+ if (rcount != 0) {
+ // If there is data left, it will show up in the select().
+ // This works because there is no other thread reading that
+ // data when in this fd_func().
+ return;
+ }
+
+ D("subproc_fde %s", dump_fde(subproc_fde).c_str());
+ subproc_fde->events |= FDE_READ;
+ if(subproc_fde->state & FDE_PENDING) {
+ return;
+ }
+ subproc_fde->state |= FDE_PENDING;
+ fdevent_call_fdfunc(subproc_fde);
+ }
+}
+
+void fdevent_subproc_setup()
+{
+ int s[2];
+
+ if(adb_socketpair(s)) {
+ PLOG(FATAL) << "cannot create shell-exit socket-pair";
+ }
+ D("fdevent_subproc: socket pair (%d, %d)", s[0], s[1]);
+
+ SHELL_EXIT_NOTIFY_FD = s[0];
+ fdevent *fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
+ CHECK(fde != nullptr) << "cannot create fdevent for shell-exit handler";
+ fdevent_add(fde, FDE_READ);
+}
+#endif // !ADB_HOST
+
void fdevent_loop()
{
- fdevent *fde;
+ set_main_thread();
#if !ADB_HOST
fdevent_subproc_setup();
#endif // !ADB_HOST
- for(;;) {
- D("--- ---- waiting for events\n");
+ while (true) {
+ if (terminate_loop) {
+ return;
+ }
+
+ D("--- --- waiting for events");
fdevent_process();
- while((fde = fdevent_plist_dequeue())) {
+ while (!g_pending_list.empty()) {
+ fdevent* fde = g_pending_list.front();
+ g_pending_list.pop_front();
fdevent_call_fdfunc(fde);
}
}
}
+
+void fdevent_terminate_loop() {
+ terminate_loop = true;
+}
+
+size_t fdevent_installed_count() {
+ return g_poll_node_map.size();
+}
+
+void fdevent_reset() {
+ g_poll_node_map.clear();
+ g_pending_list.clear();
+ main_thread_valid = false;
+ terminate_loop = false;
+}
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 8d84b29..207f9b7 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -17,21 +17,33 @@
#ifndef __FDEVENT_H
#define __FDEVENT_H
+#include <stddef.h>
#include <stdint.h> /* for int64_t */
/* events that may be observed */
#define FDE_READ 0x0001
#define FDE_WRITE 0x0002
#define FDE_ERROR 0x0004
-#define FDE_TIMEOUT 0x0008
/* features that may be set (via the events set/add/del interface) */
#define FDE_DONT_CLOSE 0x0080
-struct fdevent;
-
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+struct fdevent {
+ fdevent *next;
+ fdevent *prev;
+
+ int fd;
+ int force_eof;
+
+ uint16_t state;
+ uint16_t events;
+
+ fd_func func;
+ void *arg;
+};
+
/* Allocate and initialize a new fdevent object
* Note: use FD_TIMER as 'fd' to create a fd-less object
* (used to implement timers).
@@ -64,18 +76,9 @@
*/
void fdevent_loop();
-struct fdevent {
- fdevent *next;
- fdevent *prev;
-
- int fd;
- int force_eof;
-
- uint16_t state;
- uint16_t events;
-
- fd_func func;
- void *arg;
-};
+// The following functions are used only for tests.
+void fdevent_terminate_loop();
+size_t fdevent_installed_count();
+void fdevent_reset();
#endif
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
new file mode 100644
index 0000000..c933ed5
--- /dev/null
+++ b/adb/fdevent_test.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fdevent.h"
+
+#include <gtest/gtest.h>
+
+#include <limits>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "adb_io.h"
+#include "fdevent_test.h"
+
+class FdHandler {
+ public:
+ FdHandler(int read_fd, int write_fd) : read_fd_(read_fd), write_fd_(write_fd) {
+ fdevent_install(&read_fde_, read_fd_, FdEventCallback, this);
+ fdevent_add(&read_fde_, FDE_READ);
+ fdevent_install(&write_fde_, write_fd_, FdEventCallback, this);
+ }
+
+ ~FdHandler() {
+ fdevent_remove(&read_fde_);
+ fdevent_remove(&write_fde_);
+ }
+
+ private:
+ static void FdEventCallback(int fd, unsigned events, void* userdata) {
+ FdHandler* handler = reinterpret_cast<FdHandler*>(userdata);
+ ASSERT_EQ(0u, (events & ~(FDE_READ | FDE_WRITE))) << "unexpected events: " << events;
+ if (events & FDE_READ) {
+ ASSERT_EQ(fd, handler->read_fd_);
+ char c;
+ ASSERT_EQ(1, adb_read(fd, &c, 1));
+ handler->queue_.push(c);
+ fdevent_add(&handler->write_fde_, FDE_WRITE);
+ }
+ if (events & FDE_WRITE) {
+ ASSERT_EQ(fd, handler->write_fd_);
+ ASSERT_FALSE(handler->queue_.empty());
+ char c = handler->queue_.front();
+ handler->queue_.pop();
+ ASSERT_EQ(1, adb_write(fd, &c, 1));
+ if (handler->queue_.empty()) {
+ fdevent_del(&handler->write_fde_, FDE_WRITE);
+ }
+ }
+ }
+
+ private:
+ const int read_fd_;
+ const int write_fd_;
+ fdevent read_fde_;
+ fdevent write_fde_;
+ std::queue<char> queue_;
+};
+
+struct ThreadArg {
+ int first_read_fd;
+ int last_write_fd;
+ size_t middle_pipe_count;
+};
+
+TEST_F(FdeventTest, fdevent_terminate) {
+ adb_thread_t thread;
+ PrepareThread();
+ ASSERT_TRUE(adb_thread_create([](void*) { fdevent_loop(); }, nullptr, &thread));
+ TerminateThread(thread);
+}
+
+static void FdEventThreadFunc(ThreadArg* arg) {
+ std::vector<int> read_fds;
+ std::vector<int> write_fds;
+
+ read_fds.push_back(arg->first_read_fd);
+ for (size_t i = 0; i < arg->middle_pipe_count; ++i) {
+ int fds[2];
+ ASSERT_EQ(0, adb_socketpair(fds));
+ read_fds.push_back(fds[0]);
+ write_fds.push_back(fds[1]);
+ }
+ write_fds.push_back(arg->last_write_fd);
+
+ std::vector<std::unique_ptr<FdHandler>> fd_handlers;
+ for (size_t i = 0; i < read_fds.size(); ++i) {
+ fd_handlers.push_back(std::unique_ptr<FdHandler>(new FdHandler(read_fds[i], write_fds[i])));
+ }
+
+ fdevent_loop();
+}
+
+TEST_F(FdeventTest, smoke) {
+ const size_t PIPE_COUNT = 10;
+ const size_t MESSAGE_LOOP_COUNT = 100;
+ const std::string MESSAGE = "fdevent_test";
+ int fd_pair1[2];
+ int fd_pair2[2];
+ ASSERT_EQ(0, adb_socketpair(fd_pair1));
+ ASSERT_EQ(0, adb_socketpair(fd_pair2));
+ adb_thread_t thread;
+ ThreadArg thread_arg;
+ thread_arg.first_read_fd = fd_pair1[0];
+ thread_arg.last_write_fd = fd_pair2[1];
+ thread_arg.middle_pipe_count = PIPE_COUNT;
+ int writer = fd_pair1[1];
+ int reader = fd_pair2[0];
+
+ PrepareThread();
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(FdEventThreadFunc), &thread_arg,
+ &thread));
+
+ for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
+ std::string read_buffer = MESSAGE;
+ std::string write_buffer(MESSAGE.size(), 'a');
+ ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
+ ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
+ ASSERT_EQ(read_buffer, write_buffer);
+ }
+
+ TerminateThread(thread);
+ ASSERT_EQ(0, adb_close(writer));
+ ASSERT_EQ(0, adb_close(reader));
+}
+
+struct InvalidFdArg {
+ fdevent fde;
+ unsigned expected_events;
+ size_t* happened_event_count;
+};
+
+static void InvalidFdEventCallback(int fd, unsigned events, void* userdata) {
+ InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
+ ASSERT_EQ(arg->expected_events, events);
+ fdevent_remove(&arg->fde);
+ if (++*(arg->happened_event_count) == 2) {
+ fdevent_terminate_loop();
+ }
+}
+
+static void InvalidFdThreadFunc(void*) {
+ const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
+ size_t happened_event_count = 0;
+ InvalidFdArg read_arg;
+ read_arg.expected_events = FDE_READ | FDE_ERROR;
+ read_arg.happened_event_count = &happened_event_count;
+ fdevent_install(&read_arg.fde, 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);
+ fdevent_loop();
+}
+
+TEST_F(FdeventTest, invalid_fd) {
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(InvalidFdThreadFunc, nullptr, &thread));
+ ASSERT_TRUE(adb_thread_join(thread));
+}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
new file mode 100644
index 0000000..ef65b74
--- /dev/null
+++ b/adb/fdevent_test.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "socket.h"
+#include "sysdeps.h"
+
+class FdeventTest : public ::testing::Test {
+ protected:
+ int dummy = -1;
+
+ static void SetUpTestCase() {
+#if !defined(_WIN32)
+ ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
+#endif
+ }
+
+ void SetUp() override {
+ fdevent_reset();
+ ASSERT_EQ(0u, fdevent_installed_count());
+ }
+
+ // Register a dummy socket used to wake up the fdevent loop to tell it to die.
+ void PrepareThread() {
+ int dummy_fds[2];
+ if (adb_socketpair(dummy_fds) != 0) {
+ FAIL() << "failed to create socketpair: " << strerror(errno);
+ }
+
+ asocket* dummy_socket = create_local_socket(dummy_fds[1]);
+ if (!dummy_socket) {
+ FAIL() << "failed to create local socket: " << strerror(errno);
+ }
+ dummy_socket->ready(dummy_socket);
+ dummy = dummy_fds[0];
+ }
+
+ size_t GetAdditionalLocalSocketCount() {
+#if ADB_HOST
+ // dummy socket installed in PrepareThread()
+ return 1;
+#else
+ // dummy socket and one more socket installed in fdevent_subproc_setup()
+ return 2;
+#endif
+ }
+
+ void TerminateThread(adb_thread_t thread) {
+ fdevent_terminate_loop();
+ ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
+ ASSERT_TRUE(adb_thread_join(thread));
+ ASSERT_EQ(0, adb_close(dummy));
+ }
+};
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index da80013..271943d 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -15,18 +15,24 @@
*/
#include <dirent.h>
-#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
+#include <unistd.h>
#include <utime.h>
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
#include "sysdeps.h"
#include "adb.h"
@@ -34,96 +40,13 @@
#include "adb_io.h"
#include "adb_utils.h"
#include "file_sync_service.h"
+#include "line_printer.h"
+#include "sysdeps/errno.h"
+#include "sysdeps/stat.h"
-#include <base/stringprintf.h>
-
-static unsigned long long total_bytes;
-static long long start_time;
-
-static long long NOW()
-{
- struct timeval tv;
- gettimeofday(&tv, 0);
- return ((long long) tv.tv_usec) +
- 1000000LL * ((long long) tv.tv_sec);
-}
-
-static void BEGIN()
-{
- total_bytes = 0;
- start_time = NOW();
-}
-
-static void END()
-{
- long long t = NOW() - start_time;
- if(total_bytes == 0) return;
-
- if (t == 0) /* prevent division by 0 :-) */
- t = 1000000;
-
- fprintf(stderr,"%lld KB/s (%lld bytes in %lld.%03llds)\n",
- ((total_bytes * 1000000LL) / t) / 1024LL,
- total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
-}
-
-static void print_transfer_progress(uint64_t bytes_current,
- uint64_t bytes_total) {
- if (bytes_total == 0) return;
-
- fprintf(stderr, "\rTransferring: %" PRIu64 "/%" PRIu64 " (%d%%)",
- bytes_current, bytes_total,
- (int) (bytes_current * 100 / bytes_total));
-
- if (bytes_current == bytes_total) {
- fputc('\n', stderr);
- }
-
- fflush(stderr);
-}
-
-static void sync_quit(int fd) {
- syncmsg msg;
-
- msg.req.id = ID_QUIT;
- msg.req.namelen = 0;
-
- WriteFdExactly(fd, &msg.req, sizeof(msg.req));
-}
-
-typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
-
-static int sync_ls(int fd, const char* path, sync_ls_cb func, void* cookie) {
- int len = strlen(path);
- if (len > 1024) goto fail;
-
- syncmsg msg;
- msg.req.id = ID_LIST;
- msg.req.namelen = htoll(len);
-
- if (!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) || !WriteFdExactly(fd, path, len)) {
- goto fail;
- }
-
- for (;;) {
- if (!ReadFdExactly(fd, &msg.dent, sizeof(msg.dent))) break;
- if (msg.dent.id == ID_DONE) return 0;
- if (msg.dent.id != ID_DENT) break;
-
- len = ltohl(msg.dent.namelen);
- if (len > 256) break;
-
- char buf[257];
- if (!ReadFdExactly(fd, buf, len)) break;
- buf[len] = 0;
-
- func(ltohl(msg.dent.mode), ltohl(msg.dent.size), ltohl(msg.dent.time), buf, cookie);
- }
-
-fail:
- adb_close(fd);
- return -1;
-}
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
struct syncsendbuf {
unsigned id;
@@ -131,852 +54,1201 @@
char data[SYNC_DATA_MAX];
};
-static syncsendbuf send_buffer;
-
-static int sync_readtime(int fd, const char* path, unsigned int* timestamp, unsigned int* mode) {
- syncmsg msg;
- int len = strlen(path);
-
- msg.req.id = ID_STAT;
- msg.req.namelen = htoll(len);
-
- if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
- !WriteFdExactly(fd, path, len)) {
- return -1;
+static void ensure_trailing_separators(std::string& local_path, std::string& remote_path) {
+ if (!adb_is_separator(local_path.back())) {
+ local_path.push_back(OS_PATH_SEPARATOR);
}
-
- if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat))) {
- return -1;
+ if (remote_path.back() != '/') {
+ remote_path.push_back('/');
}
-
- if(msg.stat.id != ID_STAT) {
- return -1;
- }
-
- *timestamp = ltohl(msg.stat.time);
- *mode = ltohl(msg.stat.mode);
- return 0;
}
-static int sync_start_readtime(int fd, const char *path)
-{
- syncmsg msg;
- int len = strlen(path);
-
- msg.req.id = ID_STAT;
- msg.req.namelen = htoll(len);
-
- if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
- !WriteFdExactly(fd, path, len)) {
- return -1;
- }
-
- return 0;
+static bool should_pull_file(mode_t mode) {
+ return S_ISREG(mode) || S_ISBLK(mode) || S_ISCHR(mode);
}
-static int sync_finish_readtime(int fd, unsigned int *timestamp,
- unsigned int *mode, unsigned int *size)
-{
- syncmsg msg;
-
- if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat)))
- return -1;
-
- if(msg.stat.id != ID_STAT)
- return -1;
-
- *timestamp = ltohl(msg.stat.time);
- *mode = ltohl(msg.stat.mode);
- *size = ltohl(msg.stat.size);
-
- return 0;
+static bool should_push_file(mode_t mode) {
+ return S_ISREG(mode) || S_ISLNK(mode);
}
-static int sync_readmode(int fd, const char* path, unsigned* mode) {
- syncmsg msg;
- int len = strlen(path);
+struct copyinfo {
+ std::string lpath;
+ std::string rpath;
+ int64_t time = 0;
+ uint32_t mode;
+ uint64_t size = 0;
+ bool skip = false;
- msg.req.id = ID_STAT;
- msg.req.namelen = htoll(len);
-
- if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
- !WriteFdExactly(fd, path, len)) {
- return -1;
- }
-
- if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat))) {
- return -1;
- }
-
- if(msg.stat.id != ID_STAT) {
- return -1;
- }
-
- *mode = ltohl(msg.stat.mode);
- return 0;
-}
-
-static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, bool show_progress)
-{
- int lfd, err = 0;
- unsigned long long size = 0;
-
- lfd = adb_open(path, O_RDONLY);
- if(lfd < 0) {
- fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno));
- return -1;
- }
-
- if (show_progress) {
- // Determine local file size.
- struct stat st;
- if (stat(path, &st)) {
- fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno));
- return -1;
- }
-
- size = st.st_size;
- }
-
- sbuf->id = ID_DATA;
- for(;;) {
- int ret;
-
- ret = adb_read(lfd, sbuf->data, SYNC_DATA_MAX);
- if(!ret)
- break;
-
- if(ret < 0) {
- if(errno == EINTR)
- continue;
- fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno));
- break;
- }
-
- sbuf->size = htoll(ret);
- if(!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + ret)){
- err = -1;
- break;
- }
- total_bytes += ret;
-
- if (show_progress) {
- print_transfer_progress(total_bytes, size);
+ copyinfo(const std::string& local_path,
+ const std::string& remote_path,
+ const std::string& name,
+ unsigned int mode)
+ : lpath(local_path), rpath(remote_path), mode(mode) {
+ ensure_trailing_separators(lpath, rpath);
+ lpath.append(name);
+ rpath.append(name);
+ if (S_ISDIR(mode)) {
+ ensure_trailing_separators(lpath, rpath);
}
}
-
- adb_close(lfd);
- return err;
-}
-
-static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf,
- bool show_progress)
-{
- int err = 0;
- int total = 0;
-
- sbuf->id = ID_DATA;
- while (total < size) {
- int count = size - total;
- if (count > SYNC_DATA_MAX) {
- count = SYNC_DATA_MAX;
- }
-
- memcpy(sbuf->data, &file_buffer[total], count);
- sbuf->size = htoll(count);
- if(!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + count)){
- err = -1;
- break;
- }
- total += count;
- total_bytes += count;
-
- if (show_progress) {
- print_transfer_progress(total, size);
- }
- }
-
- return err;
-}
-
-#if defined(_WIN32)
-extern int write_data_link(int fd, const char *path, syncsendbuf *sbuf) __attribute__((error("no symlinks on Windows")));
-#else
-static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
-{
- int len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
- if (len < 0) {
- fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
- return -1;
- }
- sbuf->data[len] = '\0';
-
- sbuf->size = htoll(len + 1);
- sbuf->id = ID_DATA;
-
- if (!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + len + 1)) {
- return -1;
- }
-
- total_bytes += len + 1;
-
- return 0;
-}
-#endif
-
-static int sync_send(int fd, const char *lpath, const char *rpath,
- unsigned mtime, mode_t mode, bool show_progress)
-{
- syncmsg msg;
- int len, r;
- syncsendbuf *sbuf = &send_buffer;
- char* file_buffer = NULL;
- int size = 0;
- char tmp[64];
-
- len = strlen(rpath);
- if(len > 1024) goto fail;
-
- snprintf(tmp, sizeof(tmp), ",%d", mode);
- r = strlen(tmp);
-
- msg.req.id = ID_SEND;
- msg.req.namelen = htoll(len + r);
-
- if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
- !WriteFdExactly(fd, rpath, len) || !WriteFdExactly(fd, tmp, r)) {
- free(file_buffer);
- goto fail;
- }
-
- if (file_buffer) {
- write_data_buffer(fd, file_buffer, size, sbuf, show_progress);
- free(file_buffer);
- } else if (S_ISREG(mode))
- write_data_file(fd, lpath, sbuf, show_progress);
- else if (S_ISLNK(mode))
- write_data_link(fd, lpath, sbuf);
- else
- goto fail;
-
- msg.data.id = ID_DONE;
- msg.data.size = htoll(mtime);
- if(!WriteFdExactly(fd, &msg.data, sizeof(msg.data)))
- goto fail;
-
- if(!ReadFdExactly(fd, &msg.status, sizeof(msg.status)))
- return -1;
-
- if(msg.status.id != ID_OKAY) {
- if(msg.status.id == ID_FAIL) {
- len = ltohl(msg.status.msglen);
- if(len > 256) len = 256;
- if(!ReadFdExactly(fd, sbuf->data, len)) {
- return -1;
- }
- sbuf->data[len] = 0;
- } else
- strcpy(sbuf->data, "unknown reason");
-
- fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
- return -1;
- }
-
- return 0;
-
-fail:
- fprintf(stderr,"protocol failure\n");
- adb_close(fd);
- return -1;
-}
-
-static int sync_recv(int fd, const char* rpath, const char* lpath, bool show_progress) {
- syncmsg msg;
- int len;
- int lfd = -1;
- char *buffer = send_buffer.data;
- unsigned id;
- unsigned long long size = 0;
-
- len = strlen(rpath);
- if(len > 1024) return -1;
-
- if (show_progress) {
- // Determine remote file size.
- syncmsg stat_msg;
- stat_msg.req.id = ID_STAT;
- stat_msg.req.namelen = htoll(len);
-
- if (!WriteFdExactly(fd, &stat_msg.req, sizeof(stat_msg.req)) ||
- !WriteFdExactly(fd, rpath, len)) {
- return -1;
- }
-
- if (!ReadFdExactly(fd, &stat_msg.stat, sizeof(stat_msg.stat))) {
- return -1;
- }
-
- if (stat_msg.stat.id != ID_STAT) return -1;
-
- size = ltohl(stat_msg.stat.size);
- }
-
- msg.req.id = ID_RECV;
- msg.req.namelen = htoll(len);
- if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
- !WriteFdExactly(fd, rpath, len)) {
- return -1;
- }
-
- if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
- return -1;
- }
- id = msg.data.id;
-
- if((id == ID_DATA) || (id == ID_DONE)) {
- adb_unlink(lpath);
- mkdirs(lpath);
- lfd = adb_creat(lpath, 0644);
- if(lfd < 0) {
- fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno));
- return -1;
- }
- goto handle_data;
- } else {
- goto remote_error;
- }
-
- for(;;) {
- if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
- return -1;
- }
- id = msg.data.id;
-
- handle_data:
- len = ltohl(msg.data.size);
- if(id == ID_DONE) break;
- if(id != ID_DATA) goto remote_error;
- if(len > SYNC_DATA_MAX) {
- fprintf(stderr,"data overrun\n");
- adb_close(lfd);
- return -1;
- }
-
- if(!ReadFdExactly(fd, buffer, len)) {
- adb_close(lfd);
- return -1;
- }
-
- if(!WriteFdExactly(lfd, buffer, len)) {
- fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno));
- adb_close(lfd);
- return -1;
- }
-
- total_bytes += len;
-
- if (show_progress) {
- print_transfer_progress(total_bytes, size);
- }
- }
-
- adb_close(lfd);
- return 0;
-
-remote_error:
- adb_close(lfd);
- adb_unlink(lpath);
-
- if(id == ID_FAIL) {
- len = ltohl(msg.data.size);
- if(len > 256) len = 256;
- if(!ReadFdExactly(fd, buffer, len)) {
- return -1;
- }
- buffer[len] = 0;
- } else {
- memcpy(buffer, &id, 4);
- buffer[4] = 0;
- }
- fprintf(stderr,"failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
- return 0;
-}
-
-/* --- */
-static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
- const char *name, void *cookie)
-{
- printf("%08x %08x %08x %s\n", mode, size, time, name);
-}
-
-int do_sync_ls(const char* path) {
- std::string error;
- int fd = adb_connect("sync:", &error);
- if (fd < 0) {
- fprintf(stderr,"error: %s\n", error.c_str());
- return 1;
- }
-
- if (sync_ls(fd, path, do_sync_ls_cb, 0)) {
- return 1;
- }
-
- sync_quit(fd);
- return 0;
-}
-
-struct copyinfo
-{
- copyinfo *next;
- const char *src;
- const char *dst;
- unsigned int time;
- unsigned int mode;
- unsigned int size;
- int flag;
};
-static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* name, int isdir) {
- int slen = strlen(spath);
- int dlen = strlen(dpath);
- int nlen = strlen(name);
- int ssize = slen + nlen + 2;
- int dsize = dlen + nlen + 2;
+enum class TransferDirection {
+ push,
+ pull,
+};
- copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
- if(ci == 0) {
- fprintf(stderr,"out of memory\n");
- abort();
+struct TransferLedger {
+ std::chrono::steady_clock::time_point start_time;
+ uint64_t files_transferred;
+ uint64_t files_skipped;
+ uint64_t bytes_transferred;
+ uint64_t bytes_expected;
+ bool expect_multiple_files;
+
+ TransferLedger() {
+ Reset();
}
- ci->next = 0;
- ci->time = 0;
- ci->mode = 0;
- ci->size = 0;
- ci->flag = 0;
- ci->src = (const char*)(ci + 1);
- ci->dst = ci->src + ssize;
- snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
- snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
-
- return ci;
-}
-
-
-static int local_build_list(copyinfo **filelist,
- const char *lpath, const char *rpath)
-{
- DIR *d;
- struct dirent *de;
- struct stat st;
- copyinfo *dirlist = 0;
- copyinfo *ci, *next;
-
- d = opendir(lpath);
- if(d == 0) {
- fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
- return -1;
+ bool operator==(const TransferLedger& other) const {
+ return files_transferred == other.files_transferred &&
+ files_skipped == other.files_skipped && bytes_transferred == other.bytes_transferred;
}
- while((de = readdir(d))) {
- char stat_path[PATH_MAX];
- char *name = de->d_name;
+ bool operator!=(const TransferLedger& other) const {
+ return !(*this == other);
+ }
- if(name[0] == '.') {
- if(name[1] == 0) continue;
- if((name[1] == '.') && (name[2] == 0)) continue;
+ void Reset() {
+ start_time = std::chrono::steady_clock::now();
+ files_transferred = 0;
+ files_skipped = 0;
+ bytes_transferred = 0;
+ bytes_expected = 0;
+ }
+
+ std::string TransferRate() {
+ if (bytes_transferred == 0) return "";
+
+ std::chrono::duration<double> duration;
+ duration = std::chrono::steady_clock::now() - start_time;
+
+ double s = duration.count();
+ if (s == 0) {
+ return "";
+ }
+ double rate = (static_cast<double>(bytes_transferred) / s) / (1024 * 1024);
+ return android::base::StringPrintf(" %.1f MB/s (%" PRIu64 " bytes in %.3fs)", rate,
+ bytes_transferred, s);
+ }
+
+ void ReportProgress(LinePrinter& lp, const std::string& file, uint64_t file_copied_bytes,
+ uint64_t file_total_bytes) {
+ char overall_percentage_str[5] = "?";
+ if (bytes_expected != 0 && bytes_transferred <= bytes_expected) {
+ int overall_percentage = static_cast<int>(bytes_transferred * 100 / bytes_expected);
+ // If we're pulling symbolic links, we'll pull the target of the link rather than
+ // just create a local link, and that will cause us to go over 100%.
+ if (overall_percentage <= 100) {
+ snprintf(overall_percentage_str, sizeof(overall_percentage_str), "%d%%",
+ overall_percentage);
+ }
}
- /*
- * We could use d_type if HAVE_DIRENT_D_TYPE is defined, but reiserfs
- * always returns DT_UNKNOWN, so we just use stat() for all cases.
- */
- if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path))
- continue;
- strcpy(stat_path, lpath);
- strcat(stat_path, de->d_name);
-
- if(!lstat(stat_path, &st)) {
- if (S_ISDIR(st.st_mode)) {
- ci = mkcopyinfo(lpath, rpath, name, 1);
- ci->next = dirlist;
- dirlist = ci;
- } else {
- ci = mkcopyinfo(lpath, rpath, name, 0);
- if(lstat(ci->src, &st)) {
- fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
- free(ci);
- closedir(d);
- return -1;
- }
- if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
- fprintf(stderr, "skipping special file '%s'\n", ci->src);
- free(ci);
- } else {
- ci->time = st.st_mtime;
- ci->mode = st.st_mode;
- ci->size = st.st_size;
- ci->next = *filelist;
- *filelist = ci;
- }
- }
+ std::string output;
+ if (file_copied_bytes > file_total_bytes || file_total_bytes == 0) {
+ // This case can happen if we're racing against something that wrote to the file
+ // between our stat and our read, or if we're reading a magic file that lies about
+ // its size. Just show how much we've copied.
+ output = android::base::StringPrintf("[%4s] %s: %" PRId64 "/?", overall_percentage_str,
+ file.c_str(), file_copied_bytes);
} else {
- fprintf(stderr, "cannot lstat '%s': %s\n",stat_path , strerror(errno));
+ // If we're transferring multiple files, we want to know how far through the current
+ // file we are, as well as the overall percentage.
+ if (expect_multiple_files) {
+ int file_percentage = static_cast<int>(file_copied_bytes * 100 / file_total_bytes);
+ output = android::base::StringPrintf("[%4s] %s: %d%%", overall_percentage_str,
+ file.c_str(), file_percentage);
+ } else {
+ output =
+ android::base::StringPrintf("[%4s] %s", overall_percentage_str, file.c_str());
+ }
+ }
+ lp.Print(output, LinePrinter::LineType::INFO);
+ }
+
+ void ReportTransferRate(LinePrinter& lp, const std::string& name, TransferDirection direction) {
+ const char* direction_str = (direction == TransferDirection::push) ? "pushed" : "pulled";
+ std::stringstream ss;
+ if (!name.empty()) {
+ ss << name << ": ";
+ }
+ ss << files_transferred << " file" << ((files_transferred == 1) ? "" : "s") << " "
+ << direction_str << ".";
+ if (files_skipped > 0) {
+ ss << " " << files_skipped << " file" << ((files_skipped == 1) ? "" : "s")
+ << " skipped.";
+ }
+ ss << TransferRate();
+
+ lp.Print(ss.str(), LinePrinter::LineType::INFO);
+ lp.KeepInfoLine();
+ }
+};
+
+class SyncConnection {
+ public:
+ SyncConnection() : expect_done_(false) {
+ max = SYNC_DATA_MAX; // TODO: decide at runtime.
+
+ std::string error;
+ FeatureSet features;
+ if (!adb_get_feature_set(&features, &error)) {
+ fd = -1;
+ Error("failed to get feature set: %s", error.c_str());
+ } else {
+ have_stat_v2_ = CanUseFeature(features, kFeatureStat2);
+ fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ Error("connect failed: %s", error.c_str());
+ }
}
}
- closedir(d);
+ ~SyncConnection() {
+ if (!IsValid()) return;
- for(ci = dirlist; ci != 0; ci = next) {
- next = ci->next;
- local_build_list(filelist, ci->src, ci->dst);
- free(ci);
+ if (SendQuit()) {
+ // We sent a quit command, so the server should be doing orderly
+ // shutdown soon. But if we encountered an error while we were using
+ // the connection, the server might still be sending data (before
+ // doing orderly shutdown), in which case we won't wait for all of
+ // the data nor the coming orderly shutdown. In the common success
+ // case, this will wait for the server to do orderly shutdown.
+ ReadOrderlyShutdown(fd);
+ }
+ adb_close(fd);
+
+ line_printer_.KeepInfoLine();
}
- return 0;
+ bool IsValid() { return fd >= 0; }
+
+ bool ReceivedError(const char* from, const char* to) {
+ adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+ int rc = adb_poll(&pfd, 1, 0);
+ if (rc < 0) {
+ Error("failed to poll: %s", strerror(errno));
+ return true;
+ }
+ return rc != 0;
+ }
+
+ void NewTransfer() {
+ current_ledger_.Reset();
+ }
+
+ void RecordBytesTransferred(size_t bytes) {
+ current_ledger_.bytes_transferred += bytes;
+ global_ledger_.bytes_transferred += bytes;
+ }
+
+ void RecordFilesTransferred(size_t files) {
+ current_ledger_.files_transferred += files;
+ global_ledger_.files_transferred += files;
+ }
+
+ void RecordFilesSkipped(size_t files) {
+ current_ledger_.files_skipped += files;
+ global_ledger_.files_skipped += files;
+ }
+
+ void ReportProgress(const std::string& file, uint64_t file_copied_bytes,
+ uint64_t file_total_bytes) {
+ current_ledger_.ReportProgress(line_printer_, file, file_copied_bytes, file_total_bytes);
+ }
+
+ void ReportTransferRate(const std::string& file, TransferDirection direction) {
+ current_ledger_.ReportTransferRate(line_printer_, file, direction);
+ }
+
+ void ReportOverallTransferRate(TransferDirection direction) {
+ if (current_ledger_ != global_ledger_) {
+ global_ledger_.ReportTransferRate(line_printer_, "", direction);
+ }
+ }
+
+ bool SendRequest(int id, const char* path_and_mode) {
+ size_t path_length = strlen(path_and_mode);
+ if (path_length > 1024) {
+ Error("SendRequest failed: path too long: %zu", path_length);
+ errno = ENAMETOOLONG;
+ return false;
+ }
+
+ // Sending header and payload in a single write makes a noticeable
+ // difference to "adb sync" performance.
+ std::vector<char> buf(sizeof(SyncRequest) + path_length);
+ SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]);
+ req->id = id;
+ req->path_length = path_length;
+ char* data = reinterpret_cast<char*>(req + 1);
+ memcpy(data, path_and_mode, path_length);
+
+ return WriteFdExactly(fd, &buf[0], buf.size());
+ }
+
+ bool SendStat(const char* path_and_mode) {
+ if (!have_stat_v2_) {
+ errno = ENOTSUP;
+ return false;
+ }
+ return SendRequest(ID_STAT_V2, path_and_mode);
+ }
+
+ bool SendLstat(const char* path_and_mode) {
+ if (have_stat_v2_) {
+ return SendRequest(ID_LSTAT_V2, path_and_mode);
+ } else {
+ return SendRequest(ID_LSTAT_V1, path_and_mode);
+ }
+ }
+
+ bool FinishStat(struct stat* st) {
+ syncmsg msg;
+
+ memset(st, 0, sizeof(*st));
+ if (have_stat_v2_) {
+ if (!ReadFdExactly(fd, &msg.stat_v2, sizeof(msg.stat_v2))) {
+ fatal_errno("protocol fault: failed to read stat response");
+ }
+
+ if (msg.stat_v2.id != ID_LSTAT_V2 && msg.stat_v2.id != ID_STAT_V2) {
+ fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
+ msg.stat_v2.id);
+ }
+
+ if (msg.stat_v2.error != 0) {
+ errno = errno_from_wire(msg.stat_v2.error);
+ return false;
+ }
+
+ st->st_dev = msg.stat_v2.dev;
+ st->st_ino = msg.stat_v2.ino;
+ st->st_mode = msg.stat_v2.mode;
+ st->st_nlink = msg.stat_v2.nlink;
+ st->st_uid = msg.stat_v2.uid;
+ st->st_gid = msg.stat_v2.gid;
+ st->st_size = msg.stat_v2.size;
+ st->st_atime = msg.stat_v2.atime;
+ st->st_mtime = msg.stat_v2.mtime;
+ st->st_ctime = msg.stat_v2.ctime;
+ return true;
+ } else {
+ if (!ReadFdExactly(fd, &msg.stat_v1, sizeof(msg.stat_v1))) {
+ fatal_errno("protocol fault: failed to read stat response");
+ }
+
+ if (msg.stat_v1.id != ID_LSTAT_V1) {
+ fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
+ msg.stat_v1.id);
+ }
+
+ if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.time == 0) {
+ // There's no way for us to know what the error was.
+ errno = ENOPROTOOPT;
+ return false;
+ }
+
+ st->st_mode = msg.stat_v1.mode;
+ st->st_size = msg.stat_v1.size;
+ st->st_ctime = msg.stat_v1.time;
+ st->st_mtime = msg.stat_v1.time;
+ }
+
+ return true;
+ }
+
+ // Sending header, payload, and footer in a single write makes a huge
+ // difference to "adb sync" performance.
+ bool SendSmallFile(const char* path_and_mode,
+ const char* lpath, const char* rpath,
+ unsigned mtime,
+ const char* data, size_t data_length) {
+ size_t path_length = strlen(path_and_mode);
+ if (path_length > 1024) {
+ Error("SendSmallFile failed: path too long: %zu", path_length);
+ errno = ENAMETOOLONG;
+ return false;
+ }
+
+ std::vector<char> buf(sizeof(SyncRequest) + path_length +
+ sizeof(SyncRequest) + data_length +
+ sizeof(SyncRequest));
+ char* p = &buf[0];
+
+ SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
+ req_send->id = ID_SEND;
+ req_send->path_length = path_length;
+ p += sizeof(SyncRequest);
+ memcpy(p, path_and_mode, path_length);
+ p += path_length;
+
+ SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
+ req_data->id = ID_DATA;
+ req_data->path_length = data_length;
+ p += sizeof(SyncRequest);
+ memcpy(p, data, data_length);
+ p += data_length;
+
+ SyncRequest* req_done = reinterpret_cast<SyncRequest*>(p);
+ req_done->id = ID_DONE;
+ req_done->path_length = mtime;
+ p += sizeof(SyncRequest);
+
+ WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
+ expect_done_ = true;
+
+ // RecordFilesTransferred gets called in CopyDone.
+ RecordBytesTransferred(data_length);
+ ReportProgress(rpath, data_length, data_length);
+ return true;
+ }
+
+ bool SendLargeFile(const char* path_and_mode,
+ const char* lpath, const char* rpath,
+ unsigned mtime) {
+ if (!SendRequest(ID_SEND, path_and_mode)) {
+ Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
+ return false;
+ }
+
+ struct stat st;
+ if (stat(lpath, &st) == -1) {
+ Error("cannot stat '%s': %s", lpath, strerror(errno));
+ return false;
+ }
+
+ uint64_t total_size = st.st_size;
+ uint64_t bytes_copied = 0;
+
+ int lfd = adb_open(lpath, O_RDONLY);
+ if (lfd < 0) {
+ Error("opening '%s' locally failed: %s", lpath, strerror(errno));
+ return false;
+ }
+
+ syncsendbuf sbuf;
+ sbuf.id = ID_DATA;
+ while (true) {
+ int bytes_read = adb_read(lfd, sbuf.data, max);
+ if (bytes_read == -1) {
+ Error("reading '%s' locally failed: %s", lpath, strerror(errno));
+ adb_close(lfd);
+ return false;
+ } else if (bytes_read == 0) {
+ break;
+ }
+
+ sbuf.size = bytes_read;
+ WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + bytes_read);
+
+ RecordBytesTransferred(bytes_read);
+ bytes_copied += bytes_read;
+
+ // Check to see if we've received an error from the other side.
+ if (ReceivedError(lpath, rpath)) {
+ break;
+ }
+
+ ReportProgress(rpath, bytes_copied, total_size);
+ }
+
+ adb_close(lfd);
+
+ syncmsg msg;
+ msg.data.id = ID_DONE;
+ msg.data.size = mtime;
+ expect_done_ = true;
+
+ // RecordFilesTransferred gets called in CopyDone.
+ return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
+ }
+
+ bool CopyDone(const char* from, const char* to) {
+ syncmsg msg;
+ if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
+ Error("failed to copy '%s' to '%s': couldn't read from device", from, to);
+ return false;
+ }
+ if (msg.status.id == ID_OKAY) {
+ if (expect_done_) {
+ expect_done_ = false;
+ RecordFilesTransferred(1);
+ return true;
+ } else {
+ Error("failed to copy '%s' to '%s': received premature success", from, to);
+ return true;
+ }
+ }
+ if (msg.status.id != ID_FAIL) {
+ Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
+ return false;
+ }
+ return ReportCopyFailure(from, to, msg);
+ }
+
+ bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
+ std::vector<char> buf(msg.status.msglen + 1);
+ if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
+ Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
+ from, to, strerror(errno));
+ return false;
+ }
+ buf[msg.status.msglen] = 0;
+ Error("failed to copy '%s' to '%s': remote %s", from, to, &buf[0]);
+ return false;
+ }
+
+
+ void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s;
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ line_printer_.Print(s, LinePrinter::INFO);
+ }
+
+ void Println(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s;
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ line_printer_.Print(s, LinePrinter::INFO);
+ line_printer_.KeepInfoLine();
+ }
+
+ void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s = "adb: error: ";
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ line_printer_.Print(s, LinePrinter::ERROR);
+ }
+
+ void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s = "adb: warning: ";
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ line_printer_.Print(s, LinePrinter::WARNING);
+ }
+
+ void ComputeExpectedTotalBytes(const std::vector<copyinfo>& file_list) {
+ current_ledger_.bytes_expected = 0;
+ for (const copyinfo& ci : file_list) {
+ // Unfortunately, this doesn't work for symbolic links, because we'll copy the
+ // target of the link rather than just creating a link. (But ci.size is the link size.)
+ if (!ci.skip) current_ledger_.bytes_expected += ci.size;
+ }
+ current_ledger_.expect_multiple_files = true;
+ }
+
+ void SetExpectedTotalBytes(uint64_t expected_total_bytes) {
+ current_ledger_.bytes_expected = expected_total_bytes;
+ current_ledger_.expect_multiple_files = false;
+ }
+
+ // TODO: add a char[max] buffer here, to replace syncsendbuf...
+ int fd;
+ size_t max;
+
+ private:
+ bool expect_done_;
+ bool have_stat_v2_;
+
+ TransferLedger global_ledger_;
+ TransferLedger current_ledger_;
+ LinePrinter line_printer_;
+
+ bool SendQuit() {
+ return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
+ }
+
+ bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) {
+ if (!WriteFdExactly(fd, data, data_length)) {
+ if (errno == ECONNRESET) {
+ // Assume adbd told us why it was closing the connection, and
+ // try to read failure reason from adbd.
+ syncmsg msg;
+ if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
+ Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno));
+ } else if (msg.status.id != ID_FAIL) {
+ Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id);
+ } else {
+ ReportCopyFailure(from, to, msg);
+ }
+ } else {
+ Error("%zu-byte write failed: %s", data_length, strerror(errno));
+ }
+ _exit(1);
+ }
+ return true;
+ }
+};
+
+typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name);
+
+static bool sync_ls(SyncConnection& sc, const char* path,
+ const std::function<sync_ls_cb>& func) {
+ if (!sc.SendRequest(ID_LIST, path)) return false;
+
+ while (true) {
+ syncmsg msg;
+ if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false;
+
+ if (msg.dent.id == ID_DONE) return true;
+ if (msg.dent.id != ID_DENT) return false;
+
+ size_t len = msg.dent.namelen;
+ if (len > 256) return false; // TODO: resize buffer? continue?
+
+ char buf[257];
+ if (!ReadFdExactly(sc.fd, buf, len)) return false;
+ buf[len] = 0;
+
+ func(msg.dent.mode, msg.dent.size, msg.dent.time, buf);
+ }
}
+static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) {
+ return sc.SendStat(path) && sc.FinishStat(st);
+}
-static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly)
+static bool sync_lstat(SyncConnection& sc, const char* path, struct stat* st) {
+ return sc.SendLstat(path) && sc.FinishStat(st);
+}
+
+static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat* st) {
+ if (sync_stat(sc, path, st)) {
+ return true;
+ }
+
+ if (errno != ENOTSUP) {
+ return false;
+ }
+
+ // Try to emulate the parts we can when talking to older adbds.
+ bool lstat_result = sync_lstat(sc, path, st);
+ if (!lstat_result) {
+ return false;
+ }
+
+ if (S_ISLNK(st->st_mode)) {
+ // If the target is a symlink, figure out whether it's a file or a directory.
+ // Also, zero out the st_size field, since no one actually cares what the path length is.
+ st->st_size = 0;
+ std::string dir_path = path;
+ dir_path.push_back('/');
+ struct stat tmp_st;
+
+ st->st_mode &= ~S_IFMT;
+ if (sync_lstat(sc, dir_path.c_str(), &tmp_st)) {
+ st->st_mode |= S_IFDIR;
+ } else {
+ st->st_mode |= S_IFREG;
+ }
+ }
+ return true;
+}
+
+static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
+ unsigned mtime, mode_t mode)
{
- copyinfo *filelist = 0;
- copyinfo *ci, *next;
- int pushed = 0;
+ std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
+
+ if (S_ISLNK(mode)) {
+#if !defined(_WIN32)
+ char buf[PATH_MAX];
+ ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
+ if (data_length == -1) {
+ sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
+ return false;
+ }
+ buf[data_length++] = '\0';
+
+ if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) {
+ return false;
+ }
+ return sc.CopyDone(lpath, rpath);
+#endif
+ }
+
+ struct stat st;
+ if (stat(lpath, &st) == -1) {
+ sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
+ return false;
+ }
+ if (st.st_size < SYNC_DATA_MAX) {
+ std::string data;
+ if (!android::base::ReadFileToString(lpath, &data, true)) {
+ sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
+ return false;
+ }
+ if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime,
+ data.data(), data.size())) {
+ return false;
+ }
+ } else {
+ if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) {
+ return false;
+ }
+ }
+ return sc.CopyDone(lpath, rpath);
+}
+
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
+ const char* name, uint64_t expected_size) {
+ if (!sc.SendRequest(ID_RECV, rpath)) return false;
+
+ adb_unlink(lpath);
+ int lfd = adb_creat(lpath, 0644);
+ if (lfd < 0) {
+ sc.Error("cannot create '%s': %s", lpath, strerror(errno));
+ return false;
+ }
+
+ uint64_t bytes_copied = 0;
+ while (true) {
+ syncmsg msg;
+ if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+ adb_close(lfd);
+ adb_unlink(lpath);
+ return false;
+ }
+
+ if (msg.data.id == ID_DONE) break;
+
+ if (msg.data.id != ID_DATA) {
+ adb_close(lfd);
+ adb_unlink(lpath);
+ sc.ReportCopyFailure(rpath, lpath, msg);
+ return false;
+ }
+
+ if (msg.data.size > sc.max) {
+ sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
+ adb_close(lfd);
+ adb_unlink(lpath);
+ return false;
+ }
+
+ char buffer[SYNC_DATA_MAX];
+ if (!ReadFdExactly(sc.fd, buffer, msg.data.size)) {
+ adb_close(lfd);
+ adb_unlink(lpath);
+ return false;
+ }
+
+ if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
+ sc.Error("cannot write '%s': %s", lpath, strerror(errno));
+ adb_close(lfd);
+ adb_unlink(lpath);
+ return false;
+ }
+
+ bytes_copied += msg.data.size;
+
+ sc.RecordBytesTransferred(msg.data.size);
+ sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size);
+ }
+
+ sc.RecordFilesTransferred(1);
+ adb_close(lfd);
+ return true;
+}
+
+bool do_sync_ls(const char* path) {
+ SyncConnection sc;
+ if (!sc.IsValid()) return false;
+
+ return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
+ const char* name) {
+ printf("%08x %08x %08x %s\n", mode, size, time, name);
+ });
+}
+
+static bool IsDotOrDotDot(const char* name) {
+ return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
+}
+
+static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
+ const std::string& lpath,
+ const std::string& rpath) {
+ std::vector<copyinfo> dirlist;
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
+ if (!dir) {
+ sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno));
+ return false;
+ }
+
+ bool empty_dir = true;
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ if (IsDotOrDotDot(de->d_name)) {
+ continue;
+ }
+
+ empty_dir = false;
+ std::string stat_path = lpath + de->d_name;
+
+ struct stat st;
+ if (lstat(stat_path.c_str(), &st) == -1) {
+ sc.Error("cannot lstat '%s': %s", stat_path.c_str(),
+ strerror(errno));
+ continue;
+ }
+
+ copyinfo ci(lpath, rpath, de->d_name, st.st_mode);
+ if (S_ISDIR(st.st_mode)) {
+ dirlist.push_back(ci);
+ } else {
+ if (!should_push_file(st.st_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", lpath.c_str(), st.st_mode);
+ ci.skip = true;
+ }
+ ci.time = st.st_mtime;
+ ci.size = st.st_size;
+ file_list->push_back(ci);
+ }
+ }
+
+ // Close this directory and recurse.
+ dir.reset();
+
+ // Add the current directory to the list if it was empty, to ensure that
+ // it gets created.
+ if (empty_dir) {
+ // TODO(b/25566053): Make pushing empty directories work.
+ // TODO(b/25457350): We don't preserve permissions on directories.
+ sc.Warning("skipping empty directory '%s'", lpath.c_str());
+ copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+ ci.skip = true;
+ file_list->push_back(ci);
+ return true;
+ }
+
+ for (const copyinfo& ci : dirlist) {
+ local_build_list(sc, file_list, ci.lpath, ci.rpath);
+ }
+
+ return true;
+}
+
+static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
+ std::string rpath, bool check_timestamps,
+ bool list_only) {
+ sc.NewTransfer();
+
+ // Make sure that both directory paths end in a slash.
+ // Both paths are known to be nonempty, so we don't need to check.
+ ensure_trailing_separators(lpath, rpath);
+
+ // Recursively build the list of files to copy.
+ std::vector<copyinfo> file_list;
int skipped = 0;
-
- if((lpath[0] == 0) || (rpath[0] == 0)) return -1;
- if(lpath[strlen(lpath) - 1] != '/') {
- int tmplen = strlen(lpath)+2;
- char *tmp = reinterpret_cast<char*>(malloc(tmplen));
- if(tmp == 0) return -1;
- snprintf(tmp, tmplen, "%s/",lpath);
- lpath = tmp;
- }
- if(rpath[strlen(rpath) - 1] != '/') {
- int tmplen = strlen(rpath)+2;
- char *tmp = reinterpret_cast<char*>(malloc(tmplen));
- if(tmp == 0) return -1;
- snprintf(tmp, tmplen, "%s/",rpath);
- rpath = tmp;
+ if (!local_build_list(sc, &file_list, lpath, rpath)) {
+ return false;
}
- if(local_build_list(&filelist, lpath, rpath)) {
- return -1;
- }
-
- if(checktimestamps){
- for(ci = filelist; ci != 0; ci = ci->next) {
- if(sync_start_readtime(fd, ci->dst)) {
- return 1;
+ if (check_timestamps) {
+ for (const copyinfo& ci : file_list) {
+ if (!sc.SendLstat(ci.rpath.c_str())) {
+ sc.Error("failed to send lstat");
+ return false;
}
}
- for(ci = filelist; ci != 0; ci = ci->next) {
- unsigned int timestamp, mode, size;
- if(sync_finish_readtime(fd, ×tamp, &mode, &size))
- return 1;
- if(size == ci->size) {
- /* for links, we cannot update the atime/mtime */
- if((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
- (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
- ci->flag = 1;
+ for (copyinfo& ci : file_list) {
+ struct stat st;
+ if (sc.FinishStat(&st)) {
+ if (st.st_size == static_cast<off_t>(ci.size)) {
+ // For links, we cannot update the atime/mtime.
+ if ((S_ISREG(ci.mode & st.st_mode) && st.st_mtime == ci.time) ||
+ (S_ISLNK(ci.mode & st.st_mode) && st.st_mtime >= ci.time)) {
+ ci.skip = true;
+ }
+ }
}
}
}
- for(ci = filelist; ci != 0; ci = next) {
- next = ci->next;
- if(ci->flag == 0) {
- fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
- if(!listonly &&
- sync_send(fd, ci->src, ci->dst, ci->time, ci->mode,
- 0 /* no show progress */)) {
- return 1;
+
+ sc.ComputeExpectedTotalBytes(file_list);
+
+ for (const copyinfo& ci : file_list) {
+ if (!ci.skip) {
+ if (list_only) {
+ sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
+ } else {
+ if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode)) {
+ return false;
+ }
}
- pushed++;
} else {
skipped++;
}
- free(ci);
}
- fprintf(stderr,"%d file%s pushed. %d file%s skipped.\n",
- pushed, (pushed == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s");
-
- return 0;
+ sc.RecordFilesSkipped(skipped);
+ sc.ReportTransferRate(lpath, TransferDirection::push);
+ return true;
}
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
+ SyncConnection sc;
+ if (!sc.IsValid()) return false;
-int do_sync_push(const char* lpath, const char* rpath, bool show_progress) {
- std::string error;
- int fd = adb_connect("sync:", &error);
- if (fd < 0) {
- fprintf(stderr,"error: %s\n", error.c_str());
- return 1;
- }
+ bool success = true;
+ bool dst_exists;
+ bool dst_isdir;
struct stat st;
- if (stat(lpath, &st)) {
- fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno));
- sync_quit(fd);
- return 1;
+ if (sync_stat_fallback(sc, dst, &st)) {
+ dst_exists = true;
+ dst_isdir = S_ISDIR(st.st_mode);
+ } else {
+ if (errno == ENOENT || errno == ENOPROTOOPT) {
+ dst_exists = false;
+ dst_isdir = false;
+ } else {
+ sc.Error("stat failed when trying to push to %s: %s", dst, strerror(errno));
+ return false;
+ }
}
- if (S_ISDIR(st.st_mode)) {
- BEGIN();
- if (copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
- return 1;
+ if (!dst_isdir) {
+ if (srcs.size() > 1) {
+ sc.Error("target '%s' is not a directory", dst);
+ return false;
+ } else {
+ size_t dst_len = strlen(dst);
+
+ // A path that ends with a slash doesn't have to be a directory if
+ // it doesn't exist yet.
+ if (dst[dst_len - 1] == '/' && dst_exists) {
+ sc.Error("failed to access '%s': Not a directory", dst);
+ return false;
+ }
}
- } else {
- unsigned mode;
- if (sync_readmode(fd, rpath, &mode)) {
- return 1;
+ }
+
+ for (const char* src_path : srcs) {
+ const char* dst_path = dst;
+ struct stat st;
+ if (stat(src_path, &st) == -1) {
+ sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
+ success = false;
+ continue;
}
+
+ if (S_ISDIR(st.st_mode)) {
+ std::string dst_dir = dst;
+
+ // If the destination path existed originally, the source directory
+ // should be copied as a child of the destination.
+ if (dst_exists) {
+ if (!dst_isdir) {
+ sc.Error("target '%s' is not a directory", dst);
+ return false;
+ }
+ // dst is a POSIX path, so we don't want to use the sysdeps
+ // helpers here.
+ if (dst_dir.back() != '/') {
+ dst_dir.push_back('/');
+ }
+ dst_dir.append(adb_basename(src_path));
+ }
+
+ success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
+ continue;
+ } else if (!should_push_file(st.st_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
+ continue;
+ }
+
std::string path_holder;
- if ((mode != 0) && S_ISDIR(mode)) {
+ if (dst_isdir) {
// If we're copying a local file to a remote directory,
// we really want to copy to remote_dir + "/" + local_filename.
- path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str());
- rpath = path_holder.c_str();
+ path_holder = dst_path;
+ if (path_holder.back() != '/') {
+ path_holder.push_back('/');
+ }
+ path_holder += adb_basename(src_path);
+ dst_path = path_holder.c_str();
}
- BEGIN();
- if (sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, show_progress)) {
- return 1;
- }
+
+ sc.NewTransfer();
+ sc.SetExpectedTotalBytes(st.st_size);
+ success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
+ sc.ReportTransferRate(src_path, TransferDirection::push);
}
- END();
- sync_quit(fd);
- return 0;
+ sc.ReportOverallTransferRate(TransferDirection::push);
+ return success;
}
+static bool remote_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
+ const std::string& rpath, const std::string& lpath) {
+ std::vector<copyinfo> dirlist;
+ std::vector<copyinfo> linklist;
-struct sync_ls_build_list_cb_args {
- copyinfo **filelist;
- copyinfo **dirlist;
- const char *rpath;
- const char *lpath;
-};
+ // Add an entry for the current directory to ensure it gets created before pulling its contents.
+ copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+ file_list->push_back(ci);
-static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
- const char* name, void* cookie)
-{
- sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
- copyinfo *ci;
-
- if (S_ISDIR(mode)) {
- copyinfo **dirlist = args->dirlist;
-
- /* Don't try recursing down "." or ".." */
- if (name[0] == '.') {
- if (name[1] == '\0') return;
- if ((name[1] == '.') && (name[2] == '\0')) return;
+ // Put the files/dirs in rpath on the lists.
+ auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) {
+ if (IsDotOrDotDot(name)) {
+ return;
}
- ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
- ci->next = *dirlist;
- *dirlist = ci;
- } else if (S_ISREG(mode) || S_ISLNK(mode)) {
- copyinfo **filelist = args->filelist;
-
- ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
- ci->time = time;
- ci->mode = mode;
- ci->size = size;
- ci->next = *filelist;
- *filelist = ci;
- } else {
- fprintf(stderr, "skipping special file '%s'\n", name);
- }
-}
-
-static int remote_build_list(int syncfd, copyinfo **filelist,
- const char *rpath, const char *lpath)
-{
- copyinfo *dirlist = NULL;
- sync_ls_build_list_cb_args args;
-
- args.filelist = filelist;
- args.dirlist = &dirlist;
- args.rpath = rpath;
- args.lpath = lpath;
-
- /* Put the files/dirs in rpath on the lists. */
- if (sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
- return 1;
- }
-
- /* Recurse into each directory we found. */
- while (dirlist != NULL) {
- copyinfo *next = dirlist->next;
- if (remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
- return 1;
+ copyinfo ci(lpath, rpath, name, mode);
+ if (S_ISDIR(mode)) {
+ dirlist.push_back(ci);
+ } else if (S_ISLNK(mode)) {
+ linklist.push_back(ci);
+ } else {
+ if (!should_pull_file(ci.mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", ci.rpath.c_str(), ci.mode);
+ ci.skip = true;
+ }
+ ci.time = time;
+ ci.size = size;
+ file_list->push_back(ci);
}
- free(dirlist);
- dirlist = next;
+ };
+
+ if (!sync_ls(sc, rpath.c_str(), callback)) {
+ return false;
}
- return 0;
+ // Check each symlink we found to see whether it's a file or directory.
+ for (copyinfo& link_ci : linklist) {
+ struct stat st;
+ if (!sync_stat_fallback(sc, link_ci.rpath.c_str(), &st)) {
+ sc.Warning("stat failed for path %s: %s", link_ci.rpath.c_str(), strerror(errno));
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ dirlist.emplace_back(std::move(link_ci));
+ } else {
+ file_list->emplace_back(std::move(link_ci));
+ }
+ }
+
+ // Recurse into each directory we found.
+ while (!dirlist.empty()) {
+ copyinfo current = dirlist.back();
+ dirlist.pop_back();
+ if (!remote_build_list(sc, file_list, current.rpath, current.lpath)) {
+ return false;
+ }
+ }
+
+ return true;
}
-static int set_time_and_mode(const char *lpath, time_t time, unsigned int mode)
-{
+static int set_time_and_mode(const std::string& lpath, time_t time,
+ unsigned int mode) {
struct utimbuf times = { time, time };
- int r1 = utime(lpath, ×);
+ int r1 = utime(lpath.c_str(), ×);
/* use umask for permissions */
- mode_t mask=umask(0000);
+ mode_t mask = umask(0000);
umask(mask);
- int r2 = chmod(lpath, mode & ~mask);
+ int r2 = chmod(lpath.c_str(), mode & ~mask);
- return r1 ? : r2;
+ return r1 ? r1 : r2;
}
-/* Return a copy of the path string with / appended if needed */
-static char *add_slash_to_path(const char *path)
-{
- if (path[strlen(path) - 1] != '/') {
- size_t len = strlen(path) + 2;
- char *path_with_slash = reinterpret_cast<char*>(malloc(len));
- if (path_with_slash == NULL)
- return NULL;
- snprintf(path_with_slash, len, "%s/", path);
- return path_with_slash;
- } else {
- return strdup(path);
+static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
+ std::string lpath, bool copy_attrs) {
+ sc.NewTransfer();
+
+ // Make sure that both directory paths end in a slash.
+ // Both paths are known to be nonempty, so we don't need to check.
+ ensure_trailing_separators(lpath, rpath);
+
+ // Recursively build the list of files to copy.
+ sc.Printf("pull: building file list...");
+ std::vector<copyinfo> file_list;
+ if (!remote_build_list(sc, &file_list, rpath.c_str(), lpath.c_str())) {
+ return false;
}
-}
-static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath,
- int copy_attrs)
-{
- copyinfo *filelist = 0;
- copyinfo *ci, *next;
- int pulled = 0;
+ sc.ComputeExpectedTotalBytes(file_list);
+
int skipped = 0;
- char *rpath_clean = NULL;
- char *lpath_clean = NULL;
- int ret = 0;
-
- if (rpath[0] == '\0' || lpath[0] == '\0') {
- ret = -1;
- goto finish;
- }
-
- /* Make sure that both directory paths end in a slash. */
- rpath_clean = add_slash_to_path(rpath);
- if (!rpath_clean) {
- ret = -1;
- goto finish;
- }
- lpath_clean = add_slash_to_path(lpath);
- if (!lpath_clean) {
- ret = -1;
- goto finish;
- }
-
- /* Recursively build the list of files to copy. */
- fprintf(stderr, "pull: building file list...\n");
- if (remote_build_list(fd, &filelist, rpath_clean, lpath_clean)) {
- ret = -1;
- goto finish;
- }
-
- for (ci = filelist; ci != 0; ci = next) {
- next = ci->next;
- if (ci->flag == 0) {
- fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
- if (sync_recv(fd, ci->src, ci->dst, 0 /* no show progress */)) {
- ret = -1;
- goto finish;
+ for (const copyinfo &ci : file_list) {
+ if (!ci.skip) {
+ if (S_ISDIR(ci.mode)) {
+ // Entry is for an empty directory, create it and continue.
+ // TODO(b/25457350): We don't preserve permissions on directories.
+ if (!mkdirs(ci.lpath)) {
+ sc.Error("failed to create directory '%s': %s",
+ ci.lpath.c_str(), strerror(errno));
+ return false;
+ }
+ continue;
}
- if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) {
- ret = -1;
- goto finish;
+ if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size)) {
+ return false;
}
- pulled++;
+
+ if (copy_attrs && set_time_and_mode(ci.lpath, ci.time, ci.mode)) {
+ return false;
+ }
} else {
skipped++;
}
- free(ci);
}
- fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
- pulled, (pulled == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s");
-
-finish:
- free(lpath_clean);
- free(rpath_clean);
- return ret;
+ sc.RecordFilesSkipped(skipped);
+ sc.ReportTransferRate(rpath, TransferDirection::pull);
+ return true;
}
-int do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs) {
- std::string error;
- int fd = adb_connect("sync:", &error);
- if (fd < 0) {
- fprintf(stderr,"error: %s\n", error.c_str());
- return 1;
- }
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+ bool copy_attrs, const char* name) {
+ SyncConnection sc;
+ if (!sc.IsValid()) return false;
- unsigned mode, time;
- if (sync_readtime(fd, rpath, &time, &mode)) {
- return 1;
- }
- if (mode == 0) {
- fprintf(stderr,"remote object '%s' does not exist\n", rpath);
- return 1;
- }
+ bool success = true;
+ struct stat st;
+ bool dst_exists = true;
- if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
- std::string path_holder;
- struct stat st;
- if (stat(lpath, &st) == 0) {
- if (S_ISDIR(st.st_mode)) {
- // If we're copying a remote file to a local directory,
- // we really want to copy to local_dir + "/" + basename(remote).
- path_holder = android::base::StringPrintf("%s/%s", lpath, adb_basename(rpath).c_str());
- lpath = path_holder.c_str();
+ if (stat(dst, &st) == -1) {
+ dst_exists = false;
+
+ // If we're only pulling one path, the destination path might point to
+ // a path that doesn't exist yet.
+ if (srcs.size() == 1 && errno == ENOENT) {
+ // However, its parent must exist.
+ struct stat parent_st;
+ if (stat(adb_dirname(dst).c_str(), &parent_st) == -1) {
+ sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno));
+ return false;
}
- }
- BEGIN();
- if (sync_recv(fd, rpath, lpath, show_progress)) {
- return 1;
} else {
- if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
- return 1;
+ sc.Error("failed to access '%s': %s", dst, strerror(errno));
+ return false;
+ }
+ }
+
+ bool dst_isdir = dst_exists && S_ISDIR(st.st_mode);
+ if (!dst_isdir) {
+ if (srcs.size() > 1) {
+ sc.Error("target '%s' is not a directory", dst);
+ return false;
+ } else {
+ size_t dst_len = strlen(dst);
+
+ // A path that ends with a slash doesn't have to be a directory if
+ // it doesn't exist yet.
+ if (adb_is_separator(dst[dst_len - 1]) && dst_exists) {
+ sc.Error("failed to access '%s': Not a directory", dst);
+ return false;
}
}
- } else if(S_ISDIR(mode)) {
- BEGIN();
- if (copy_remote_dir_local(fd, rpath, lpath, copy_attrs)) {
- return 1;
- }
- } else {
- fprintf(stderr,"remote object '%s' not a file or directory\n", rpath);
- return 1;
}
- END();
- sync_quit(fd);
- return 0;
+
+ for (const char* src_path : srcs) {
+ const char* dst_path = dst;
+ struct stat src_st;
+ if (!sync_stat_fallback(sc, src_path, &src_st)) {
+ if (errno == ENOPROTOOPT) {
+ sc.Error("remote object '%s' does not exist", src_path);
+ } else {
+ sc.Error("failed to stat remote object '%s': %s", src_path, strerror(errno));
+ }
+
+ success = false;
+ continue;
+ }
+
+ bool src_isdir = S_ISDIR(src_st.st_mode);
+ if (src_isdir) {
+ std::string dst_dir = dst;
+
+ // If the destination path existed originally, the source directory
+ // should be copied as a child of the destination.
+ if (dst_exists) {
+ if (!dst_isdir) {
+ sc.Error("target '%s' is not a directory", dst);
+ return false;
+ }
+ if (!adb_is_separator(dst_dir.back())) {
+ dst_dir.push_back(OS_PATH_SEPARATOR);
+ }
+ dst_dir.append(adb_basename(src_path));
+ }
+
+ success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
+ continue;
+ } else if (!should_pull_file(src_st.st_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode);
+ continue;
+ }
+
+ std::string path_holder;
+ if (dst_isdir) {
+ // If we're copying a remote file to a local directory, we
+ // really want to copy to local_dir + OS_PATH_SEPARATOR +
+ // basename(remote).
+ path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
+ adb_basename(src_path).c_str());
+ dst_path = path_holder.c_str();
+ }
+
+ sc.NewTransfer();
+ sc.SetExpectedTotalBytes(src_st.st_size);
+ if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size)) {
+ success = false;
+ continue;
+ }
+
+ if (copy_attrs && set_time_and_mode(dst_path, src_st.st_mtime, src_st.st_mode) != 0) {
+ success = false;
+ continue;
+ }
+ sc.ReportTransferRate(src_path, TransferDirection::pull);
+ }
+
+ sc.ReportOverallTransferRate(TransferDirection::pull);
+ return success;
}
-int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only)
-{
- fprintf(stderr, "syncing %s...\n", rpath.c_str());
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
+ SyncConnection sc;
+ if (!sc.IsValid()) return false;
- std::string error;
- int fd = adb_connect("sync:", &error);
- if (fd < 0) {
- fprintf(stderr, "error: %s\n", error.c_str());
- return 1;
+ bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only);
+ if (!list_only) {
+ sc.ReportOverallTransferRate(TransferDirection::push);
}
-
- BEGIN();
- if (copy_local_dir_remote(fd, lpath.c_str(), rpath.c_str(), 1, list_only)) {
- return 1;
- }
- END();
- sync_quit(fd);
- return 0;
+ return success;
}
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index ea019f4..e667bf8 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -14,27 +14,37 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_SYNC
+#define TRACE_TAG SYNC
#include "sysdeps.h"
#include "file_sync_service.h"
#include <dirent.h>
#include <errno.h>
-#include <selinux/android.h>
+#include <linux/xattr.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/xattr.h>
#include <unistd.h>
#include <utime.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <selinux/android.h>
+
#include "adb.h"
#include "adb_io.h"
-#include "private/android_filesystem_config.h"
+#include "adb_trace.h"
+#include "adb_utils.h"
+#include "security_log_tags.h"
+#include "sysdeps/errno.h"
-#include <base/strings.h>
+using android::base::StringPrintf;
static bool should_use_fs_config(const std::string& path) {
// TODO: use fs_config to configure permissions on /data.
@@ -43,71 +53,100 @@
android::base::StartsWith(path, "/oem/");
}
+static bool update_capabilities(const char* path, uint64_t capabilities) {
+ if (capabilities == 0) {
+ // Ensure we clean up in case the capabilities weren't 0 in the past.
+ removexattr(path, XATTR_NAME_CAPS);
+ return true;
+ }
+
+ vfs_cap_data cap_data = {};
+ cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
+ cap_data.data[0].permitted = (capabilities & 0xffffffff);
+ cap_data.data[0].inheritable = 0;
+ cap_data.data[1].permitted = (capabilities >> 32);
+ cap_data.data[1].inheritable = 0;
+ return setxattr(path, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) != -1;
+}
+
static bool secure_mkdirs(const std::string& path) {
uid_t uid = -1;
gid_t gid = -1;
unsigned int mode = 0775;
- uint64_t cap = 0;
+ uint64_t capabilities = 0;
if (path[0] != '/') return false;
std::vector<std::string> path_components = android::base::Split(path, "/");
- path_components.pop_back(); // For "/system/bin/sh", only create "/system/bin".
-
std::string partial_path;
- for (auto& path_component : path_components) {
+ for (const auto& path_component : path_components) {
if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
partial_path += path_component;
if (should_use_fs_config(partial_path)) {
- fs_config(partial_path.c_str(), 1, &uid, &gid, &mode, &cap);
+ fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &capabilities);
}
if (adb_mkdir(partial_path.c_str(), mode) == -1) {
if (errno != EEXIST) {
return false;
}
} else {
- if (chown(partial_path.c_str(), uid, gid) == -1) {
- return false;
- }
+ if (chown(partial_path.c_str(), uid, gid) == -1) return false;
+
+ // Not all filesystems support setting SELinux labels. http://b/23530370.
selinux_android_restorecon(partial_path.c_str(), 0);
+
+ if (!update_capabilities(partial_path.c_str(), capabilities)) return false;
}
}
return true;
}
-static int do_stat(int s, const char *path)
-{
- syncmsg msg;
- struct stat st;
+static bool do_lstat_v1(int s, const char* path) {
+ syncmsg msg = {};
+ msg.stat_v1.id = ID_LSTAT_V1;
- msg.stat.id = ID_STAT;
-
- if(lstat(path, &st)) {
- msg.stat.mode = 0;
- msg.stat.size = 0;
- msg.stat.time = 0;
- } else {
- msg.stat.mode = htoll(st.st_mode);
- msg.stat.size = htoll(st.st_size);
- msg.stat.time = htoll(st.st_mtime);
- }
-
- return WriteFdExactly(s, &msg.stat, sizeof(msg.stat)) ? 0 : -1;
+ struct stat st = {};
+ lstat(path, &st);
+ msg.stat_v1.mode = st.st_mode;
+ msg.stat_v1.size = st.st_size;
+ msg.stat_v1.time = st.st_mtime;
+ return WriteFdExactly(s, &msg.stat_v1, sizeof(msg.stat_v1));
}
-static int do_list(int s, const char *path)
-{
- struct dirent *de;
- struct stat st;
+static bool do_stat_v2(int s, uint32_t id, const char* path) {
+ syncmsg msg = {};
+ msg.stat_v2.id = id;
- char tmp[1024 + 256 + 1];
- char *fname;
+ decltype(&stat) stat_fn;
+ if (id == ID_STAT_V2) {
+ stat_fn = stat;
+ } else {
+ stat_fn = lstat;
+ }
- size_t len = strlen(path);
- memcpy(tmp, path, len);
- tmp[len] = '/';
- fname = tmp + len + 1;
+ struct stat st = {};
+ int rc = stat_fn(path, &st);
+ if (rc == -1) {
+ msg.stat_v2.error = errno_to_wire(errno);
+ } else {
+ msg.stat_v2.dev = st.st_dev;
+ msg.stat_v2.ino = st.st_ino;
+ msg.stat_v2.mode = st.st_mode;
+ msg.stat_v2.nlink = st.st_nlink;
+ msg.stat_v2.uid = st.st_uid;
+ msg.stat_v2.gid = st.st_gid;
+ msg.stat_v2.size = st.st_size;
+ msg.stat_v2.atime = st.st_atime;
+ msg.stat_v2.mtime = st.st_mtime;
+ msg.stat_v2.ctime = st.st_ctime;
+ }
+
+ return WriteFdExactly(s, &msg.stat_v2, sizeof(msg.stat_v2));
+}
+
+static bool do_list(int s, const char* path) {
+ dirent* de;
syncmsg msg;
msg.dent.id = ID_DENT;
@@ -116,22 +155,19 @@
if (!d) goto done;
while ((de = readdir(d.get()))) {
- int len = strlen(de->d_name);
+ std::string filename(StringPrintf("%s/%s", path, de->d_name));
- /* not supposed to be possible, but
- if it does happen, let's not buffer overrun */
- if(len > 256) continue;
+ struct stat st;
+ if (lstat(filename.c_str(), &st) == 0) {
+ size_t d_name_length = strlen(de->d_name);
+ msg.dent.mode = st.st_mode;
+ msg.dent.size = st.st_size;
+ msg.dent.time = st.st_mtime;
+ msg.dent.namelen = d_name_length;
- strcpy(fname, de->d_name);
- if(lstat(tmp, &st) == 0) {
- msg.dent.mode = htoll(st.st_mode);
- msg.dent.size = htoll(st.st_size);
- msg.dent.time = htoll(st.st_mtime);
- msg.dent.namelen = htoll(len);
-
- if(!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
- !WriteFdExactly(s, de->d_name, len)) {
- return -1;
+ if (!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
+ !WriteFdExactly(s, de->d_name, d_name_length)) {
+ return false;
}
}
}
@@ -142,255 +178,254 @@
msg.dent.size = 0;
msg.dent.time = 0;
msg.dent.namelen = 0;
- return WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ? 0 : -1;
+ return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
}
-static int fail_message(int s, const char *reason)
-{
+// Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
+#pragma GCC poison SendFail
+
+static bool SendSyncFail(int fd, const std::string& reason) {
+ D("sync: failure: %s", reason.c_str());
+
syncmsg msg;
- int len = strlen(reason);
-
- D("sync: failure: %s\n", reason);
-
msg.data.id = ID_FAIL;
- msg.data.size = htoll(len);
- if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
- !WriteFdExactly(s, reason, len)) {
- return -1;
- } else {
- return 0;
- }
+ msg.data.size = reason.size();
+ return WriteFdExactly(fd, &msg.data, sizeof(msg.data)) && WriteFdExactly(fd, reason);
}
-static int fail_errno(int s)
-{
- return fail_message(s, strerror(errno));
+static bool SendSyncFailErrno(int fd, const std::string& reason) {
+ return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
}
-static int handle_send_file(int s, char *path, uid_t uid,
- gid_t gid, mode_t mode, char *buffer, bool do_unlink)
-{
+static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
+ mode_t mode, std::vector<char>& buffer, bool do_unlink) {
syncmsg msg;
unsigned int timestamp = 0;
- int fd;
- fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
- if(fd < 0 && errno == ENOENT) {
- if (!secure_mkdirs(path)) {
- if(fail_errno(s))
- return -1;
- fd = -1;
- } else {
- fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+ __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
+
+ int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+ if (fd < 0 && errno == ENOENT) {
+ if (!secure_mkdirs(adb_dirname(path))) {
+ SendSyncFailErrno(s, "secure_mkdirs failed");
+ goto fail;
}
+ fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
}
- if(fd < 0 && errno == EEXIST) {
+ if (fd < 0 && errno == EEXIST) {
fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
}
- if(fd < 0) {
- if(fail_errno(s))
- return -1;
- fd = -1;
+ if (fd < 0) {
+ SendSyncFailErrno(s, "couldn't create file");
+ goto fail;
} else {
- if(fchown(fd, uid, gid) != 0) {
- fail_errno(s);
- errno = 0;
+ if (fchown(fd, uid, gid) == -1) {
+ SendSyncFailErrno(s, "fchown failed");
+ goto fail;
}
- /*
- * fchown clears the setuid bit - restore it if present.
- * Ignore the result of calling fchmod. It's not supported
- * by all filesystems. b/12441485
- */
+ // Not all filesystems support setting SELinux labels. http://b/23530370.
+ selinux_android_restorecon(path, 0);
+
+ // fchown clears the setuid bit - restore it if present.
+ // Ignore the result of calling fchmod. It's not supported
+ // by all filesystems, so we don't check for success. b/12441485
fchmod(fd, mode);
}
- for(;;) {
- unsigned int len;
+ while (true) {
+ if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
- if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
- goto fail;
-
- if(msg.data.id != ID_DATA) {
- if(msg.data.id == ID_DONE) {
- timestamp = ltohl(msg.data.size);
+ if (msg.data.id != ID_DATA) {
+ if (msg.data.id == ID_DONE) {
+ timestamp = msg.data.size;
break;
}
- fail_message(s, "invalid data message");
- goto fail;
+ SendSyncFail(s, "invalid data message");
+ goto abort;
}
- len = ltohl(msg.data.size);
- if(len > SYNC_DATA_MAX) {
- fail_message(s, "oversize data message");
- goto fail;
- }
- if(!ReadFdExactly(s, buffer, len))
- goto fail;
- if(fd < 0)
- continue;
- if(!WriteFdExactly(fd, buffer, len)) {
- int saved_errno = errno;
- adb_close(fd);
- if (do_unlink) adb_unlink(path);
- fd = -1;
- errno = saved_errno;
- if(fail_errno(s)) return -1;
+ if (msg.data.size > buffer.size()) { // TODO: resize buffer?
+ SendSyncFail(s, "oversize data message");
+ goto abort;
+ }
+
+ if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
+
+ if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
+ SendSyncFailErrno(s, "write failed");
+ goto fail;
}
}
- if(fd >= 0) {
- struct utimbuf u;
- adb_close(fd);
- selinux_android_restorecon(path, 0);
- u.actime = timestamp;
- u.modtime = timestamp;
- utime(path, &u);
+ adb_close(fd);
- msg.status.id = ID_OKAY;
- msg.status.msglen = 0;
- if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
- return -1;
+ if (!update_capabilities(path, capabilities)) {
+ SendSyncFailErrno(s, "update_capabilities failed");
+ goto fail;
}
- return 0;
+
+ utimbuf u;
+ u.actime = timestamp;
+ u.modtime = timestamp;
+ utime(path, &u);
+
+ msg.status.id = ID_OKAY;
+ msg.status.msglen = 0;
+ return WriteFdExactly(s, &msg.status, sizeof(msg.status));
fail:
- if(fd >= 0)
- adb_close(fd);
+ // If there's a problem on the device, we'll send an ID_FAIL message and
+ // close the socket. Unfortunately the kernel will sometimes throw that
+ // data away if the other end keeps writing without reading (which is
+ // the case with old versions of adb). To maintain compatibility, keep
+ // reading and throwing away ID_DATA packets until the other side notices
+ // that we've reported an error.
+ while (true) {
+ if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
+
+ if (msg.data.id == ID_DONE) {
+ goto abort;
+ } else if (msg.data.id != ID_DATA) {
+ char id[5];
+ memcpy(id, &msg.data.id, sizeof(msg.data.id));
+ id[4] = '\0';
+ D("handle_send_fail received unexpected id '%s' during failure", id);
+ goto abort;
+ }
+
+ if (msg.data.size > buffer.size()) {
+ D("handle_send_fail received oversized packet of length '%u' during failure",
+ msg.data.size);
+ goto abort;
+ }
+
+ if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
+ }
+
+abort:
+ if (fd >= 0) adb_close(fd);
if (do_unlink) adb_unlink(path);
- return -1;
+ return false;
}
#if defined(_WIN32)
-extern int handle_send_link(int s, char *path, char *buffer) __attribute__((error("no symlinks on Windows")));
+extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) __attribute__((error("no symlinks on Windows")));
#else
-static int handle_send_link(int s, char *path, char *buffer)
-{
+static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
syncmsg msg;
unsigned int len;
int ret;
- if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
- return -1;
+ if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
- if(msg.data.id != ID_DATA) {
- fail_message(s, "invalid data message: expected ID_DATA");
- return -1;
+ if (msg.data.id != ID_DATA) {
+ SendSyncFail(s, "invalid data message: expected ID_DATA");
+ return false;
}
- len = ltohl(msg.data.size);
- if(len > SYNC_DATA_MAX) {
- fail_message(s, "oversize data message");
- return -1;
+ len = msg.data.size;
+ if (len > buffer.size()) { // TODO: resize buffer?
+ SendSyncFail(s, "oversize data message");
+ return false;
}
- if(!ReadFdExactly(s, buffer, len))
- return -1;
+ if (!ReadFdExactly(s, &buffer[0], len)) return false;
- ret = symlink(buffer, path);
- if(ret && errno == ENOENT) {
- if (!secure_mkdirs(path)) {
- fail_errno(s);
- return -1;
+ ret = symlink(&buffer[0], path.c_str());
+ if (ret && errno == ENOENT) {
+ if (!secure_mkdirs(adb_dirname(path))) {
+ SendSyncFailErrno(s, "secure_mkdirs failed");
+ return false;
}
- ret = symlink(buffer, path);
+ ret = symlink(&buffer[0], path.c_str());
}
- if(ret) {
- fail_errno(s);
- return -1;
+ if (ret) {
+ SendSyncFailErrno(s, "symlink failed");
+ return false;
}
- if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
- return -1;
+ if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
- if(msg.data.id == ID_DONE) {
+ if (msg.data.id == ID_DONE) {
msg.status.id = ID_OKAY;
msg.status.msglen = 0;
- if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
- return -1;
+ if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
} else {
- fail_message(s, "invalid data message: expected ID_DONE");
- return -1;
+ SendSyncFail(s, "invalid data message: expected ID_DONE");
+ return false;
}
- return 0;
+ return true;
}
#endif
-static int do_send(int s, char *path, char *buffer)
-{
- unsigned int mode;
- bool is_link = false;
- bool do_unlink;
-
- char* tmp = strrchr(path,',');
- if(tmp) {
- *tmp = 0;
- errno = 0;
- mode = strtoul(tmp + 1, NULL, 0);
- is_link = S_ISLNK((mode_t) mode);
- mode &= 0777;
- }
- if(!tmp || errno) {
- mode = 0644;
- is_link = 0;
- do_unlink = true;
- } else {
- struct stat st;
- /* Don't delete files before copying if they are not "regular" */
- do_unlink = lstat(path, &st) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
- if (do_unlink) {
- adb_unlink(path);
- }
+static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
+ // 'spec' is of the form "/some/path,0755". Break it up.
+ size_t comma = spec.find_last_of(',');
+ if (comma == std::string::npos) {
+ SendSyncFail(s, "missing , in ID_SEND");
+ return false;
}
- if (is_link) {
- return handle_send_link(s, path, buffer);
+ std::string path = spec.substr(0, comma);
+
+ errno = 0;
+ mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
+ if (errno != 0) {
+ SendSyncFail(s, "bad mode");
+ return false;
}
- uid_t uid = -1;
- gid_t gid = -1;
- uint64_t cap = 0;
+ // Don't delete files before copying if they are not "regular" or symlinks.
+ struct stat st;
+ bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
+ if (do_unlink) {
+ adb_unlink(path.c_str());
+ }
- /* copy user permission bits to "group" and "other" permissions */
+ if (S_ISLNK(mode)) {
+ return handle_send_link(s, path.c_str(), buffer);
+ }
+
+ // Copy user permission bits to "group" and "other" permissions.
+ mode &= 0777;
mode |= ((mode >> 3) & 0070);
mode |= ((mode >> 3) & 0007);
- tmp = path;
- if(*tmp == '/') {
- tmp++;
- }
+ uid_t uid = -1;
+ gid_t gid = -1;
+ uint64_t capabilities = 0;
if (should_use_fs_config(path)) {
- fs_config(tmp, 0, &uid, &gid, &mode, &cap);
+ unsigned int broken_api_hack = mode;
+ fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
+ mode = broken_api_hack;
}
- return handle_send_file(s, path, uid, gid, mode, buffer, do_unlink);
+ return handle_send_file(s, path.c_str(), uid, gid, capabilities, mode, buffer, do_unlink);
}
-static int do_recv(int s, const char *path, char *buffer)
-{
- syncmsg msg;
- int fd, r;
+static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
+ __android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
- fd = adb_open(path, O_RDONLY | O_CLOEXEC);
- if(fd < 0) {
- if(fail_errno(s)) return -1;
- return 0;
+ int fd = adb_open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ SendSyncFailErrno(s, "open failed");
+ return false;
}
+ syncmsg msg;
msg.data.id = ID_DATA;
- for(;;) {
- r = adb_read(fd, buffer, SYNC_DATA_MAX);
- if(r <= 0) {
- if(r == 0) break;
- if(errno == EINTR) continue;
- r = fail_errno(s);
+ while (true) {
+ int r = adb_read(fd, &buffer[0], buffer.size());
+ if (r <= 0) {
+ if (r == 0) break;
+ SendSyncFailErrno(s, "read failed");
adb_close(fd);
- return r;
+ return false;
}
- msg.data.size = htoll(r);
- if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
- !WriteFdExactly(s, buffer, r)) {
+ msg.data.size = r;
+ if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
adb_close(fd);
- return -1;
+ return false;
}
}
@@ -398,66 +433,89 @@
msg.data.id = ID_DONE;
msg.data.size = 0;
- if(!WriteFdExactly(s, &msg.data, sizeof(msg.data))) {
- return -1;
- }
-
- return 0;
+ return WriteFdExactly(s, &msg.data, sizeof(msg.data));
}
-void file_sync_service(int fd, void *cookie)
-{
- syncmsg msg;
+static const char* sync_id_to_name(uint32_t id) {
+ switch (id) {
+ case ID_LSTAT_V1:
+ return "lstat_v1";
+ case ID_LSTAT_V2:
+ return "lstat_v2";
+ case ID_STAT_V2:
+ return "stat_v2";
+ case ID_LIST:
+ return "list";
+ case ID_SEND:
+ return "send";
+ case ID_RECV:
+ return "recv";
+ case ID_QUIT:
+ return "quit";
+ default:
+ return "???";
+ }
+}
+
+static bool handle_sync_command(int fd, std::vector<char>& buffer) {
+ D("sync: waiting for request");
+
+ ATRACE_CALL();
+ SyncRequest request;
+ if (!ReadFdExactly(fd, &request, sizeof(request))) {
+ SendSyncFail(fd, "command read failure");
+ return false;
+ }
+ size_t path_length = request.path_length;
+ if (path_length > 1024) {
+ SendSyncFail(fd, "path too long");
+ return false;
+ }
char name[1025];
- unsigned namelen;
+ if (!ReadFdExactly(fd, name, path_length)) {
+ SendSyncFail(fd, "filename read failure");
+ return false;
+ }
+ name[path_length] = 0;
- char *buffer = reinterpret_cast<char*>(malloc(SYNC_DATA_MAX));
- if(buffer == 0) goto fail;
+ std::string id_name = sync_id_to_name(request.id);
+ std::string trace_name = StringPrintf("%s(%s)", id_name.c_str(), name);
+ ATRACE_NAME(trace_name.c_str());
- for(;;) {
- D("sync: waiting for command\n");
-
- if(!ReadFdExactly(fd, &msg.req, sizeof(msg.req))) {
- fail_message(fd, "command read failure");
+ D("sync: %s('%s')", id_name.c_str(), name);
+ switch (request.id) {
+ case ID_LSTAT_V1:
+ if (!do_lstat_v1(fd, name)) return false;
break;
- }
- namelen = ltohl(msg.req.namelen);
- if(namelen > 1024) {
- fail_message(fd, "invalid namelen");
- break;
- }
- if(!ReadFdExactly(fd, name, namelen)) {
- fail_message(fd, "filename read failure");
- break;
- }
- name[namelen] = 0;
-
- msg.req.namelen = 0;
- D("sync: '%s' '%s'\n", (char*) &msg.req, name);
-
- switch(msg.req.id) {
- case ID_STAT:
- if(do_stat(fd, name)) goto fail;
+ case ID_LSTAT_V2:
+ case ID_STAT_V2:
+ if (!do_stat_v2(fd, request.id, name)) return false;
break;
case ID_LIST:
- if(do_list(fd, name)) goto fail;
+ if (!do_list(fd, name)) return false;
break;
case ID_SEND:
- if(do_send(fd, name, buffer)) goto fail;
+ if (!do_send(fd, name, buffer)) return false;
break;
case ID_RECV:
- if(do_recv(fd, name, buffer)) goto fail;
+ if (!do_recv(fd, name, buffer)) return false;
break;
case ID_QUIT:
- goto fail;
+ return false;
default:
- fail_message(fd, "unknown command");
- goto fail;
- }
+ SendSyncFail(fd, StringPrintf("unknown command %08x", request.id));
+ return false;
}
-fail:
- if(buffer != 0) free(buffer);
- D("sync: done\n");
+ return true;
+}
+
+void file_sync_service(int fd, void*) {
+ std::vector<char> buffer(SYNC_DATA_MAX);
+
+ while (handle_sync_command(fd, buffer)) {
+ }
+
+ D("sync: done");
adb_close(fd);
}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 1d3e3bd..90f1965 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -18,15 +18,14 @@
#define _FILE_SYNC_SERVICE_H_
#include <string>
-
-#define htoll(x) (x)
-#define ltohl(x) (x)
+#include <vector>
#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
-#define ID_STAT MKID('S','T','A','T')
+#define ID_LSTAT_V1 MKID('S','T','A','T')
+#define ID_STAT_V2 MKID('S','T','A','2')
+#define ID_LSTAT_V2 MKID('L','S','T','2')
#define ID_LIST MKID('L','I','S','T')
-#define ID_ULNK MKID('U','L','N','K')
#define ID_SEND MKID('S','E','N','D')
#define ID_RECV MKID('R','E','C','V')
#define ID_DENT MKID('D','E','N','T')
@@ -36,41 +35,57 @@
#define ID_FAIL MKID('F','A','I','L')
#define ID_QUIT MKID('Q','U','I','T')
+struct SyncRequest {
+ uint32_t id; // ID_STAT, et cetera.
+ uint32_t path_length; // <= 1024
+ // Followed by 'path_length' bytes of path (not NUL-terminated).
+} __attribute__((packed)) ;
+
union syncmsg {
- unsigned id;
- struct {
- unsigned id;
- unsigned namelen;
- } req;
- struct {
- unsigned id;
- unsigned mode;
- unsigned size;
- unsigned time;
- } stat;
- struct {
- unsigned id;
- unsigned mode;
- unsigned size;
- unsigned time;
- unsigned namelen;
+ struct __attribute__((packed)) {
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t time;
+ } stat_v1;
+ struct __attribute__((packed)) {
+ uint32_t id;
+ uint32_t error;
+ uint64_t dev;
+ uint64_t ino;
+ uint32_t mode;
+ uint32_t nlink;
+ uint32_t uid;
+ uint32_t gid;
+ uint64_t size;
+ int64_t atime;
+ int64_t mtime;
+ int64_t ctime;
+ } stat_v2;
+ struct __attribute__((packed)) {
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t time;
+ uint32_t namelen;
} dent;
- struct {
- unsigned id;
- unsigned size;
+ struct __attribute__((packed)) {
+ uint32_t id;
+ uint32_t size;
} data;
- struct {
- unsigned id;
- unsigned msglen;
+ struct __attribute__((packed)) {
+ uint32_t id;
+ uint32_t msglen;
} status;
-} ;
+};
+void file_sync_service(int fd, void* cookie);
+bool do_sync_ls(const char* path);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+ bool copy_attrs, const char* name=nullptr);
-void file_sync_service(int fd, void *cookie);
-int do_sync_ls(const char *path);
-int do_sync_push(const char *lpath, const char *rpath, bool show_progress);
-int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
-int do_sync_pull(const char *rpath, const char *lpath, bool show_progress, int pullTime);
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
#define SYNC_DATA_MAX (64*1024)
diff --git a/adb/get_my_path_darwin.cpp b/adb/get_my_path_darwin.cpp
deleted file mode 100644
index b0c962e..0000000
--- a/adb/get_my_path_darwin.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Carbon/Carbon.h>
-#include <unistd.h>
-
-#include "adb.h"
-
-void get_my_path(char *s, size_t maxLen)
-{
- CFBundleRef mainBundle = CFBundleGetMainBundle();
- CFURLRef executableURL = CFBundleCopyExecutableURL(mainBundle);
- CFStringRef executablePathString = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
- CFRelease(executableURL);
-
- CFStringGetFileSystemRepresentation(executablePathString, s, maxLen);
- CFRelease(executablePathString);
-}
-
diff --git a/adb/get_my_path_linux.cpp b/adb/get_my_path_linux.cpp
deleted file mode 100644
index 11c0b21..0000000
--- a/adb/get_my_path_linux.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <limits.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "adb.h"
-
-void get_my_path(char *exe, size_t maxLen)
-{
- char proc[64];
- snprintf(proc, sizeof proc, "/proc/%d/exe", getpid());
- int err = readlink(proc, exe, maxLen - 1);
- if(err > 0) {
- exe[err] = '\0';
- } else {
- exe[0] = '\0';
- }
-}
-
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index 06e7780..3135aa4 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-/* implement the "debug-ports" and "track-debug-ports" device services */
+#if !ADB_HOST
-#define TRACE_TAG TRACE_JDWP
+#define TRACE_TAG JDWP
#include "sysdeps.h"
@@ -24,21 +24,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include <unistd.h>
+#include <list>
+#include <memory>
+#include <vector>
+
#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
/* here's how these things work.
when adbd starts, it creates a unix server socket
- named @vm-debug-control (@ is a shortcut for "first byte is zero"
+ named @jdwp-control (@ is a shortcut for "first byte is zero"
to use the private namespace instead of the file system)
when a new JDWP daemon thread starts in a new VM process, it creates
- a connection to @vm-debug-control to announce its availability.
+ a connection to @jdwp-control to announce its availability.
- JDWP thread @vm-debug-control
+ JDWP thread @jdwp-control
| |
|-------------------------------> |
| hello I'm in process <pid> |
@@ -71,7 +80,7 @@
to the JDWP process with the help of sendmsg()
- JDWP thread @vm-debug-control
+ JDWP thread @jdwp-control
| |
| <----------------------|
| OK, try this file descriptor |
@@ -115,335 +124,251 @@
** for each JDWP process, we record its pid and its connected socket
**/
-#define MAX_OUT_FDS 4
+// PIDs are transmitted as 4 hex digits in ascii.
+static constexpr size_t PID_LEN = 4;
-#if !ADB_HOST
+static void jdwp_process_event(int socket, unsigned events, void* _proc);
+static void jdwp_process_list_updated(void);
-#include <sys/socket.h>
-#include <sys/un.h>
+struct JdwpProcess;
+static std::list<std::unique_ptr<JdwpProcess>> _jdwp_list;
struct JdwpProcess {
- JdwpProcess* next;
- JdwpProcess* prev;
- int pid;
- int socket;
- fdevent* fde;
+ explicit JdwpProcess(int socket) {
+ this->socket = socket;
+ this->fde = fdevent_create(socket, jdwp_process_event, this);
- char in_buff[4]; /* input character to read PID */
- int in_len; /* number from JDWP process */
+ if (!this->fde) {
+ fatal("could not create fdevent for new JDWP process");
+ }
- int out_fds[MAX_OUT_FDS]; /* output array of file descriptors */
- int out_count; /* to send to the JDWP process */
+ this->fde->state |= FDE_DONT_CLOSE;
+
+ /* start by waiting for the PID */
+ fdevent_add(this->fde, FDE_READ);
+ }
+
+ ~JdwpProcess() {
+ if (this->socket >= 0) {
+ adb_shutdown(this->socket);
+ adb_close(this->socket);
+ this->socket = -1;
+ }
+
+ if (this->fde) {
+ fdevent_destroy(this->fde);
+ this->fde = nullptr;
+ }
+
+ out_fds.clear();
+ }
+
+ void RemoveFromList() {
+ if (this->pid >= 0) {
+ D("removing pid %d from jdwp process list", this->pid);
+ } else {
+ D("removing transient JdwpProcess from list");
+ }
+
+ auto pred = [this](const auto& proc) { return proc.get() == this; };
+ _jdwp_list.remove_if(pred);
+ }
+
+ int pid = -1;
+ int socket = -1;
+ fdevent* fde = nullptr;
+
+ std::vector<unique_fd> out_fds;
+ char in_buf[PID_LEN + 1];
+ ssize_t in_len = 0;
};
-static JdwpProcess _jdwp_list;
+static size_t jdwp_process_list(char* buffer, size_t bufferlen) {
+ std::string temp;
-static int
-jdwp_process_list( char* buffer, int bufferlen )
-{
- char* end = buffer + bufferlen;
- char* p = buffer;
- JdwpProcess* proc = _jdwp_list.next;
-
- for ( ; proc != &_jdwp_list; proc = proc->next ) {
- int len;
-
+ for (auto& proc : _jdwp_list) {
/* skip transient connections */
- if (proc->pid < 0)
+ if (proc->pid < 0) {
continue;
+ }
- len = snprintf(p, end-p, "%d\n", proc->pid);
- if (p + len >= end)
+ std::string next = std::to_string(proc->pid) + "\n";
+ if (temp.length() + next.length() > bufferlen) {
+ D("truncating JDWP process list (max len = %zu)", bufferlen);
break;
- p += len;
- }
- p[0] = 0;
- return (p - buffer);
-}
-
-
-static int
-jdwp_process_list_msg( char* buffer, int bufferlen )
-{
- char head[5];
- int len = jdwp_process_list( buffer+4, bufferlen-4 );
- snprintf(head, sizeof head, "%04x", len);
- memcpy(buffer, head, 4);
- return len + 4;
-}
-
-
-static void jdwp_process_list_updated(void);
-
-static void
-jdwp_process_free( JdwpProcess* proc )
-{
- if (proc) {
- int n;
-
- proc->prev->next = proc->next;
- proc->next->prev = proc->prev;
-
- if (proc->socket >= 0) {
- adb_shutdown(proc->socket);
- adb_close(proc->socket);
- proc->socket = -1;
}
-
- if (proc->fde != NULL) {
- fdevent_destroy(proc->fde);
- proc->fde = NULL;
- }
- proc->pid = -1;
-
- for (n = 0; n < proc->out_count; n++) {
- adb_close(proc->out_fds[n]);
- }
- proc->out_count = 0;
-
- free(proc);
-
- jdwp_process_list_updated();
+ temp.append(next);
}
+
+ memcpy(buffer, temp.data(), temp.length());
+ return temp.length();
}
-
-static void jdwp_process_event(int, unsigned, void*); /* forward */
-
-
-static JdwpProcess*
-jdwp_process_alloc( int socket )
-{
- JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(
- calloc(1, sizeof(*proc)));
-
- if (proc == NULL) {
- D("not enough memory to create new JDWP process\n");
- return NULL;
+static size_t jdwp_process_list_msg(char* buffer, size_t bufferlen) {
+ // Message is length-prefixed with 4 hex digits in ASCII.
+ static constexpr size_t header_len = 4;
+ if (bufferlen < header_len) {
+ fatal("invalid JDWP process list buffer size: %zu", bufferlen);
}
- proc->socket = socket;
- proc->pid = -1;
- proc->next = proc;
- proc->prev = proc;
-
- proc->fde = fdevent_create( socket, jdwp_process_event, proc );
- if (proc->fde == NULL) {
- D("could not create fdevent for new JDWP process\n" );
- free(proc);
- return NULL;
- }
-
- proc->fde->state |= FDE_DONT_CLOSE;
- proc->in_len = 0;
- proc->out_count = 0;
-
- /* append to list */
- proc->next = &_jdwp_list;
- proc->prev = proc->next->prev;
-
- proc->prev->next = proc;
- proc->next->prev = proc;
-
- /* start by waiting for the PID */
- fdevent_add(proc->fde, FDE_READ);
-
- return proc;
+ char head[header_len + 1];
+ size_t len = jdwp_process_list(buffer + header_len, bufferlen - header_len);
+ snprintf(head, sizeof head, "%04zx", len);
+ memcpy(buffer, head, header_len);
+ return len + header_len;
}
-
-static void
-jdwp_process_event( int socket, unsigned events, void* _proc )
-{
- JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
+static void jdwp_process_event(int socket, unsigned events, void* _proc) {
+ JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
if (events & FDE_READ) {
if (proc->pid < 0) {
/* read the PID as a 4-hexchar string */
- char* p = proc->in_buff + proc->in_len;
- int size = 4 - proc->in_len;
- char temp[5];
- while (size > 0) {
- int len = recv( socket, p, size, 0 );
- if (len < 0) {
- if (errno == EINTR)
- continue;
- if (errno == EAGAIN)
- return;
- /* this can fail here if the JDWP process crashes very fast */
- D("weird unknown JDWP process failure: %s\n",
- strerror(errno));
-
- goto CloseProcess;
- }
- if (len == 0) { /* end of stream ? */
- D("weird end-of-stream from unknown JDWP process\n");
- goto CloseProcess;
- }
- p += len;
- proc->in_len += len;
- size -= len;
+ if (proc->in_len < 0) {
+ fatal("attempting to read JDWP pid again?");
}
- /* we have read 4 characters, now decode the pid */
- memcpy(temp, proc->in_buff, 4);
- temp[4] = 0;
- if (sscanf( temp, "%04x", &proc->pid ) != 1) {
- D("could not decode JDWP %p PID number: '%s'\n", proc, temp);
+ char* p = proc->in_buf + proc->in_len;
+ size_t size = PID_LEN - proc->in_len;
+
+ ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, p, size, 0));
+ if (rc < 0) {
+ if (errno == EAGAIN) {
+ return;
+ }
+
+ D("failed to read jdwp pid: %s", strerror(errno));
+ goto CloseProcess;
+ }
+
+ proc->in_len += rc;
+ if (proc->in_len != PID_LEN) {
+ return;
+ }
+
+ proc->in_buf[PID_LEN] = '\0';
+ proc->in_len = -1;
+
+ if (sscanf(proc->in_buf, "%04x", &proc->pid) != 1) {
+ D("could not decode JDWP %p PID number: '%s'", proc, p);
goto CloseProcess;
}
/* all is well, keep reading to detect connection closure */
- D("Adding pid %d to jdwp process list\n", proc->pid);
+ D("Adding pid %d to jdwp process list", proc->pid);
jdwp_process_list_updated();
- }
- else
- {
+ } else {
/* the pid was read, if we get there it's probably because the connection
* was closed (e.g. the JDWP process exited or crashed) */
- char buf[32];
+ char buf[32];
- for (;;) {
- int len = recv(socket, buf, sizeof(buf), 0);
+ while (true) {
+ int len = TEMP_FAILURE_RETRY(recv(socket, buf, sizeof(buf), 0));
- if (len <= 0) {
- if (len < 0 && errno == EINTR)
- continue;
- if (len < 0 && errno == EAGAIN)
+ if (len == 0) {
+ D("terminating JDWP %d connection: EOF", proc->pid);
+ break;
+ } else if (len < 0) {
+ if (len < 0 && errno == EAGAIN) {
return;
- else {
- D("terminating JDWP %d connection: %s\n", proc->pid,
- strerror(errno));
- break;
}
- }
- else {
- D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",
- proc->pid, len );
+
+ D("terminating JDWP %d connection: EOF", proc->pid);
+ break;
+ } else {
+ D("ignoring unexpected JDWP %d control socket activity (%d bytes)", proc->pid,
+ len);
}
}
- CloseProcess:
- if (proc->pid >= 0)
- D( "remove pid %d to jdwp process list\n", proc->pid );
- jdwp_process_free(proc);
- return;
+ goto CloseProcess;
}
}
if (events & FDE_WRITE) {
- D("trying to write to JDWP pid controli (count=%d first=%d) %d\n",
- proc->pid, proc->out_count, proc->out_fds[0]);
- if (proc->out_count > 0) {
- int fd = proc->out_fds[0];
- int n, ret;
- struct cmsghdr* cmsg;
- struct msghdr msg;
- struct iovec iov;
- char dummy = '!';
- char buffer[sizeof(struct cmsghdr) + sizeof(int)];
- int flags;
+ D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size());
+ if (!proc->out_fds.empty()) {
+ int fd = proc->out_fds.back().get();
+ struct cmsghdr* cmsg;
+ struct msghdr msg;
+ struct iovec iov;
+ char dummy = '!';
+ char buffer[sizeof(struct cmsghdr) + sizeof(int)];
- iov.iov_base = &dummy;
- iov.iov_len = 1;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = buffer;
+ iov.iov_base = &dummy;
+ iov.iov_len = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = buffer;
msg.msg_controllen = sizeof(buffer);
cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = msg.msg_controllen;
+ cmsg->cmsg_len = msg.msg_controllen;
cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_type = SCM_RIGHTS;
((int*)CMSG_DATA(cmsg))[0] = fd;
- flags = fcntl(proc->socket,F_GETFL,0);
-
- if (flags == -1) {
- D("failed to get cntl flags for socket %d: %s\n",
- proc->pid, strerror(errno));
- goto CloseProcess;
-
- }
-
- if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
- D("failed to remove O_NONBLOCK flag for socket %d: %s\n",
- proc->pid, strerror(errno));
+ if (!set_file_block_mode(proc->socket, true)) {
+ VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
goto CloseProcess;
}
- for (;;) {
- ret = sendmsg(proc->socket, &msg, 0);
- if (ret >= 0) {
- adb_close(fd);
- break;
- }
- if (errno == EINTR)
- continue;
- D("sending new file descriptor to JDWP %d failed: %s\n",
- proc->pid, strerror(errno));
+ int ret = TEMP_FAILURE_RETRY(sendmsg(proc->socket, &msg, 0));
+ if (ret < 0) {
+ D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
goto CloseProcess;
}
- D("sent file descriptor %d to JDWP process %d\n",
- fd, proc->pid);
+ adb_close(fd);
- for (n = 1; n < proc->out_count; n++)
- proc->out_fds[n-1] = proc->out_fds[n];
+ D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
- if (fcntl(proc->socket, F_SETFL, flags) == -1) {
- D("failed to set O_NONBLOCK flag for socket %d: %s\n",
- proc->pid, strerror(errno));
+ proc->out_fds.pop_back();
+
+ if (!set_file_block_mode(proc->socket, false)) {
+ VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
goto CloseProcess;
}
- if (--proc->out_count == 0)
- fdevent_del( proc->fde, FDE_WRITE );
+ if (proc->out_fds.empty()) {
+ fdevent_del(proc->fde, FDE_WRITE);
+ }
}
}
+
+ return;
+
+CloseProcess:
+ proc->RemoveFromList();
+ jdwp_process_list_updated();
}
+int create_jdwp_connection_fd(int pid) {
+ D("looking for pid %d in JDWP process list", pid);
-int
-create_jdwp_connection_fd(int pid)
-{
- JdwpProcess* proc = _jdwp_list.next;
-
- D("looking for pid %d in JDWP process list\n", pid);
- for ( ; proc != &_jdwp_list; proc = proc->next ) {
+ for (auto& proc : _jdwp_list) {
if (proc->pid == pid) {
- goto FoundIt;
+ int fds[2];
+
+ if (adb_socketpair(fds) < 0) {
+ D("%s: socket pair creation failed: %s", __FUNCTION__, strerror(errno));
+ return -1;
+ }
+ D("socketpair: (%d,%d)", fds[0], fds[1]);
+
+ proc->out_fds.emplace_back(fds[1]);
+ if (proc->out_fds.size() == 1) {
+ fdevent_add(proc->fde, FDE_WRITE);
+ }
+
+ return fds[0];
}
}
- D("search failed !!\n");
+ D("search failed !!");
return -1;
-
-FoundIt:
- {
- int fds[2];
-
- if (proc->out_count >= MAX_OUT_FDS) {
- D("%s: too many pending JDWP connection for pid %d\n",
- __FUNCTION__, pid);
- return -1;
- }
-
- if (adb_socketpair(fds) < 0) {
- D("%s: socket pair creation failed: %s\n",
- __FUNCTION__, strerror(errno));
- return -1;
- }
- D("socketpair: (%d,%d)\n", fds[0], fds[1]);
-
- proc->out_fds[ proc->out_count ] = fds[1];
- if (++proc->out_count == 1)
- fdevent_add( proc->fde, FDE_WRITE );
-
- return fds[0];
- }
}
/** VM DEBUG CONTROL SOCKET
@@ -452,33 +377,27 @@
**/
/* name of the debug control Unix socket */
-#define JDWP_CONTROL_NAME "\0jdwp-control"
-#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME)-1)
+#define JDWP_CONTROL_NAME "\0jdwp-control"
+#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1)
struct JdwpControl {
- int listen_socket;
- fdevent* fde;
+ int listen_socket;
+ fdevent* fde;
};
+static JdwpControl _jdwp_control;
-static void
-jdwp_control_event(int s, unsigned events, void* user);
+static void jdwp_control_event(int s, unsigned events, void* user);
-
-static int
-jdwp_control_init( JdwpControl* control,
- const char* sockname,
- int socknamelen )
-{
- struct sockaddr_un addr;
- socklen_t addrlen;
- int s;
- int maxpath = sizeof(addr.sun_path);
- int pathlen = socknamelen;
+static int jdwp_control_init(JdwpControl* control, const char* sockname, int socknamelen) {
+ sockaddr_un addr;
+ socklen_t addrlen;
+ int s;
+ int maxpath = sizeof(addr.sun_path);
+ int pathlen = socknamelen;
if (pathlen >= maxpath) {
- D( "vm debug control socket name too long (%d extra chars)\n",
- pathlen+1-maxpath );
+ D("vm debug control socket name too long (%d extra chars)", pathlen + 1 - maxpath);
return -1;
}
@@ -486,25 +405,22 @@
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, sockname, socknamelen);
- s = socket( AF_UNIX, SOCK_STREAM, 0 );
+ s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (s < 0) {
- D( "could not create vm debug control socket. %d: %s\n",
- errno, strerror(errno));
+ D("could not create vm debug control socket. %d: %s", errno, strerror(errno));
return -1;
}
- addrlen = (pathlen + sizeof(addr.sun_family));
+ addrlen = pathlen + sizeof(addr.sun_family);
- if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) {
- D( "could not bind vm debug control socket: %d: %s\n",
- errno, strerror(errno) );
+ if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
+ D("could not bind vm debug control socket: %d: %s", errno, strerror(errno));
adb_close(s);
return -1;
}
- if ( listen(s, 4) < 0 ) {
- D("listen failed in jdwp control socket: %d: %s\n",
- errno, strerror(errno));
+ if (listen(s, 4) < 0) {
+ D("listen failed in jdwp control socket: %d: %s", errno, strerror(errno));
adb_close(s);
return -1;
}
@@ -513,127 +429,106 @@
control->fde = fdevent_create(s, jdwp_control_event, control);
if (control->fde == NULL) {
- D( "could not create fdevent for jdwp control socket\n" );
+ D("could not create fdevent for jdwp control socket");
adb_close(s);
return -1;
}
/* only wait for incoming connections */
fdevent_add(control->fde, FDE_READ);
- close_on_exec(s);
- D("jdwp control socket started (%d)\n", control->listen_socket);
+ D("jdwp control socket started (%d)", control->listen_socket);
return 0;
}
-
-static void
-jdwp_control_event( int s, unsigned events, void* _control )
-{
- JdwpControl* control = (JdwpControl*) _control;
+static void jdwp_control_event(int s, unsigned events, void* _control) {
+ JdwpControl* control = (JdwpControl*)_control;
if (events & FDE_READ) {
- struct sockaddr addr;
- socklen_t addrlen = sizeof(addr);
- int s = -1;
- JdwpProcess* proc;
-
- do {
- s = adb_socket_accept( control->listen_socket, &addr, &addrlen );
- if (s < 0) {
- if (errno == EINTR)
- continue;
- if (errno == ECONNABORTED) {
- /* oops, the JDWP process died really quick */
- D("oops, the JDWP process died really quick\n");
- return;
- }
+ int s = adb_socket_accept(control->listen_socket, nullptr, nullptr);
+ if (s < 0) {
+ if (errno == ECONNABORTED) {
+ /* oops, the JDWP process died really quick */
+ D("oops, the JDWP process died really quick");
+ return;
+ } else {
/* the socket is probably closed ? */
- D( "weird accept() failed on jdwp control socket: %s\n",
- strerror(errno) );
+ D("weird accept() failed on jdwp control socket: %s", strerror(errno));
return;
}
}
- while (s < 0);
- proc = jdwp_process_alloc( s );
- if (proc == NULL)
- return;
+ auto proc = std::make_unique<JdwpProcess>(s);
+ if (!proc) {
+ fatal("failed to allocate JdwpProcess");
+ }
+
+ _jdwp_list.emplace_back(std::move(proc));
}
}
-
-static JdwpControl _jdwp_control;
-
/** "jdwp" local service implementation
** this simply returns the list of known JDWP process pids
**/
-struct JdwpSocket {
- asocket socket;
- int pass;
+struct JdwpSocket : public asocket {
+ bool pass;
};
-static void
-jdwp_socket_close( asocket* s )
-{
- asocket* peer = s->peer;
+static void jdwp_socket_close(asocket* s) {
+ D("LS(%d): closing jdwp socket", s->id);
+
+ if (s->peer) {
+ D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
+ s->peer->peer = nullptr;
+ s->peer->close(s->peer);
+ s->peer = nullptr;
+ }
remove_socket(s);
-
- if (peer) {
- peer->peer = NULL;
- peer->close(peer);
- }
free(s);
}
-static int
-jdwp_socket_enqueue( asocket* s, apacket* p )
-{
+static int jdwp_socket_enqueue(asocket* s, apacket* p) {
/* you can't write to this asocket */
+ D("LS(%d): JDWP socket received data?", s->id);
put_apacket(p);
s->peer->close(s->peer);
return -1;
}
+static void jdwp_socket_ready(asocket* s) {
+ JdwpSocket* jdwp = (JdwpSocket*)s;
+ asocket* peer = jdwp->peer;
-static void
-jdwp_socket_ready( asocket* s )
-{
- JdwpSocket* jdwp = (JdwpSocket*)s;
- asocket* peer = jdwp->socket.peer;
-
- /* on the first call, send the list of pids,
- * on the second one, close the connection
- */
- if (jdwp->pass == 0) {
- apacket* p = get_apacket();
+ /* on the first call, send the list of pids,
+ * on the second one, close the connection
+ */
+ if (!jdwp->pass) {
+ apacket* p = get_apacket();
p->len = jdwp_process_list((char*)p->data, s->get_max_payload());
peer->enqueue(peer, p);
- jdwp->pass = 1;
- }
- else {
+ jdwp->pass = true;
+ } else {
peer->close(peer);
}
}
-asocket*
-create_jdwp_service_socket( void )
-{
+asocket* create_jdwp_service_socket(void) {
JdwpSocket* s = reinterpret_cast<JdwpSocket*>(calloc(sizeof(*s), 1));
- if (s == NULL)
- return NULL;
+ if (!s) {
+ fatal("failed to allocate JdwpSocket");
+ }
- install_local_socket(&s->socket);
+ install_local_socket(s);
- s->socket.ready = jdwp_socket_ready;
- s->socket.enqueue = jdwp_socket_enqueue;
- s->socket.close = jdwp_socket_close;
- s->pass = 0;
+ s->ready = jdwp_socket_ready;
+ s->enqueue = jdwp_socket_enqueue;
+ s->close = jdwp_socket_close;
+ s->pass = false;
- return &s->socket;
+ return s;
}
/** "track-jdwp" local service implementation
@@ -641,113 +536,88 @@
** to the client...
**/
-struct JdwpTracker {
- asocket socket;
- JdwpTracker* next;
- JdwpTracker* prev;
- int need_update;
+struct JdwpTracker : public asocket {
+ bool need_initial;
};
-static JdwpTracker _jdwp_trackers_list;
+static std::vector<std::unique_ptr<JdwpTracker>> _jdwp_trackers;
+static void jdwp_process_list_updated(void) {
+ char buffer[1024];
+ int len = jdwp_process_list_msg(buffer, sizeof(buffer));
-static void
-jdwp_process_list_updated(void)
-{
- char buffer[1024];
- int len;
- JdwpTracker* t = _jdwp_trackers_list.next;
-
- len = jdwp_process_list_msg(buffer, sizeof(buffer));
-
- for ( ; t != &_jdwp_trackers_list; t = t->next ) {
- apacket* p = get_apacket();
- asocket* peer = t->socket.peer;
+ for (auto& t : _jdwp_trackers) {
+ apacket* p = get_apacket();
memcpy(p->data, buffer, len);
p->len = len;
- peer->enqueue( peer, p );
+
+ if (t->peer) {
+ // The tracker might not have been connected yet.
+ t->peer->enqueue(t->peer, p);
+ }
}
}
-static void
-jdwp_tracker_close( asocket* s )
-{
- JdwpTracker* tracker = (JdwpTracker*) s;
- asocket* peer = s->peer;
+static void jdwp_tracker_close(asocket* s) {
+ D("LS(%d): destroying jdwp tracker service", s->id);
- if (peer) {
- peer->peer = NULL;
- peer->close(peer);
+ if (s->peer) {
+ D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
+ s->peer->peer = nullptr;
+ s->peer->close(s->peer);
+ s->peer = nullptr;
}
remove_socket(s);
- tracker->prev->next = tracker->next;
- tracker->next->prev = tracker->prev;
-
- free(s);
+ auto pred = [s](const auto& tracker) { return tracker.get() == s; };
+ std::remove_if(_jdwp_trackers.begin(), _jdwp_trackers.end(), pred);
}
-static void
-jdwp_tracker_ready( asocket* s )
-{
- JdwpTracker* t = (JdwpTracker*) s;
+static void jdwp_tracker_ready(asocket* s) {
+ JdwpTracker* t = (JdwpTracker*)s;
- if (t->need_update) {
- apacket* p = get_apacket();
- t->need_update = 0;
+ if (t->need_initial) {
+ apacket* p = get_apacket();
+ t->need_initial = false;
p->len = jdwp_process_list_msg((char*)p->data, s->get_max_payload());
s->peer->enqueue(s->peer, p);
}
}
-static int
-jdwp_tracker_enqueue( asocket* s, apacket* p )
-{
+static int jdwp_tracker_enqueue(asocket* s, apacket* p) {
/* you can't write to this socket */
+ D("LS(%d): JDWP tracker received data?", s->id);
put_apacket(p);
s->peer->close(s->peer);
return -1;
}
+asocket* create_jdwp_tracker_service_socket(void) {
+ auto t = std::make_unique<JdwpTracker>();
+ if (!t) {
+ fatal("failed to allocate JdwpTracker");
+ }
-asocket*
-create_jdwp_tracker_service_socket( void )
-{
- JdwpTracker* t = reinterpret_cast<JdwpTracker*>(calloc(sizeof(*t), 1));
+ memset(t.get(), 0, sizeof(asocket));
- if (t == NULL)
- return NULL;
+ install_local_socket(t.get());
+ D("LS(%d): created new jdwp tracker service", t->id);
- t->next = &_jdwp_trackers_list;
- t->prev = t->next->prev;
+ t->ready = jdwp_tracker_ready;
+ t->enqueue = jdwp_tracker_enqueue;
+ t->close = jdwp_tracker_close;
+ t->need_initial = true;
- t->next->prev = t;
- t->prev->next = t;
+ asocket* result = t.get();
- install_local_socket(&t->socket);
+ _jdwp_trackers.emplace_back(std::move(t));
- t->socket.ready = jdwp_tracker_ready;
- t->socket.enqueue = jdwp_tracker_enqueue;
- t->socket.close = jdwp_tracker_close;
- t->need_update = 1;
-
- return &t->socket;
+ return result;
}
-
-int
-init_jdwp(void)
-{
- _jdwp_list.next = &_jdwp_list;
- _jdwp_list.prev = &_jdwp_list;
-
- _jdwp_trackers_list.next = &_jdwp_trackers_list;
- _jdwp_trackers_list.prev = &_jdwp_trackers_list;
-
- return jdwp_control_init( &_jdwp_control,
- JDWP_CONTROL_NAME,
- JDWP_CONTROL_NAME_LEN );
+int init_jdwp(void) {
+ return jdwp_control_init(&_jdwp_control, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
}
#endif /* !ADB_HOST */
diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp
new file mode 100644
index 0000000..64d10b6
--- /dev/null
+++ b/adb/line_printer.cpp
@@ -0,0 +1,128 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "line_printer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/time.h>
+#endif
+
+// Make sure printf is really adb_printf which works for UTF-8 on Windows.
+#include <sysdeps.h>
+
+// Stuff from ninja's util.h that's needed below.
+#include <vector>
+using namespace std;
+string ElideMiddle(const string& str, size_t width) {
+ const int kMargin = 3; // Space for "...".
+ string result = str;
+ if (result.size() + kMargin > width) {
+ size_t elide_size = (width - kMargin) / 2;
+ result = result.substr(0, elide_size)
+ + "..."
+ + result.substr(result.size() - elide_size, elide_size);
+ }
+ return result;
+}
+
+LinePrinter::LinePrinter() : have_blank_line_(true) {
+#ifndef _WIN32
+ const char* term = getenv("TERM");
+ smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
+#else
+ // Disable output buffer. It'd be nice to use line buffering but
+ // MSDN says: "For some systems, [_IOLBF] provides line
+ // buffering. However, for Win32, the behavior is the same as _IOFBF
+ // - Full Buffering."
+ setvbuf(stdout, NULL, _IONBF, 0);
+ console_ = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
+#endif
+}
+
+static void Out(const std::string& s) {
+ // Avoid printf and C strings, since the actual output might contain null
+ // bytes like UTF-16 does (yuck).
+ fwrite(s.data(), 1, s.size(), stdout);
+}
+
+void LinePrinter::Print(string to_print, LineType type) {
+ if (!smart_terminal_) {
+ Out(to_print + "\n");
+ return;
+ }
+
+ // Print over previous line, if any.
+ // On Windows, calling a C library function writing to stdout also handles
+ // pausing the executable when the "Pause" key or Ctrl-S is pressed.
+ printf("\r");
+
+ if (type == INFO) {
+#ifdef _WIN32
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ GetConsoleScreenBufferInfo(console_, &csbi);
+
+ // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)...
+ // TODO: wstring ElideMiddle.
+ to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
+ // We don't want to have the cursor spamming back and forth, so instead of
+ // printf use WriteConsoleOutput which updates the contents of the buffer,
+ // but doesn't move the cursor position.
+ COORD buf_size = { csbi.dwSize.X, 1 };
+ COORD zero_zero = { 0, 0 };
+ SMALL_RECT target = {
+ csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
+ static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
+ csbi.dwCursorPosition.Y
+ };
+ vector<CHAR_INFO> char_data(csbi.dwSize.X);
+ for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
+ // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
+ char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
+ char_data[i].Attributes = csbi.wAttributes;
+ }
+ // TODO: WriteConsoleOutputW.
+ WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
+#else
+ // Limit output to width of the terminal if provided so we don't cause
+ // line-wrapping.
+ winsize size;
+ if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
+ to_print = ElideMiddle(to_print, size.ws_col);
+ }
+ Out(to_print);
+ printf("\x1B[K"); // Clear to end of line.
+ fflush(stdout);
+#endif
+
+ have_blank_line_ = false;
+ } else {
+ Out(to_print);
+ Out("\n");
+ have_blank_line_ = true;
+ }
+}
+
+void LinePrinter::KeepInfoLine() {
+ if (!have_blank_line_) Out("\n");
+ have_blank_line_ = true;
+}
diff --git a/adb/line_printer.h b/adb/line_printer.h
new file mode 100644
index 0000000..42345e2
--- /dev/null
+++ b/adb/line_printer.h
@@ -0,0 +1,50 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 NINJA_LINE_PRINTER_H_
+#define NINJA_LINE_PRINTER_H_
+
+#include <stddef.h>
+#include <string>
+
+/// Prints lines of text, possibly overprinting previously printed lines
+/// if the terminal supports it.
+struct LinePrinter {
+ LinePrinter();
+
+ bool is_smart_terminal() const { return smart_terminal_; }
+ void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
+
+ enum LineType { INFO, WARNING, ERROR };
+
+ /// Outputs the given line. INFO output will be overwritten.
+ /// WARNING and ERROR appear on a line to themselves.
+ void Print(std::string to_print, LineType type);
+
+ /// If there's an INFO line, keep it. If not, do nothing.
+ void KeepInfoLine();
+
+ private:
+ /// Whether we can do fancy terminal control codes.
+ bool smart_terminal_;
+
+ /// Whether the caret is at the beginning of a blank line.
+ bool have_blank_line_;
+
+#ifdef _WIN32
+ void* console_;
+#endif
+};
+
+#endif // NINJA_LINE_PRINTER_H_
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
deleted file mode 100644
index 9003361..0000000
--- a/adb/mutex_list.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* the list of mutexes used by adb */
-/* #ifndef __MUTEX_LIST_H
- * Do not use an include-guard. This file is included once to declare the locks
- * and once in win32 to actually do the runtime initialization.
- */
-#ifndef ADB_MUTEX
-#error ADB_MUTEX not defined when including this file
-#endif
-ADB_MUTEX(dirname_lock)
-ADB_MUTEX(socket_list_lock)
-ADB_MUTEX(transport_lock)
-#if ADB_HOST
-ADB_MUTEX(local_transports_lock)
-#endif
-ADB_MUTEX(usb_lock)
-
-// Sadly logging to /data/adb/adb-... is not thread safe.
-// After modifying adb.h::D() to count invocations:
-// DEBUG(jpa):0:Handling main()
-// DEBUG(jpa):1:[ usb_init - starting thread ]
-// (Oopsies, no :2:, and matching message is also gone.)
-// DEBUG(jpa):3:[ usb_thread - opening device ]
-// DEBUG(jpa):4:jdwp control socket started (10)
-ADB_MUTEX(D_lock)
-
-#undef ADB_MUTEX
diff --git a/adb/protocol.txt b/adb/protocol.txt
index 5c7c6ba..55ea87f 100644
--- a/adb/protocol.txt
+++ b/adb/protocol.txt
@@ -29,12 +29,12 @@
32 bit words which are sent across the wire in little endian format.
struct message {
- unsigned command; /* command identifier constant */
- unsigned arg0; /* first argument */
- unsigned arg1; /* second argument */
- unsigned data_length; /* length of payload (0 is allowed) */
- unsigned data_crc32; /* crc32 of data payload */
- unsigned magic; /* command ^ 0xffffffff */
+ unsigned command; /* command identifier constant (A_CNXN, ...) */
+ unsigned arg0; /* first argument */
+ unsigned arg1; /* second argument */
+ unsigned data_length; /* length of payload (0 is allowed) */
+ unsigned data_crc32; /* crc32 of data payload */
+ unsigned magic; /* command ^ 0xffffffff */
};
Receipt of an invalid message header, corrupt message payload, or an
@@ -55,6 +55,8 @@
--- CONNECT(version, maxdata, "system-identity-string") ----------------
+Command constant: A_CNXN
+
The CONNECT message establishes the presence of a remote system.
The version is used to ensure protocol compatibility and maxdata
declares the maximum message body size that the remote system
@@ -80,6 +82,8 @@
--- AUTH(type, 0, "data") ----------------------------------------------
+Command constant: A_AUTH
+
The AUTH message informs the recipient that authentication is required to
connect to the sender. If type is TOKEN(1), data is a random token that
the recipient can sign with a private key. The recipient replies with an
@@ -98,6 +102,8 @@
--- OPEN(local-id, 0, "destination") -----------------------------------
+Command constant: A_OPEN
+
The OPEN message informs the recipient that the sender has a stream
identified by local-id that it wishes to connect to the named
destination in the message payload. The local-id may not be zero.
@@ -120,11 +126,13 @@
--- READY(local-id, remote-id, "") -------------------------------------
+Command constant: A_OKAY
+
The READY message informs the recipient that the sender's stream
identified by local-id is ready for write messages and that it is
connected to the recipient's stream identified by remote-id.
-Neither the local-id nor the remote-id may be zero.
+Neither the local-id nor the remote-id may be zero.
A READY message containing a remote-id which does not map to an open
stream on the recipient's side is ignored. The stream may have been
@@ -135,9 +143,10 @@
not change on later READY messages sent to the same stream.
-
--- WRITE(local-id, remote-id, "data") ---------------------------------
+Command constant: A_WRTE
+
The WRITE message sends data to the recipient's stream identified by
remote-id. The payload MUST be <= maxdata in length.
@@ -154,6 +163,8 @@
--- CLOSE(local-id, remote-id, "") -------------------------------------
+Command constant: A_CLSE
+
The CLOSE message informs recipient that the connection between the
sender's stream (local-id) and the recipient's stream (remote-id) is
broken. The remote-id MUST not be zero, but the local-id MAY be zero
@@ -170,12 +181,14 @@
--- SYNC(online, sequence, "") -----------------------------------------
+Command constant: A_SYNC
+
The SYNC message is used by the io pump to make sure that stale
outbound messages are discarded when the connection to the remote side
is broken. It is only used internally to the bridge and never valid
-to send across the wire.
+to send across the wire.
-* when the connection to the remote side goes offline, the io pump
+* when the connection to the remote side goes offline, the io pump
sends a SYNC(0, 0) and starts discarding all messages
* when the connection to the remote side is established, the io pump
sends a SYNC(1, token) and continues to discard messages
diff --git a/adb/qemu_tracing.cpp b/adb/qemu_tracing.cpp
deleted file mode 100644
index f31eae8..0000000
--- a/adb/qemu_tracing.cpp
+++ /dev/null
@@ -1,69 +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.
- */
-
-/*
- * Implements ADB tracing inside the emulator.
- */
-
-#include <stdarg.h>
-
-#include "sysdeps.h"
-#include "qemu_tracing.h"
-
-/*
- * Redefine open and write for qemu_pipe.h that contains inlined references
- * to those routines. We will redifine them back after qemu_pipe.h inclusion.
- */
-
-#undef open
-#undef write
-#define open adb_open
-#define write adb_write
-#include <hardware/qemu_pipe.h>
-#undef open
-#undef write
-#define open ___xxx_open
-#define write ___xxx_write
-
-/* A handle to adb-debug qemud service in the emulator. */
-int adb_debug_qemu = -1;
-
-/* Initializes connection with the adb-debug qemud service in the emulator. */
-int adb_qemu_trace_init(void)
-{
- char con_name[32];
-
- if (adb_debug_qemu >= 0) {
- return 0;
- }
-
- /* adb debugging QEMUD service connection request. */
- snprintf(con_name, sizeof(con_name), "qemud:adb-debug");
- adb_debug_qemu = qemu_pipe_open(con_name);
- return (adb_debug_qemu >= 0) ? 0 : -1;
-}
-
-void adb_qemu_trace(const char* fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- char msg[1024];
-
- if (adb_debug_qemu >= 0) {
- vsnprintf(msg, sizeof(msg), fmt, args);
- adb_write(adb_debug_qemu, msg, strlen(msg));
- }
-}
diff --git a/adb/qemu_tracing.h b/adb/qemu_tracing.h
deleted file mode 100644
index ff42d4f..0000000
--- a/adb/qemu_tracing.h
+++ /dev/null
@@ -1,28 +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.
- */
-
-/*
- * Implements ADB tracing inside the emulator.
- */
-
-#ifndef __QEMU_TRACING_H
-#define __QEMU_TRACING_H
-
-/* Initializes connection with the adb-debug qemud service in the emulator. */
-int adb_qemu_trace_init(void);
-void adb_qemu_trace(const char* fmt, ...);
-
-#endif /* __QEMU_TRACING_H */
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
index 2893263..5ca73cc 100644
--- a/adb/remount_service.cpp
+++ b/adb/remount_service.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
#include "sysdeps.h"
@@ -29,14 +29,13 @@
#include <string>
+#include <android-base/properties.h>
+
#include "adb.h"
#include "adb_io.h"
#include "adb_utils.h"
-#include "cutils/properties.h"
#include "fs_mgr.h"
-const std::string kFstab_Prefix = "/fstab.";
-
// Returns the device used to mount a directory in /proc/mounts.
static std::string find_proc_mount(const char* dir) {
std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
@@ -55,10 +54,7 @@
// Returns the device used to mount a directory in the fstab.
static std::string find_fstab_mount(const char* dir) {
- char propbuf[PROPERTY_VALUE_MAX];
-
- property_get("ro.hardware", propbuf, "");
- std::string fstab_filename = kFstab_Prefix + propbuf;
+ std::string fstab_filename = "/fstab." + android::base::GetProperty("ro.hardware", "");
struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str());
struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir);
std::string dev = rec ? std::string(rec->blk_device) : "";
@@ -115,12 +111,8 @@
return;
}
- char prop_buf[PROPERTY_VALUE_MAX];
- property_get("partition.system.verified", prop_buf, "");
- bool system_verified = (strlen(prop_buf) > 0);
-
- property_get("partition.vendor.verified", prop_buf, "");
- bool vendor_verified = (strlen(prop_buf) > 0);
+ 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) {
// Allow remount but warn of likely bad effects
@@ -138,9 +130,7 @@
}
bool success = true;
- property_get("ro.build.system_root_image", prop_buf, "");
- bool system_root = !strcmp(prop_buf, "true");
- if (system_root) {
+ if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
success &= remount_partition(fd, "/");
} else {
success &= remount_partition(fd, "/system");
diff --git a/adb/security_log_tags.h b/adb/security_log_tags.h
new file mode 100644
index 0000000..1d02744
--- /dev/null
+++ b/adb/security_log_tags.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __SECURITY_LOG_TAGS_H
+#define __SECURITY_LOG_TAGS_H
+
+/* TODO: Automatically generate this file from the logtags file when build
+ * infrastructure is in place.
+ * Defined in frameworks/base/core/java/android/auditing/SecurityLog.logtags
+ */
+#define SEC_TAG_ADB_SHELL_INTERACTIVE 210001
+#define SEC_TAG_ADB_SHELL_CMD 210002
+#define SEC_TAG_ADB_RECV_FILE 210003
+#define SEC_TAG_ADB_SEND_FILE 210004
+
+#endif
diff --git a/adb/services.cpp b/adb/services.cpp
index 255be09..df1b134 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_SERVICES
+#define TRACE_TAG SERVICES
#include "sysdeps.h"
@@ -24,11 +24,6 @@
#include <stdlib.h>
#include <string.h>
-#if !ADB_HOST
-#include <pty.h>
-#include <termios.h>
-#endif
-
#ifndef _WIN32
#include <netdb.h>
#include <netinet/in.h>
@@ -36,14 +31,17 @@
#include <unistd.h>
#endif
-#include <base/file.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/sockets.h>
#if !ADB_HOST
-#include "cutils/android_reboot.h"
-#include "cutils/properties.h"
+#include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+#include <private/android_logger.h>
#endif
#include "adb.h"
@@ -51,6 +49,10 @@
#include "adb_utils.h"
#include "file_sync_service.h"
#include "remount_service.h"
+#include "services.h"
+#include "shell_service.h"
+#include "socket_spec.h"
+#include "sysdeps.h"
#include "transport.h"
struct stinfo {
@@ -59,17 +61,11 @@
void *cookie;
};
-enum class SubprocessType {
- kPty,
- kRaw,
-};
-
-void *service_bootstrap_func(void *x)
-{
+static void service_bootstrap_func(void* x) {
stinfo* sti = reinterpret_cast<stinfo*>(x);
+ adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
sti->func(sti->fd, sti->cookie);
free(sti);
- return 0;
}
#if !ADB_HOST
@@ -79,15 +75,13 @@
WriteFdExactly(fd, "adbd is already running as root\n");
adb_close(fd);
} else {
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.debuggable", value, "");
- if (strcmp(value, "1") != 0) {
+ if (!__android_log_is_debuggable()) {
WriteFdExactly(fd, "adbd cannot run as root in production builds\n");
adb_close(fd);
return;
}
- property_set("service.adb.root", "1");
+ android::base::SetProperty("service.adb.root", "1");
WriteFdExactly(fd, "restarting adbd as root\n");
adb_close(fd);
}
@@ -98,7 +92,7 @@
WriteFdExactly(fd, "adbd not running as root\n");
adb_close(fd);
} else {
- property_set("service.adb.root", "0");
+ android::base::SetProperty("service.adb.root", "0");
WriteFdExactly(fd, "restarting adbd as non root\n");
adb_close(fd);
}
@@ -112,15 +106,13 @@
return;
}
- char value[PROPERTY_VALUE_MAX];
- snprintf(value, sizeof(value), "%d", port);
- property_set("service.adb.tcp.port", value);
+ android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
WriteFdFmt(fd, "restarting in TCP mode port: %d\n", port);
adb_close(fd);
}
void restart_usb_service(int fd, void *cookie) {
- property_set("service.adb.tcp.port", "0");
+ android::base::SetProperty("service.adb.tcp.port", "0");
WriteFdExactly(fd, "restarting in USB mode\n");
adb_close(fd);
}
@@ -142,17 +134,12 @@
return false;
}
- const char* const recovery_dir = "/cache/recovery";
- const char* const command_file = "/cache/recovery/command";
- // Ensure /cache/recovery exists.
- if (adb_mkdir(recovery_dir, 0770) == -1 && errno != EEXIST) {
- D("Failed to create directory '%s': %s\n", recovery_dir, strerror(errno));
- return false;
- }
-
- bool write_status = android::base::WriteStringToFile(
- auto_reboot ? "--sideload_auto_reboot" : "--sideload", command_file);
- if (!write_status) {
+ const std::vector<std::string> options = {
+ auto_reboot ? "--sideload_auto_reboot" : "--sideload"
+ };
+ std::string err;
+ if (!write_bootloader_message(options, &err)) {
+ D("Failed to set bootloader message: %s", err.c_str());
return false;
}
@@ -161,16 +148,9 @@
sync();
- char property_val[PROPERTY_VALUE_MAX];
- int ret = snprintf(property_val, sizeof(property_val), "reboot,%s", reboot_arg);
- if (ret >= static_cast<int>(sizeof(property_val))) {
- WriteFdFmt(fd, "reboot string too long: %d\n", ret);
- return false;
- }
-
- ret = property_set(ANDROID_RB_PROPERTY, property_val);
- if (ret < 0) {
- WriteFdFmt(fd, "reboot failed: %d\n", ret);
+ std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
+ if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
+ WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
return false;
}
@@ -191,18 +171,67 @@
adb_close(fd);
}
-void reverse_service(int fd, void* arg)
-{
- const char* command = reinterpret_cast<const char*>(arg);
-
- if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) {
- SendFail(fd, "not a reverse forwarding command");
- }
- free(arg);
+static void reconnect_service(int fd, void* arg) {
+ WriteFdExactly(fd, "done");
adb_close(fd);
+ atransport* t = static_cast<atransport*>(arg);
+ kick_transport(t);
}
-#endif
+int reverse_service(const char* command) {
+ int s[2];
+ if (adb_socketpair(s)) {
+ PLOG(ERROR) << "cannot create service socket pair.";
+ return -1;
+ }
+ VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
+ if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
+ SendFail(s[1], "not a reverse forwarding command");
+ }
+ adb_close(s[1]);
+ return s[0];
+}
+
+// Shell service string can look like:
+// shell[,arg1,arg2,...]:[command]
+static int ShellService(const std::string& args, const atransport* transport) {
+ size_t delimiter_index = args.find(':');
+ if (delimiter_index == std::string::npos) {
+ LOG(ERROR) << "No ':' found in shell service arguments: " << args;
+ return -1;
+ }
+
+ const std::string service_args = args.substr(0, delimiter_index);
+ const std::string command = args.substr(delimiter_index + 1);
+
+ // Defaults:
+ // PTY for interactive, raw for non-interactive.
+ // No protocol.
+ // $TERM set to "dumb".
+ SubprocessType type(command.empty() ? SubprocessType::kPty
+ : SubprocessType::kRaw);
+ SubprocessProtocol protocol = SubprocessProtocol::kNone;
+ std::string terminal_type = "dumb";
+
+ for (const std::string& arg : android::base::Split(service_args, ",")) {
+ if (arg == kShellServiceArgRaw) {
+ type = SubprocessType::kRaw;
+ } else if (arg == kShellServiceArgPty) {
+ type = SubprocessType::kPty;
+ } else if (arg == kShellServiceArgShellProtocol) {
+ protocol = SubprocessProtocol::kShell;
+ } else if (android::base::StartsWith(arg, "TERM=")) {
+ terminal_type = arg.substr(5);
+ } else if (!arg.empty()) {
+ // This is not an error to allow for future expansion.
+ LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
+ }
+ }
+
+ return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
+}
+
+#endif // !ADB_HOST
static int create_service_thread(void (*func)(int, void *), void *cookie)
{
@@ -211,7 +240,7 @@
printf("cannot create service socket pair\n");
return -1;
}
- D("socketpair: (%d,%d)\n", s[0], s[1]);
+ D("socketpair: (%d,%d)", s[0], s[1]);
stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
if (sti == nullptr) {
@@ -229,249 +258,19 @@
return -1;
}
- D("service thread started, %d:%d\n",s[0], s[1]);
+ D("service thread started, %d:%d",s[0], s[1]);
return s[0];
}
-#if !ADB_HOST
-
-static void init_subproc_child()
-{
- setsid();
-
- // Set OOM score adjustment to prevent killing
- int fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC);
- if (fd >= 0) {
- adb_write(fd, "0", 1);
- adb_close(fd);
- } else {
- D("adb: unable to update oom_score_adj\n");
- }
-}
-
-#if !ADB_HOST
-static int create_subproc_pty(const char* cmd, const char* arg0,
- const char* arg1, pid_t* pid) {
- D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
- char pts_name[PATH_MAX];
- int ptm;
- *pid = forkpty(&ptm, pts_name, nullptr, nullptr);
- if (*pid == -1) {
- printf("- fork failed: %s -\n", strerror(errno));
- unix_close(ptm);
- return -1;
- }
-
- if (*pid == 0) {
- init_subproc_child();
-
- int pts = unix_open(pts_name, O_RDWR | O_CLOEXEC);
- if (pts == -1) {
- fprintf(stderr, "child failed to open pseudo-term slave %s: %s\n",
- pts_name, strerror(errno));
- unix_close(ptm);
- exit(-1);
- }
-
- // arg0 is "-c" in batch mode and "-" in interactive mode.
- if (strcmp(arg0, "-c") == 0) {
- termios tattr;
- if (tcgetattr(pts, &tattr) == -1) {
- fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno));
- unix_close(pts);
- unix_close(ptm);
- exit(-1);
- }
-
- cfmakeraw(&tattr);
- if (tcsetattr(pts, TCSADRAIN, &tattr) == -1) {
- fprintf(stderr, "tcsetattr failed: %s\n", strerror(errno));
- unix_close(pts);
- unix_close(ptm);
- exit(-1);
- }
- }
-
- dup2(pts, STDIN_FILENO);
- dup2(pts, STDOUT_FILENO);
- dup2(pts, STDERR_FILENO);
-
- unix_close(pts);
- unix_close(ptm);
-
- execl(cmd, cmd, arg0, arg1, nullptr);
- fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
- cmd, strerror(errno), errno);
- exit(-1);
- } else {
- return ptm;
- }
-}
-#endif // !ADB_HOST
-
-static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
-{
- D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
-#if defined(_WIN32)
- fprintf(stderr, "error: create_subproc_raw not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
- return -1;
-#else
-
- // 0 is parent socket, 1 is child socket
- int sv[2];
- if (adb_socketpair(sv) < 0) {
- printf("[ cannot create socket pair - %s ]\n", strerror(errno));
- return -1;
- }
- D("socketpair: (%d,%d)\n", sv[0], sv[1]);
-
- *pid = fork();
- if (*pid < 0) {
- printf("- fork failed: %s -\n", strerror(errno));
- adb_close(sv[0]);
- adb_close(sv[1]);
- return -1;
- }
-
- if (*pid == 0) {
- adb_close(sv[0]);
- init_subproc_child();
-
- dup2(sv[1], STDIN_FILENO);
- dup2(sv[1], STDOUT_FILENO);
- dup2(sv[1], STDERR_FILENO);
-
- adb_close(sv[1]);
-
- execl(cmd, cmd, arg0, arg1, NULL);
- fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
- cmd, strerror(errno), errno);
- exit(-1);
- } else {
- adb_close(sv[1]);
- return sv[0];
- }
-#endif /* !defined(_WIN32) */
-}
-#endif /* !ABD_HOST */
-
-#if ADB_HOST
-#define SHELL_COMMAND "/bin/sh"
-#else
-#define SHELL_COMMAND "/system/bin/sh"
-#endif
-
-#if !ADB_HOST
-static void subproc_waiter_service(int fd, void *cookie)
-{
- pid_t pid = (pid_t) (uintptr_t) cookie;
-
- D("entered. fd=%d of pid=%d\n", fd, pid);
- while (true) {
- int status;
- pid_t p = waitpid(pid, &status, 0);
- if (p == pid) {
- D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status);
- if (WIFSIGNALED(status)) {
- D("*** Killed by signal %d\n", WTERMSIG(status));
- break;
- } else if (!WIFEXITED(status)) {
- D("*** Didn't exit!!. status %d\n", status);
- break;
- } else if (WEXITSTATUS(status) >= 0) {
- D("*** Exit code %d\n", WEXITSTATUS(status));
- break;
- }
- }
- }
- D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno);
- if (SHELL_EXIT_NOTIFY_FD >=0) {
- int res;
- res = WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)) ? 0 : -1;
- D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n",
- SHELL_EXIT_NOTIFY_FD, pid, res, errno);
- }
-}
-
-// Starts a subprocess and spawns a thread to wait for the subprocess to finish
-// and trigger the necessary cleanup.
-//
-// |name| is the command to execute in the subprocess; empty string will start
-// an interactive session.
-// |type| selects between a PTY or raw subprocess.
-//
-// Returns an open file descriptor tied to the subprocess stdin/stdout/stderr.
-static int create_subproc_thread(const char *name, SubprocessType type) {
- const char *arg0, *arg1;
- if (*name == '\0') {
- arg0 = "-";
- arg1 = nullptr;
- } else {
- arg0 = "-c";
- arg1 = name;
- }
-
- pid_t pid = -1;
- int ret_fd;
- if (type == SubprocessType::kPty) {
- ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid);
- } else {
- ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid);
- }
- D("create_subproc ret_fd=%d pid=%d\n", ret_fd, pid);
-
- stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
- if(sti == 0) fatal("cannot allocate stinfo");
- sti->func = subproc_waiter_service;
- sti->cookie = (void*) (uintptr_t) pid;
- sti->fd = ret_fd;
-
- if (!adb_thread_create(service_bootstrap_func, sti)) {
- free(sti);
- adb_close(ret_fd);
- fprintf(stderr, "cannot create service thread\n");
- return -1;
- }
-
- D("service thread started, fd=%d pid=%d\n", ret_fd, pid);
- return ret_fd;
-}
-#endif
-
-int service_to_fd(const char *name)
-{
+int service_to_fd(const char* name, const atransport* transport) {
int ret = -1;
- if(!strncmp(name, "tcp:", 4)) {
- int port = atoi(name + 4);
- name = strchr(name + 4, ':');
- if(name == 0) {
- std::string error;
- ret = network_loopback_client(port, SOCK_STREAM, &error);
- if (ret >= 0)
- disable_tcp_nagle(ret);
- } else {
-#if ADB_HOST
- std::string error;
- ret = network_connect(name + 1, port, SOCK_STREAM, 0, &error);
-#else
- return -1;
-#endif
+ if (is_socket_spec(name)) {
+ std::string error;
+ ret = socket_spec_connect(name, &error);
+ if (ret < 0) {
+ LOG(ERROR) << "failed to connect to socket '" << name << "': " << error;
}
-#if !defined(_WIN32) /* winsock doesn't implement unix domain sockets */
- } else if(!strncmp(name, "local:", 6)) {
- ret = socket_local_client(name + 6,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
- } else if(!strncmp(name, "localreserved:", 14)) {
- ret = socket_local_client(name + 14,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
- } else if(!strncmp(name, "localabstract:", 14)) {
- ret = socket_local_client(name + 14,
- ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
- } else if(!strncmp(name, "localfilesystem:", 16)) {
- ret = socket_local_client(name + 16,
- ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
-#endif
#if !ADB_HOST
} else if(!strncmp("dev:", name, 4)) {
ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
@@ -479,17 +278,10 @@
ret = create_service_thread(framebuffer_service, 0);
} else if (!strncmp(name, "jdwp:", 5)) {
ret = create_jdwp_connection_fd(atoi(name+5));
- } else if(!strncmp(name, "shell:", 6)) {
- const char* args = name + 6;
- if (*args) {
- // Non-interactive session uses a raw subprocess.
- ret = create_subproc_thread(args, SubprocessType::kRaw);
- } else {
- // Interactive session uses a PTY subprocess.
- ret = create_subproc_thread(args, SubprocessType::kPty);
- }
+ } else if(!strncmp(name, "shell", 5)) {
+ ret = ShellService(name + 5, transport);
} else if(!strncmp(name, "exec:", 5)) {
- ret = create_subproc_thread(name + 5, SubprocessType::kRaw);
+ ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
} else if(!strncmp(name, "sync:", 5)) {
ret = create_service_thread(file_sync_service, NULL);
} else if(!strncmp(name, "remount:", 8)) {
@@ -503,13 +295,12 @@
} else if(!strncmp(name, "unroot:", 7)) {
ret = create_service_thread(restart_unroot_service, NULL);
} else if(!strncmp(name, "backup:", 7)) {
- ret = create_subproc_thread(
- android::base::StringPrintf("/system/bin/bu backup %s",
- (name + 7)).c_str(),
- SubprocessType::kRaw);
+ ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
+ (name + 7)).c_str(),
+ nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
} else if(!strncmp(name, "restore:", 8)) {
- ret = create_subproc_thread("/system/bin/bu restore",
- SubprocessType::kRaw);
+ ret = StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
+ SubprocessProtocol::kNone);
} else if(!strncmp(name, "tcpip:", 6)) {
int port;
if (sscanf(name + 6, "%d", &port) != 1) {
@@ -519,19 +310,13 @@
} else if(!strncmp(name, "usb:", 4)) {
ret = create_service_thread(restart_usb_service, NULL);
} else if (!strncmp(name, "reverse:", 8)) {
- char* cookie = strdup(name + 8);
- if (cookie == NULL) {
- ret = -1;
- } else {
- ret = create_service_thread(reverse_service, cookie);
- if (ret < 0) {
- free(cookie);
- }
- }
+ ret = reverse_service(name + 8);
} else if(!strncmp(name, "disable-verity:", 15)) {
ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
} else if(!strncmp(name, "enable-verity:", 15)) {
ret = create_service_thread(set_verity_enabled_state_service, (void*)1);
+ } else if (!strcmp(name, "reconnect")) {
+ ret = create_service_thread(reconnect_service, const_cast<atransport*>(transport));
#endif
}
if (ret >= 0) {
@@ -543,30 +328,44 @@
#if ADB_HOST
struct state_info {
TransportType transport_type;
- char* serial;
+ std::string serial;
ConnectionState state;
};
-static void wait_for_state(int fd, void* cookie)
-{
- state_info* sinfo = reinterpret_cast<state_info*>(cookie);
+static void wait_for_state(int fd, void* data) {
+ std::unique_ptr<state_info> sinfo(reinterpret_cast<state_info*>(data));
- D("wait_for_state %d\n", sinfo->state);
+ D("wait_for_state %d", sinfo->state);
- std::string error_msg = "unknown error";
- atransport* t = acquire_one_transport(sinfo->state, sinfo->transport_type, sinfo->serial,
- &error_msg);
- if (t != nullptr) {
- SendOkay(fd);
- } else {
- SendFail(fd, error_msg);
+ while (true) {
+ bool is_ambiguous = false;
+ std::string error = "unknown error";
+ const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
+ atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
+ if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->connection_state)) {
+ SendOkay(fd);
+ break;
+ } else if (!is_ambiguous) {
+ adb_pollfd pfd = {.fd = fd, .events = POLLIN };
+ int rc = adb_poll(&pfd, 1, 1000);
+ if (rc < 0) {
+ SendFail(fd, error);
+ break;
+ } else if (rc > 0 && (pfd.revents & POLLHUP) != 0) {
+ // The other end of the socket is closed, probably because the other side was
+ // terminated, bail out.
+ break;
+ }
+
+ // Try again...
+ } else {
+ SendFail(fd, error);
+ break;
+ }
}
- if (sinfo->serial)
- free(sinfo->serial);
- free(sinfo);
adb_close(fd);
- D("wait_for_state is done\n");
+ D("wait_for_state is done");
}
static void connect_device(const std::string& address, std::string* response) {
@@ -578,7 +377,7 @@
std::string serial;
std::string host;
int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
- if (!parse_host_and_port(address, &serial, &host, &port, response)) {
+ if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
return;
}
@@ -590,10 +389,15 @@
return;
}
- D("client: connected %s remote on fd %d\n", serial.c_str(), fd);
+ D("client: connected %s remote on fd %d", serial.c_str(), fd);
close_on_exec(fd);
disable_tcp_nagle(fd);
+ // Send a TCP keepalive ping to the device every second so we can detect disconnects.
+ if (!set_tcp_keepalive(fd, 1)) {
+ D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
+ }
+
int ret = register_socket_transport(fd, serial.c_str(), port, 0);
if (ret < 0) {
adb_close(fd);
@@ -667,42 +471,48 @@
#endif
#if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char *serial)
-{
+asocket* host_service_to_socket(const char* name, const char* serial) {
if (!strcmp(name,"track-devices")) {
return create_device_tracker();
- } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
- auto sinfo = reinterpret_cast<state_info*>(malloc(sizeof(state_info)));
- if (sinfo == nullptr) {
- fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
- return NULL;
- }
-
- if (serial)
- sinfo->serial = strdup(serial);
- else
- sinfo->serial = NULL;
-
+ } else if (android::base::StartsWith(name, "wait-for-")) {
name += strlen("wait-for-");
- if (!strncmp(name, "local", strlen("local"))) {
- sinfo->transport_type = kTransportLocal;
- sinfo->state = kCsDevice;
- } else if (!strncmp(name, "usb", strlen("usb"))) {
- sinfo->transport_type = kTransportUsb;
- sinfo->state = kCsDevice;
- } else if (!strncmp(name, "any", strlen("any"))) {
- sinfo->transport_type = kTransportAny;
- sinfo->state = kCsDevice;
- } else {
- if (sinfo->serial) {
- free(sinfo->serial);
- }
- free(sinfo);
- return NULL;
+ std::unique_ptr<state_info> sinfo(new state_info);
+ if (sinfo == nullptr) {
+ fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
+ return nullptr;
}
- int fd = create_service_thread(wait_for_state, sinfo);
+ if (serial) sinfo->serial = serial;
+
+ if (android::base::StartsWith(name, "local")) {
+ name += strlen("local");
+ sinfo->transport_type = kTransportLocal;
+ } else if (android::base::StartsWith(name, "usb")) {
+ name += strlen("usb");
+ sinfo->transport_type = kTransportUsb;
+ } else if (android::base::StartsWith(name, "any")) {
+ name += strlen("any");
+ sinfo->transport_type = kTransportAny;
+ } else {
+ return nullptr;
+ }
+
+ if (!strcmp(name, "-device")) {
+ sinfo->state = kCsDevice;
+ } else if (!strcmp(name, "-recovery")) {
+ sinfo->state = kCsRecovery;
+ } else if (!strcmp(name, "-sideload")) {
+ sinfo->state = kCsSideload;
+ } else if (!strcmp(name, "-bootloader")) {
+ sinfo->state = kCsBootloader;
+ } else if (!strcmp(name, "-any")) {
+ sinfo->state = kCsAny;
+ } else {
+ return nullptr;
+ }
+
+ int fd = create_service_thread(wait_for_state, sinfo.release());
return create_local_socket(fd);
} else if (!strncmp(name, "connect:", 8)) {
char* host = strdup(name + 8);
diff --git a/adb/services.h b/adb/services.h
new file mode 100644
index 0000000..0428ca4
--- /dev/null
+++ b/adb/services.h
@@ -0,0 +1,24 @@
+/*
+ * 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 SERVICES_H_
+#define SERVICES_H_
+
+constexpr char kShellServiceArgRaw[] = "raw";
+constexpr char kShellServiceArgPty[] = "pty";
+constexpr char kShellServiceArgShellProtocol[] = "v2";
+
+#endif // SERVICES_H_
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index bae38cf..f9e028b 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
#include "sysdeps.h"
@@ -24,15 +24,18 @@
#include <stdio.h>
#include <sys/stat.h>
-#include "cutils/properties.h"
+#include "android-base/properties.h"
+#include "android-base/stringprintf.h"
+#include <private/android_logger.h>
#include "adb.h"
#include "adb_io.h"
-#include "ext4_sb.h"
+#include "adb_unique_fd.h"
#include "fs_mgr.h"
#include "remount_service.h"
-#define FSTAB_PREFIX "/fstab."
+#include "fec/io.h"
+
struct fstab *fstab;
#ifdef ALLOW_ADBD_DISABLE_VERITY
@@ -41,167 +44,91 @@
static const bool kAllowDisableVerity = false;
#endif
-static int get_target_device_size(int fd, const char *blk_device,
- uint64_t *device_size)
-{
- int data_device;
- struct ext4_super_block sb;
- struct fs_info info;
-
- info.len = 0; /* Only len is set to 0 to ask the device for real size. */
-
- data_device = adb_open(blk_device, O_RDONLY | O_CLOEXEC);
- if (data_device < 0) {
- WriteFdFmt(fd, "Error opening block device (%s)\n", strerror(errno));
- return -1;
- }
-
- if (lseek64(data_device, 1024, SEEK_SET) < 0) {
- WriteFdFmt(fd, "Error seeking to superblock\n");
- adb_close(data_device);
- return -1;
- }
-
- if (adb_read(data_device, &sb, sizeof(sb)) != sizeof(sb)) {
- WriteFdFmt(fd, "Error reading superblock\n");
- adb_close(data_device);
- return -1;
- }
-
- ext4_parse_sb(&sb, &info);
- *device_size = info.len;
-
- adb_close(data_device);
- return 0;
-}
-
/* Turn verity on/off */
static int set_verity_enabled_state(int fd, const char *block_device,
const char* mount_point, bool enable)
{
- uint32_t magic_number;
- const uint32_t new_magic = enable ? VERITY_METADATA_MAGIC_NUMBER
- : VERITY_METADATA_MAGIC_DISABLE;
- uint64_t device_length = 0;
- int device = -1;
- int retval = -1;
-
if (!make_block_device_writable(block_device)) {
WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
block_device, strerror(errno));
- goto errout;
+ return -1;
}
- device = adb_open(block_device, O_RDWR | O_CLOEXEC);
- if (device == -1) {
+ fec::io fh(block_device, O_RDWR);
+
+ if (!fh) {
WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
- WriteFdFmt(fd, "Maybe run adb remount?\n");
- goto errout;
+ WriteFdFmt(fd, "Maybe run adb root?\n");
+ return -1;
}
- // find the start of the verity metadata
- if (get_target_device_size(fd, (char*)block_device, &device_length) < 0) {
- WriteFdFmt(fd, "Could not get target device size.\n");
- goto errout;
+ fec_verity_metadata metadata;
+
+ if (!fh.get_verity_metadata(metadata)) {
+ WriteFdFmt(fd, "Couldn't find verity metadata!\n");
+ return -1;
}
- if (lseek64(device, device_length, SEEK_SET) < 0) {
- WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
- goto errout;
- }
-
- // check the magic number
- if (adb_read(device, &magic_number, sizeof(magic_number)) != sizeof(magic_number)) {
- WriteFdFmt(fd, "Couldn't read magic number!\n");
- goto errout;
- }
-
- if (!enable && magic_number == VERITY_METADATA_MAGIC_DISABLE) {
+ if (!enable && metadata.disabled) {
WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
- goto errout;
+ return -1;
}
- if (enable && magic_number == VERITY_METADATA_MAGIC_NUMBER) {
+ if (enable && !metadata.disabled) {
WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
- goto errout;
+ return -1;
}
- if (magic_number != VERITY_METADATA_MAGIC_NUMBER
- && magic_number != VERITY_METADATA_MAGIC_DISABLE) {
- WriteFdFmt(fd, "Couldn't find verity metadata at offset %" PRIu64 "!\n", device_length);
- goto errout;
- }
-
- if (lseek64(device, device_length, SEEK_SET) < 0) {
- WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
- goto errout;
- }
-
- if (adb_write(device, &new_magic, sizeof(new_magic)) != sizeof(new_magic)) {
+ if (!fh.set_verity_status(enable)) {
WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
enable ? "enabled" : "disabled",
block_device, strerror(errno));
- goto errout;
+ return -1;
}
WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
- retval = 0;
-errout:
- if (device != -1)
- adb_close(device);
- return retval;
+ return 0;
}
-void set_verity_enabled_state_service(int fd, void* cookie)
-{
+void set_verity_enabled_state_service(int fd, void* cookie) {
+ unique_fd closer(fd);
+
bool enable = (cookie != NULL);
- if (kAllowDisableVerity) {
- char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
- char propbuf[PROPERTY_VALUE_MAX];
- int i;
- bool any_changed = false;
-
- property_get("ro.secure", propbuf, "0");
- if (strcmp(propbuf, "1")) {
- WriteFdFmt(fd, "verity not enabled - ENG build\n");
- goto errout;
- }
-
- property_get("ro.debuggable", propbuf, "0");
- if (strcmp(propbuf, "1")) {
- WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
- goto errout;
- }
-
- property_get("ro.hardware", propbuf, "");
- snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s",
- propbuf);
-
- fstab = fs_mgr_read_fstab(fstab_filename);
- if (!fstab) {
- WriteFdFmt(fd, "Failed to open %s\nMaybe run adb root?\n", fstab_filename);
- goto errout;
- }
-
- /* Loop through entries looking for ones that vold manages */
- for (i = 0; i < fstab->num_entries; i++) {
- if(fs_mgr_is_verified(&fstab->recs[i])) {
- if (!set_verity_enabled_state(fd, fstab->recs[i].blk_device,
- fstab->recs[i].mount_point,
- enable)) {
- any_changed = true;
- }
- }
- }
-
- if (any_changed) {
- WriteFdFmt(fd, "Now reboot your device for settings to take effect\n");
- }
- } else {
+ if (!kAllowDisableVerity) {
WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
enable ? "enable" : "disable");
}
-errout:
- adb_close(fd);
+ if (!android::base::GetBoolProperty("ro.secure", false)) {
+ WriteFdFmt(fd, "verity not enabled - ENG build\n");
+ return;
+ }
+ if (!__android_log_is_debuggable()) {
+ WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
+ return;
+ }
+
+ std::string fstab_filename = "/fstab." + android::base::GetProperty("ro.hardware", "");
+
+ fstab = fs_mgr_read_fstab(fstab_filename.c_str());
+ if (!fstab) {
+ WriteFdFmt(fd, "Failed to open %s\nMaybe run adb root?\n", fstab_filename.c_str());
+ return;
+ }
+
+ // Loop through entries looking for ones that vold manages.
+ bool any_changed = false;
+ for (int i = 0; i < fstab->num_entries; i++) {
+ if (fs_mgr_is_verified(&fstab->recs[i])) {
+ if (!set_verity_enabled_state(fd, fstab->recs[i].blk_device,
+ fstab->recs[i].mount_point,
+ enable)) {
+ any_changed = true;
+ }
+ }
+ }
+
+ if (any_changed) {
+ WriteFdFmt(fd, "Now reboot your device for settings to take effect\n");
+ }
}
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
new file mode 100644
index 0000000..4975fab
--- /dev/null
+++ b/adb/shell_service.cpp
@@ -0,0 +1,756 @@
+/*
+ * 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.
+ */
+
+// Functionality for launching and managing shell subprocesses.
+//
+// There are two types of subprocesses, PTY or raw. PTY is typically used for
+// an interactive session, raw for non-interactive. There are also two methods
+// of communication with the subprocess, passing raw data or using a simple
+// protocol to wrap packets. The protocol allows separating stdout/stderr and
+// passing the exit code back, but is not backwards compatible.
+// ----------------+--------------------------------------
+// Type Protocol | Exit code? Separate stdout/stderr?
+// ----------------+--------------------------------------
+// PTY No | No No
+// Raw No | No No
+// PTY Yes | Yes No
+// Raw Yes | Yes Yes
+// ----------------+--------------------------------------
+//
+// Non-protocol subprocesses work by passing subprocess stdin/out/err through
+// a single pipe which is registered with a local socket in adbd. The local
+// socket uses the fdevent loop to pass raw data between this pipe and the
+// transport, which then passes data back to the adb client. Cleanup is done by
+// waiting in a separate thread for the subprocesses to exit and then signaling
+// a separate fdevent to close out the local socket from the main loop.
+//
+// ------------------+-------------------------+------------------------------
+// Subprocess | adbd subprocess thread | adbd main fdevent loop
+// ------------------+-------------------------+------------------------------
+// | |
+// stdin/out/err <-----------------------------> LocalSocket
+// | | |
+// | | Block on exit |
+// | | * |
+// v | * |
+// Exit ---> Unblock |
+// | | |
+// | v |
+// | Notify shell exit FD ---> Close LocalSocket
+// ------------------+-------------------------+------------------------------
+//
+// The protocol requires the thread to intercept stdin/out/err in order to
+// wrap/unwrap data with shell protocol packets.
+//
+// ------------------+-------------------------+------------------------------
+// Subprocess | adbd subprocess thread | adbd main fdevent loop
+// ------------------+-------------------------+------------------------------
+// | |
+// stdin/out <---> Protocol <---> LocalSocket
+// stderr ---> Protocol ---> LocalSocket
+// | | |
+// v | |
+// Exit ---> Exit code protocol ---> LocalSocket
+// | | |
+// | v |
+// | Notify shell exit FD ---> Close LocalSocket
+// ------------------+-------------------------+------------------------------
+//
+// An alternate approach is to put the protocol wrapping/unwrapping in the main
+// fdevent loop, which has the advantage of being able to re-use the existing
+// select() code for handling data streams. However, implementation turned out
+// to be more complex due to partial reads and non-blocking I/O so this model
+// was chosen instead.
+
+#define TRACE_TAG SHELL
+
+#include "sysdeps.h"
+
+#include "shell_service.h"
+
+#include <errno.h>
+#include <paths.h>
+#include <pty.h>
+#include <pwd.h>
+#include <sys/select.h>
+#include <termios.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <private/android_logger.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "security_log_tags.h"
+
+namespace {
+
+// Reads from |fd| until close or failure.
+std::string ReadAll(int fd) {
+ char buffer[512];
+ std::string received;
+
+ while (1) {
+ int bytes = adb_read(fd, buffer, sizeof(buffer));
+ if (bytes <= 0) {
+ break;
+ }
+ received.append(buffer, bytes);
+ }
+
+ return received;
+}
+
+// Creates a socketpair and saves the endpoints to |fd1| and |fd2|.
+bool CreateSocketpair(unique_fd* fd1, unique_fd* fd2) {
+ int sockets[2];
+ if (adb_socketpair(sockets) < 0) {
+ PLOG(ERROR) << "cannot create socket pair";
+ return false;
+ }
+ fd1->reset(sockets[0]);
+ fd2->reset(sockets[1]);
+ return true;
+}
+
+class Subprocess {
+ public:
+ Subprocess(const std::string& command, const char* terminal_type,
+ SubprocessType type, SubprocessProtocol protocol);
+ ~Subprocess();
+
+ const std::string& command() const { return command_; }
+
+ int ReleaseLocalSocket() { return local_socket_sfd_.release(); }
+
+ pid_t pid() const { return pid_; }
+
+ // Sets up FDs, forks a subprocess, starts the subprocess manager thread,
+ // and exec's the child. Returns false and sets error on failure.
+ bool ForkAndExec(std::string* _Nonnull error);
+
+ // Start the subprocess manager thread. Consumes the subprocess, regardless of success.
+ // Returns false and sets error on failure.
+ static bool StartThread(std::unique_ptr<Subprocess> subprocess,
+ std::string* _Nonnull error);
+
+ private:
+ // Opens the file at |pts_name|.
+ int OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd);
+
+ static void ThreadHandler(void* userdata);
+ void PassDataStreams();
+ void WaitForExit();
+
+ unique_fd* SelectLoop(fd_set* master_read_set_ptr,
+ fd_set* master_write_set_ptr);
+
+ // Input/output stream handlers. Success returns nullptr, failure returns
+ // a pointer to the failed FD.
+ unique_fd* PassInput();
+ unique_fd* PassOutput(unique_fd* sfd, ShellProtocol::Id id);
+
+ const std::string command_;
+ const std::string terminal_type_;
+ bool make_pty_raw_ = false;
+ SubprocessType type_;
+ SubprocessProtocol protocol_;
+ pid_t pid_ = -1;
+ unique_fd local_socket_sfd_;
+
+ // Shell protocol variables.
+ unique_fd stdinout_sfd_, stderr_sfd_, protocol_sfd_;
+ std::unique_ptr<ShellProtocol> input_, output_;
+ size_t input_bytes_left_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(Subprocess);
+};
+
+Subprocess::Subprocess(const std::string& command, const char* terminal_type,
+ SubprocessType type, SubprocessProtocol protocol)
+ : command_(command),
+ terminal_type_(terminal_type ? terminal_type : ""),
+ type_(type),
+ protocol_(protocol) {
+ // If we aren't using the shell protocol we must allocate a PTY to properly close the
+ // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
+ // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
+ // e.g. screenrecord, will never notice the broken pipe and terminate.
+ // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
+ // with select() and will send SIGHUP manually to the child process.
+ if (protocol_ == SubprocessProtocol::kNone && type_ == SubprocessType::kRaw) {
+ // Disable PTY input/output processing since the client is expecting raw data.
+ D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
+ type_ = SubprocessType::kPty;
+ make_pty_raw_ = true;
+ }
+}
+
+Subprocess::~Subprocess() {
+ WaitForExit();
+}
+
+bool Subprocess::ForkAndExec(std::string* error) {
+ unique_fd child_stdinout_sfd, child_stderr_sfd;
+ unique_fd parent_error_sfd, child_error_sfd;
+ char pts_name[PATH_MAX];
+
+ if (command_.empty()) {
+ __android_log_security_bswrite(SEC_TAG_ADB_SHELL_INTERACTIVE, "");
+ } else {
+ __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
+ }
+
+ // Create a socketpair for the fork() child to report any errors back to the parent. Since we
+ // use threads, logging directly from the child might deadlock due to locks held in another
+ // thread during the fork.
+ if (!CreateSocketpair(&parent_error_sfd, &child_error_sfd)) {
+ *error = android::base::StringPrintf(
+ "failed to create pipe for subprocess error reporting: %s", strerror(errno));
+ return false;
+ }
+
+ // Construct the environment for the child before we fork.
+ passwd* pw = getpwuid(getuid());
+ std::unordered_map<std::string, std::string> env;
+ if (environ) {
+ char** current = environ;
+ while (char* env_cstr = *current++) {
+ std::string env_string = env_cstr;
+ char* delimiter = strchr(&env_string[0], '=');
+
+ // Drop any values that don't contain '='.
+ if (delimiter) {
+ *delimiter++ = '\0';
+ env[env_string.c_str()] = delimiter;
+ }
+ }
+ }
+
+ if (pw != nullptr) {
+ // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
+ env["HOME"] = pw->pw_dir;
+ env["LOGNAME"] = pw->pw_name;
+ env["USER"] = pw->pw_name;
+ env["SHELL"] = pw->pw_shell;
+ }
+
+ if (!terminal_type_.empty()) {
+ env["TERM"] = terminal_type_;
+ }
+
+ std::vector<std::string> joined_env;
+ for (auto it : env) {
+ const char* key = it.first.c_str();
+ const char* value = it.second.c_str();
+ joined_env.push_back(android::base::StringPrintf("%s=%s", key, value));
+ }
+
+ std::vector<const char*> cenv;
+ for (const std::string& str : joined_env) {
+ cenv.push_back(str.c_str());
+ }
+ cenv.push_back(nullptr);
+
+ if (type_ == SubprocessType::kPty) {
+ int fd;
+ pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
+ if (pid_ > 0) {
+ stdinout_sfd_.reset(fd);
+ }
+ } else {
+ if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
+ *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
+ strerror(errno));
+ return false;
+ }
+ // Raw subprocess + shell protocol allows for splitting stderr.
+ if (protocol_ == SubprocessProtocol::kShell &&
+ !CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
+ *error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
+ strerror(errno));
+ return false;
+ }
+ pid_ = fork();
+ }
+
+ if (pid_ == -1) {
+ *error = android::base::StringPrintf("fork failed: %s", strerror(errno));
+ return false;
+ }
+
+ if (pid_ == 0) {
+ // Subprocess child.
+ setsid();
+
+ if (type_ == SubprocessType::kPty) {
+ child_stdinout_sfd.reset(OpenPtyChildFd(pts_name, &child_error_sfd));
+ }
+
+ dup2(child_stdinout_sfd, STDIN_FILENO);
+ dup2(child_stdinout_sfd, STDOUT_FILENO);
+ dup2(child_stderr_sfd != -1 ? child_stderr_sfd : child_stdinout_sfd, STDERR_FILENO);
+
+ // exec doesn't trigger destructors, close the FDs manually.
+ stdinout_sfd_.reset(-1);
+ stderr_sfd_.reset(-1);
+ child_stdinout_sfd.reset(-1);
+ child_stderr_sfd.reset(-1);
+ parent_error_sfd.reset(-1);
+ close_on_exec(child_error_sfd);
+
+ if (command_.empty()) {
+ execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
+ } else {
+ execle(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr, cenv.data());
+ }
+ WriteFdExactly(child_error_sfd, "exec '" _PATH_BSHELL "' failed: ");
+ WriteFdExactly(child_error_sfd, strerror(errno));
+ child_error_sfd.reset(-1);
+ _Exit(1);
+ }
+
+ // Subprocess parent.
+ D("subprocess parent: stdin/stdout FD = %d, stderr FD = %d",
+ stdinout_sfd_.get(), stderr_sfd_.get());
+
+ // Wait to make sure the subprocess exec'd without error.
+ child_error_sfd.reset(-1);
+ std::string error_message = ReadAll(parent_error_sfd);
+ if (!error_message.empty()) {
+ *error = error_message;
+ return false;
+ }
+
+ D("subprocess parent: exec completed");
+ if (protocol_ == SubprocessProtocol::kNone) {
+ // No protocol: all streams pass through the stdinout FD and hook
+ // directly into the local socket for raw data transfer.
+ local_socket_sfd_.reset(stdinout_sfd_.release());
+ } else {
+ // Shell protocol: create another socketpair to intercept data.
+ if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
+ *error = android::base::StringPrintf(
+ "failed to create socketpair to intercept data: %s", strerror(errno));
+ kill(pid_, SIGKILL);
+ return false;
+ }
+ D("protocol FD = %d", protocol_sfd_.get());
+
+ input_.reset(new ShellProtocol(protocol_sfd_));
+ output_.reset(new ShellProtocol(protocol_sfd_));
+ if (!input_ || !output_) {
+ *error = "failed to allocate shell protocol objects";
+ kill(pid_, SIGKILL);
+ return false;
+ }
+
+ // Don't let reads/writes to the subprocess block our thread. This isn't
+ // likely but could happen under unusual circumstances, such as if we
+ // write a ton of data to stdin but the subprocess never reads it and
+ // the pipe fills up.
+ for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
+ if (fd >= 0) {
+ if (!set_file_block_mode(fd, false)) {
+ *error = android::base::StringPrintf(
+ "failed to set non-blocking mode for fd %d", fd);
+ kill(pid_, SIGKILL);
+ return false;
+ }
+ }
+ }
+ }
+
+ D("subprocess parent: completed");
+ return true;
+}
+
+bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
+ Subprocess* raw = subprocess.release();
+ if (!adb_thread_create(ThreadHandler, raw)) {
+ *error =
+ android::base::StringPrintf("failed to create subprocess thread: %s", strerror(errno));
+ kill(raw->pid_, SIGKILL);
+ return false;
+ }
+
+ return true;
+}
+
+int Subprocess::OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd) {
+ int child_fd = adb_open(pts_name, O_RDWR | O_CLOEXEC);
+ if (child_fd == -1) {
+ // Don't use WriteFdFmt; since we're in the fork() child we don't want
+ // to allocate any heap memory to avoid race conditions.
+ const char* messages[] = {"child failed to open pseudo-term slave ",
+ pts_name, ": ", strerror(errno)};
+ for (const char* message : messages) {
+ WriteFdExactly(*error_sfd, message);
+ }
+ abort();
+ }
+
+ if (make_pty_raw_) {
+ termios tattr;
+ if (tcgetattr(child_fd, &tattr) == -1) {
+ int saved_errno = errno;
+ WriteFdExactly(*error_sfd, "tcgetattr failed: ");
+ WriteFdExactly(*error_sfd, strerror(saved_errno));
+ abort();
+ }
+
+ cfmakeraw(&tattr);
+ if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) {
+ int saved_errno = errno;
+ WriteFdExactly(*error_sfd, "tcsetattr failed: ");
+ WriteFdExactly(*error_sfd, strerror(saved_errno));
+ abort();
+ }
+ }
+
+ return child_fd;
+}
+
+void Subprocess::ThreadHandler(void* userdata) {
+ Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
+
+ adb_thread_setname(android::base::StringPrintf(
+ "shell srvc %d", subprocess->pid()));
+
+ D("passing data streams for PID %d", subprocess->pid());
+ subprocess->PassDataStreams();
+
+ D("deleting Subprocess for PID %d", subprocess->pid());
+ delete subprocess;
+}
+
+void Subprocess::PassDataStreams() {
+ if (protocol_sfd_ == -1) {
+ return;
+ }
+
+ // Start by trying to read from the protocol FD, stdout, and stderr.
+ fd_set master_read_set, master_write_set;
+ FD_ZERO(&master_read_set);
+ FD_ZERO(&master_write_set);
+ for (unique_fd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
+ if (*sfd != -1) {
+ FD_SET(*sfd, &master_read_set);
+ }
+ }
+
+ // Pass data until the protocol FD or both the subprocess pipes die, at
+ // which point we can't pass any more data.
+ while (protocol_sfd_ != -1 && (stdinout_sfd_ != -1 || stderr_sfd_ != -1)) {
+ unique_fd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
+ if (dead_sfd) {
+ D("closing FD %d", dead_sfd->get());
+ FD_CLR(*dead_sfd, &master_read_set);
+ FD_CLR(*dead_sfd, &master_write_set);
+ if (dead_sfd == &protocol_sfd_) {
+ // Using SIGHUP is a decent general way to indicate that the
+ // controlling process is going away. If specific signals are
+ // needed (e.g. SIGINT), pass those through the shell protocol
+ // and only fall back on this for unexpected closures.
+ D("protocol FD died, sending SIGHUP to pid %d", pid_);
+ kill(pid_, SIGHUP);
+
+ // We also need to close the pipes connected to the child process
+ // so that if it ignores SIGHUP and continues to write data it
+ // won't fill up the pipe and block.
+ stdinout_sfd_.reset();
+ stderr_sfd_.reset();
+ }
+ dead_sfd->reset();
+ }
+ }
+}
+
+namespace {
+
+inline bool ValidAndInSet(const unique_fd& sfd, fd_set* set) {
+ return sfd != -1 && FD_ISSET(sfd, set);
+}
+
+} // namespace
+
+unique_fd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
+ fd_set* master_write_set_ptr) {
+ fd_set read_set, write_set;
+ int select_n = std::max(std::max(protocol_sfd_, stdinout_sfd_), stderr_sfd_) + 1;
+ unique_fd* dead_sfd = nullptr;
+
+ // Keep calling select() and passing data until an FD closes/errors.
+ while (!dead_sfd) {
+ memcpy(&read_set, master_read_set_ptr, sizeof(read_set));
+ memcpy(&write_set, master_write_set_ptr, sizeof(write_set));
+ if (select(select_n, &read_set, &write_set, nullptr, nullptr) < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ PLOG(ERROR) << "select failed, closing subprocess pipes";
+ stdinout_sfd_.reset(-1);
+ stderr_sfd_.reset(-1);
+ return nullptr;
+ }
+ }
+
+ // Read stdout, write to protocol FD.
+ if (ValidAndInSet(stdinout_sfd_, &read_set)) {
+ dead_sfd = PassOutput(&stdinout_sfd_, ShellProtocol::kIdStdout);
+ }
+
+ // Read stderr, write to protocol FD.
+ if (!dead_sfd && ValidAndInSet(stderr_sfd_, &read_set)) {
+ dead_sfd = PassOutput(&stderr_sfd_, ShellProtocol::kIdStderr);
+ }
+
+ // Read protocol FD, write to stdin.
+ if (!dead_sfd && ValidAndInSet(protocol_sfd_, &read_set)) {
+ dead_sfd = PassInput();
+ // If we didn't finish writing, block on stdin write.
+ if (input_bytes_left_) {
+ FD_CLR(protocol_sfd_, master_read_set_ptr);
+ FD_SET(stdinout_sfd_, master_write_set_ptr);
+ }
+ }
+
+ // Continue writing to stdin; only happens if a previous write blocked.
+ if (!dead_sfd && ValidAndInSet(stdinout_sfd_, &write_set)) {
+ dead_sfd = PassInput();
+ // If we finished writing, go back to blocking on protocol read.
+ if (!input_bytes_left_) {
+ FD_SET(protocol_sfd_, master_read_set_ptr);
+ FD_CLR(stdinout_sfd_, master_write_set_ptr);
+ }
+ }
+ } // while (!dead_sfd)
+
+ return dead_sfd;
+}
+
+unique_fd* Subprocess::PassInput() {
+ // Only read a new packet if we've finished writing the last one.
+ if (!input_bytes_left_) {
+ if (!input_->Read()) {
+ // Read() uses ReadFdExactly() which sets errno to 0 on EOF.
+ if (errno != 0) {
+ PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
+ }
+ return &protocol_sfd_;
+ }
+
+ if (stdinout_sfd_ != -1) {
+ switch (input_->id()) {
+ case ShellProtocol::kIdWindowSizeChange:
+ int rows, cols, x_pixels, y_pixels;
+ if (sscanf(input_->data(), "%dx%d,%dx%d",
+ &rows, &cols, &x_pixels, &y_pixels) == 4) {
+ winsize ws;
+ ws.ws_row = rows;
+ ws.ws_col = cols;
+ ws.ws_xpixel = x_pixels;
+ ws.ws_ypixel = y_pixels;
+ ioctl(stdinout_sfd_, TIOCSWINSZ, &ws);
+ }
+ break;
+ case ShellProtocol::kIdStdin:
+ input_bytes_left_ = input_->data_length();
+ break;
+ case ShellProtocol::kIdCloseStdin:
+ if (type_ == SubprocessType::kRaw) {
+ if (adb_shutdown(stdinout_sfd_, SHUT_WR) == 0) {
+ return nullptr;
+ }
+ PLOG(ERROR) << "failed to shutdown writes to FD "
+ << stdinout_sfd_;
+ return &stdinout_sfd_;
+ } else {
+ // PTYs can't close just input, so rather than close the
+ // FD and risk losing subprocess output, leave it open.
+ // This only happens if the client starts a PTY shell
+ // non-interactively which is rare and unsupported.
+ // If necessary, the client can manually close the shell
+ // with `exit` or by killing the adb client process.
+ D("can't close input for PTY FD %d", stdinout_sfd_.get());
+ }
+ break;
+ }
+ }
+ }
+
+ if (input_bytes_left_ > 0) {
+ int index = input_->data_length() - input_bytes_left_;
+ int bytes = adb_write(stdinout_sfd_, input_->data() + index, input_bytes_left_);
+ if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
+ if (bytes < 0) {
+ PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_;
+ }
+ // stdin is done, mark this packet as finished and we'll just start
+ // dumping any further data received from the protocol FD.
+ input_bytes_left_ = 0;
+ return &stdinout_sfd_;
+ } else if (bytes > 0) {
+ input_bytes_left_ -= bytes;
+ }
+ }
+
+ return nullptr;
+}
+
+unique_fd* Subprocess::PassOutput(unique_fd* sfd, ShellProtocol::Id id) {
+ int bytes = adb_read(*sfd, output_->data(), output_->data_capacity());
+ if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
+ // read() returns EIO if a PTY closes; don't report this as an error,
+ // it just means the subprocess completed.
+ if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
+ PLOG(ERROR) << "error reading output FD " << *sfd;
+ }
+ return sfd;
+ }
+
+ if (bytes > 0 && !output_->Write(id, bytes)) {
+ if (errno != 0) {
+ PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
+ }
+ return &protocol_sfd_;
+ }
+
+ return nullptr;
+}
+
+void Subprocess::WaitForExit() {
+ int exit_code = 1;
+
+ D("waiting for pid %d", pid_);
+ while (true) {
+ int status;
+ if (pid_ == waitpid(pid_, &status, 0)) {
+ D("post waitpid (pid=%d) status=%04x", pid_, status);
+ if (WIFSIGNALED(status)) {
+ exit_code = 0x80 | WTERMSIG(status);
+ D("subprocess killed by signal %d", WTERMSIG(status));
+ break;
+ } else if (!WIFEXITED(status)) {
+ D("subprocess didn't exit");
+ break;
+ } else if (WEXITSTATUS(status) >= 0) {
+ exit_code = WEXITSTATUS(status);
+ D("subprocess exit code = %d", WEXITSTATUS(status));
+ break;
+ }
+ }
+ }
+
+ // If we have an open protocol FD send an exit packet.
+ if (protocol_sfd_ != -1) {
+ output_->data()[0] = exit_code;
+ if (output_->Write(ShellProtocol::kIdExit, 1)) {
+ D("wrote the exit code packet: %d", exit_code);
+ } else {
+ PLOG(ERROR) << "failed to write the exit code packet";
+ }
+ protocol_sfd_.reset(-1);
+ }
+
+ // Pass the local socket FD to the shell cleanup fdevent.
+ if (SHELL_EXIT_NOTIFY_FD >= 0) {
+ int fd = local_socket_sfd_;
+ if (WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd))) {
+ D("passed fd %d to SHELL_EXIT_NOTIFY_FD (%d) for pid %d",
+ fd, SHELL_EXIT_NOTIFY_FD, pid_);
+ // The shell exit fdevent now owns the FD and will close it once
+ // the last bit of data flushes through.
+ static_cast<void>(local_socket_sfd_.release());
+ } else {
+ PLOG(ERROR) << "failed to write fd " << fd
+ << " to SHELL_EXIT_NOTIFY_FD (" << SHELL_EXIT_NOTIFY_FD
+ << ") for pid " << pid_;
+ }
+ }
+}
+
+} // namespace
+
+// Create a pipe containing the error.
+static int ReportError(SubprocessProtocol protocol, const std::string& message) {
+ int pipefd[2];
+ if (pipe(pipefd) != 0) {
+ LOG(ERROR) << "failed to create pipe to report error";
+ return -1;
+ }
+
+ std::string buf = android::base::StringPrintf("error: %s\n", message.c_str());
+ if (protocol == SubprocessProtocol::kShell) {
+ ShellProtocol::Id id = ShellProtocol::kIdStderr;
+ uint32_t length = buf.length();
+ WriteFdExactly(pipefd[1], &id, sizeof(id));
+ WriteFdExactly(pipefd[1], &length, sizeof(length));
+ }
+
+ WriteFdExactly(pipefd[1], buf.data(), buf.length());
+
+ if (protocol == SubprocessProtocol::kShell) {
+ ShellProtocol::Id id = ShellProtocol::kIdExit;
+ uint32_t length = 1;
+ char exit_code = 126;
+ WriteFdExactly(pipefd[1], &id, sizeof(id));
+ WriteFdExactly(pipefd[1], &length, sizeof(length));
+ WriteFdExactly(pipefd[1], &exit_code, sizeof(exit_code));
+ }
+
+ adb_close(pipefd[1]);
+ return pipefd[0];
+}
+
+int StartSubprocess(const char* name, const char* terminal_type,
+ SubprocessType type, SubprocessProtocol protocol) {
+ D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
+ type == SubprocessType::kRaw ? "raw" : "PTY",
+ protocol == SubprocessProtocol::kNone ? "none" : "shell",
+ terminal_type, name);
+
+ auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
+ if (!subprocess) {
+ LOG(ERROR) << "failed to allocate new subprocess";
+ return ReportError(protocol, "failed to allocate new subprocess");
+ }
+
+ std::string error;
+ if (!subprocess->ForkAndExec(&error)) {
+ LOG(ERROR) << "failed to start subprocess: " << error;
+ return ReportError(protocol, error);
+ }
+
+ unique_fd local_socket(subprocess->ReleaseLocalSocket());
+ D("subprocess creation successful: local_socket_fd=%d, pid=%d", local_socket.get(),
+ subprocess->pid());
+
+ if (!Subprocess::StartThread(std::move(subprocess), &error)) {
+ LOG(ERROR) << "failed to start subprocess management thread: " << error;
+ return ReportError(protocol, error);
+ }
+
+ return local_socket.release();
+}
diff --git a/adb/shell_service.h b/adb/shell_service.h
new file mode 100644
index 0000000..e3d676a
--- /dev/null
+++ b/adb/shell_service.h
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+// This file contains classes and functionality to launch shell subprocesses
+// in adbd and communicate between those subprocesses and the adb client.
+//
+// The main features exposed here are:
+// 1. A ShellPacket class to wrap data in a simple protocol. Both adbd and
+// the adb client use this class to transmit data between them.
+// 2. Functions to launch a subprocess on the adbd side.
+
+#ifndef SHELL_SERVICE_H_
+#define SHELL_SERVICE_H_
+
+#include <stdint.h>
+
+#include <android-base/macros.h>
+
+#include "adb.h"
+
+// Class to send and receive shell protocol packets.
+//
+// To keep things simple and predictable, reads and writes block until an entire
+// packet is complete.
+//
+// Example: read raw data from |fd| and send it in a packet.
+// ShellProtocol* p = new ShellProtocol(protocol_fd);
+// int len = adb_read(stdout_fd, p->data(), p->data_capacity());
+// packet->WritePacket(ShellProtocol::kIdStdout, len);
+//
+// Example: read a packet and print it to |stdout|.
+// ShellProtocol* p = new ShellProtocol(protocol_fd);
+// if (p->ReadPacket() && p->id() == kIdStdout) {
+// fwrite(p->data(), 1, p->data_length(), stdout);
+// }
+class ShellProtocol {
+ public:
+ // This is an unscoped enum to make it easier to compare against raw bytes.
+ enum Id : uint8_t {
+ kIdStdin = 0,
+ kIdStdout = 1,
+ kIdStderr = 2,
+ kIdExit = 3,
+
+ // Close subprocess stdin if possible.
+ kIdCloseStdin = 4,
+
+ // Window size change (an ASCII version of struct winsize).
+ kIdWindowSizeChange = 5,
+
+ // Indicates an invalid or unknown packet.
+ kIdInvalid = 255,
+ };
+
+ // ShellPackets will probably be too large to allocate on the stack so they
+ // should be dynamically allocated on the heap instead.
+ //
+ // |fd| is an open file descriptor to be used to send or receive packets.
+ explicit ShellProtocol(int fd);
+ virtual ~ShellProtocol();
+
+ // Returns a pointer to the data buffer.
+ const char* data() const { return buffer_ + kHeaderSize; }
+ char* data() { return buffer_ + kHeaderSize; }
+
+ // Returns the total capacity of the data buffer.
+ size_t data_capacity() const { return buffer_end_ - data(); }
+
+ // Reads a packet from the FD.
+ //
+ // If a packet is too big to fit in the buffer then Read() will split the
+ // packet across multiple calls. For example, reading a 50-byte packet into
+ // a 20-byte buffer would read 20 bytes, 20 bytes, then 10 bytes.
+ //
+ // Returns false if the FD closed or errored.
+ bool Read();
+
+ // Returns the ID of the packet in the buffer.
+ int id() const { return buffer_[0]; }
+
+ // Returns the number of bytes that have been read into the data buffer.
+ size_t data_length() const { return data_length_; }
+
+ // Writes the packet currently in the buffer to the FD.
+ //
+ // Returns false if the FD closed or errored.
+ bool Write(Id id, size_t length);
+
+ private:
+ // Packets support 4-byte lengths.
+ typedef uint32_t length_t;
+
+ enum {
+ // It's OK if MAX_PAYLOAD doesn't match on the sending and receiving
+ // end, reading will split larger packets into multiple smaller ones.
+ kBufferSize = MAX_PAYLOAD,
+
+ // Header is 1 byte ID + 4 bytes length.
+ kHeaderSize = sizeof(Id) + sizeof(length_t)
+ };
+
+ int fd_;
+ char buffer_[kBufferSize];
+ size_t data_length_ = 0, bytes_left_ = 0;
+
+ // We need to be able to modify this value for testing purposes, but it
+ // will stay constant during actual program use.
+ char* buffer_end_ = buffer_ + sizeof(buffer_);
+
+ friend class ShellProtocolTest;
+
+ DISALLOW_COPY_AND_ASSIGN(ShellProtocol);
+};
+
+#if !ADB_HOST
+
+enum class SubprocessType {
+ kPty,
+ kRaw,
+};
+
+enum class SubprocessProtocol {
+ kNone,
+ kShell,
+};
+
+// Forks and starts a new shell subprocess. If |name| is empty an interactive
+// shell is started, otherwise |name| is executed non-interactively.
+//
+// Returns an open FD connected to the subprocess or -1 on failure.
+int StartSubprocess(const char* name, const char* terminal_type,
+ SubprocessType type, SubprocessProtocol protocol);
+
+#endif // !ADB_HOST
+
+#endif // SHELL_SERVICE_H_
diff --git a/adb/shell_service_protocol.cpp b/adb/shell_service_protocol.cpp
new file mode 100644
index 0000000..623629c
--- /dev/null
+++ b/adb/shell_service_protocol.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shell_service.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "adb_io.h"
+
+ShellProtocol::ShellProtocol(int fd) : fd_(fd) {
+ buffer_[0] = kIdInvalid;
+}
+
+ShellProtocol::~ShellProtocol() {
+}
+
+bool ShellProtocol::Read() {
+ // Only read a new header if we've finished the last packet.
+ if (!bytes_left_) {
+ if (!ReadFdExactly(fd_, buffer_, kHeaderSize)) {
+ return false;
+ }
+
+ length_t packet_length;
+ memcpy(&packet_length, &buffer_[1], sizeof(packet_length));
+ bytes_left_ = packet_length;
+ data_length_ = 0;
+ }
+
+ size_t read_length = std::min(bytes_left_, data_capacity());
+ if (read_length && !ReadFdExactly(fd_, data(), read_length)) {
+ return false;
+ }
+
+ bytes_left_ -= read_length;
+ data_length_ = read_length;
+
+ return true;
+}
+
+bool ShellProtocol::Write(Id id, size_t length) {
+ buffer_[0] = id;
+ length_t typed_length = length;
+ memcpy(&buffer_[1], &typed_length, sizeof(typed_length));
+
+ return WriteFdExactly(fd_, buffer_, kHeaderSize + length);
+}
diff --git a/adb/shell_service_protocol_test.cpp b/adb/shell_service_protocol_test.cpp
new file mode 100644
index 0000000..b0fa3ed
--- /dev/null
+++ b/adb/shell_service_protocol_test.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shell_service.h"
+
+#include <gtest/gtest.h>
+
+#include <signal.h>
+#include <string.h>
+
+#include "sysdeps.h"
+
+class ShellProtocolTest : public ::testing::Test {
+ public:
+ static void SetUpTestCase() {
+#if !defined(_WIN32)
+ // This is normally done in main.cpp.
+ saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
+#endif
+ }
+
+ static void TearDownTestCase() {
+#if !defined(_WIN32)
+ signal(SIGPIPE, saved_sigpipe_handler_);
+#endif
+ }
+
+ // Initializes the socketpair and ShellProtocols needed for testing.
+ void SetUp() {
+ int fds[2];
+ ASSERT_EQ(0, adb_socketpair(fds));
+ read_fd_ = fds[0];
+ write_fd_ = fds[1];
+
+ write_protocol_ = new ShellProtocol(write_fd_);
+ ASSERT_TRUE(write_protocol_ != nullptr);
+
+ read_protocol_ = new ShellProtocol(read_fd_);
+ ASSERT_TRUE(read_protocol_ != nullptr);
+ }
+
+ // Cleans up FDs and ShellProtocols. If an FD is closed manually during a
+ // test, set it to -1 to prevent TearDown() trying to close it again.
+ void TearDown() {
+ for (int fd : {read_fd_, write_fd_}) {
+ if (fd >= 0) {
+ adb_close(fd);
+ }
+ }
+ for (ShellProtocol* protocol : {read_protocol_, write_protocol_}) {
+ if (protocol) {
+ delete protocol;
+ }
+ }
+ }
+
+ // Fakes the buffer size so we can test filling buffers.
+ void SetReadDataCapacity(size_t size) {
+ read_protocol_->buffer_end_ = read_protocol_->data() + size;
+ }
+
+#if !defined(_WIN32)
+ static sig_t saved_sigpipe_handler_;
+#endif
+
+ int read_fd_ = -1, write_fd_ = -1;
+ ShellProtocol *read_protocol_ = nullptr, *write_protocol_ = nullptr;
+};
+
+#if !defined(_WIN32)
+sig_t ShellProtocolTest::saved_sigpipe_handler_ = nullptr;
+#endif
+
+namespace {
+
+// Returns true if the packet contains the given values. `data` can't be null.
+bool PacketEquals(const ShellProtocol* protocol, ShellProtocol::Id id,
+ const void* data, size_t data_length) {
+ // Note that passing memcmp null is bad, even if data_length is 0.
+ return (protocol->id() == id &&
+ protocol->data_length() == data_length &&
+ !memcmp(data, protocol->data(), data_length));
+}
+
+} // namespace
+
+// Tests data that can fit in a single packet.
+TEST_F(ShellProtocolTest, FullPacket) {
+ ShellProtocol::Id id = ShellProtocol::kIdStdout;
+ char data[] = "abc 123 \0\r\n";
+
+ memcpy(write_protocol_->data(), data, sizeof(data));
+ ASSERT_TRUE(write_protocol_->Write(id, sizeof(data)));
+
+ ASSERT_TRUE(read_protocol_->Read());
+ ASSERT_TRUE(PacketEquals(read_protocol_, id, data, sizeof(data)));
+}
+
+// Tests data that has to be read multiple times due to smaller read buffer.
+TEST_F(ShellProtocolTest, ReadBufferOverflow) {
+ ShellProtocol::Id id = ShellProtocol::kIdStdin;
+
+ memcpy(write_protocol_->data(), "1234567890", 10);
+ ASSERT_TRUE(write_protocol_->Write(id, 10));
+
+ SetReadDataCapacity(4);
+ ASSERT_TRUE(read_protocol_->Read());
+ ASSERT_TRUE(PacketEquals(read_protocol_, id, "1234", 4));
+ ASSERT_TRUE(read_protocol_->Read());
+ ASSERT_TRUE(PacketEquals(read_protocol_, id, "5678", 4));
+ ASSERT_TRUE(read_protocol_->Read());
+ ASSERT_TRUE(PacketEquals(read_protocol_, id, "90", 2));
+}
+
+// Tests a zero length packet.
+TEST_F(ShellProtocolTest, ZeroLengthPacket) {
+ ShellProtocol::Id id = ShellProtocol::kIdStderr;
+
+ ASSERT_TRUE(write_protocol_->Write(id, 0));
+ ASSERT_TRUE(read_protocol_->Read());
+ char buf[1];
+ ASSERT_TRUE(PacketEquals(read_protocol_, id, buf, 0));
+}
+
+// Tests exit code packets.
+TEST_F(ShellProtocolTest, ExitCodePacket) {
+ write_protocol_->data()[0] = 20;
+ ASSERT_TRUE(write_protocol_->Write(ShellProtocol::kIdExit, 1));
+
+ ASSERT_TRUE(read_protocol_->Read());
+ ASSERT_EQ(ShellProtocol::kIdExit, read_protocol_->id());
+ ASSERT_EQ(20, read_protocol_->data()[0]);
+}
+
+// Tests writing to a closed pipe.
+TEST_F(ShellProtocolTest, WriteToClosedPipeFail) {
+ adb_close(read_fd_);
+ read_fd_ = -1;
+
+ ASSERT_FALSE(write_protocol_->Write(ShellProtocol::kIdStdout, 0));
+}
+
+// Tests writing to a closed FD.
+TEST_F(ShellProtocolTest, WriteToClosedFdFail) {
+ adb_close(write_fd_);
+ write_fd_ = -1;
+
+ ASSERT_FALSE(write_protocol_->Write(ShellProtocol::kIdStdout, 0));
+}
+
+// Tests reading from a closed pipe.
+TEST_F(ShellProtocolTest, ReadFromClosedPipeFail) {
+ adb_close(write_fd_);
+ write_fd_ = -1;
+
+ ASSERT_FALSE(read_protocol_->Read());
+}
+
+// Tests reading from a closed FD.
+TEST_F(ShellProtocolTest, ReadFromClosedFdFail) {
+ adb_close(read_fd_);
+ read_fd_ = -1;
+
+ ASSERT_FALSE(read_protocol_->Read());
+}
+
+// Tests reading from a closed pipe that has a packet waiting. This checks that
+// even if the pipe closes before we can fully read its contents we will still
+// be able to access the last packets.
+TEST_F(ShellProtocolTest, ReadPacketFromClosedPipe) {
+ ShellProtocol::Id id = ShellProtocol::kIdStdout;
+ char data[] = "foo bar";
+
+ memcpy(write_protocol_->data(), data, sizeof(data));
+ ASSERT_TRUE(write_protocol_->Write(id, sizeof(data)));
+ adb_close(write_fd_);
+ write_fd_ = -1;
+
+ // First read should grab the packet.
+ ASSERT_TRUE(read_protocol_->Read());
+ ASSERT_TRUE(PacketEquals(read_protocol_, id, data, sizeof(data)));
+
+ // Second read should fail.
+ ASSERT_FALSE(read_protocol_->Read());
+}
diff --git a/adb/shell_service_test.cpp b/adb/shell_service_test.cpp
new file mode 100644
index 0000000..839284e
--- /dev/null
+++ b/adb/shell_service_test.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shell_service.h"
+
+#include <gtest/gtest.h>
+
+#include <signal.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "sysdeps.h"
+
+class ShellServiceTest : public ::testing::Test {
+ public:
+ static void SetUpTestCase() {
+ // This is normally done in main.cpp.
+ saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
+
+ }
+
+ static void TearDownTestCase() {
+ signal(SIGPIPE, saved_sigpipe_handler_);
+ }
+
+ // Helpers to start and cleanup a subprocess. Cleanup normally does not
+ // need to be called manually unless multiple subprocesses are run from
+ // a single test.
+ void StartTestSubprocess(const char* command, SubprocessType type,
+ SubprocessProtocol protocol);
+ void CleanupTestSubprocess();
+
+ virtual void TearDown() override {
+ void CleanupTestSubprocess();
+ }
+
+ static sighandler_t saved_sigpipe_handler_;
+
+ int subprocess_fd_ = -1;
+ int shell_exit_receiver_fd_ = -1, saved_shell_exit_fd_;
+};
+
+sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
+
+void ShellServiceTest::StartTestSubprocess(
+ const char* command, SubprocessType type, SubprocessProtocol protocol) {
+ // We want to intercept the shell exit message to make sure it's sent.
+ saved_shell_exit_fd_ = SHELL_EXIT_NOTIFY_FD;
+ int fd[2];
+ ASSERT_TRUE(adb_socketpair(fd) >= 0);
+ SHELL_EXIT_NOTIFY_FD = fd[0];
+ shell_exit_receiver_fd_ = fd[1];
+
+ subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
+ ASSERT_TRUE(subprocess_fd_ >= 0);
+}
+
+void ShellServiceTest::CleanupTestSubprocess() {
+ if (subprocess_fd_ >= 0) {
+ // Subprocess should send its FD to SHELL_EXIT_NOTIFY_FD for cleanup.
+ int notified_fd = -1;
+ ASSERT_TRUE(ReadFdExactly(shell_exit_receiver_fd_, ¬ified_fd,
+ sizeof(notified_fd)));
+ ASSERT_EQ(notified_fd, subprocess_fd_);
+
+ adb_close(subprocess_fd_);
+ subprocess_fd_ = -1;
+
+ // Restore SHELL_EXIT_NOTIFY_FD.
+ adb_close(SHELL_EXIT_NOTIFY_FD);
+ adb_close(shell_exit_receiver_fd_);
+ shell_exit_receiver_fd_ = -1;
+ SHELL_EXIT_NOTIFY_FD = saved_shell_exit_fd_;
+ }
+}
+
+namespace {
+
+// Reads raw data from |fd| until it closes or errors.
+std::string ReadRaw(int fd) {
+ char buffer[1024];
+ char *cur_ptr = buffer, *end_ptr = buffer + sizeof(buffer);
+
+ while (1) {
+ int bytes = adb_read(fd, cur_ptr, end_ptr - cur_ptr);
+ if (bytes <= 0) {
+ return std::string(buffer, cur_ptr);
+ }
+ cur_ptr += bytes;
+ }
+}
+
+// Reads shell protocol data from |fd| until it closes or errors. Fills
+// |stdout| and |stderr| with their respective data, and returns the exit code
+// read from the protocol or -1 if an exit code packet was not received.
+int ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) {
+ int exit_code = -1;
+ stdout->clear();
+ stderr->clear();
+
+ ShellProtocol* protocol = new ShellProtocol(fd);
+ while (protocol->Read()) {
+ switch (protocol->id()) {
+ case ShellProtocol::kIdStdout:
+ stdout->append(protocol->data(), protocol->data_length());
+ break;
+ case ShellProtocol::kIdStderr:
+ stderr->append(protocol->data(), protocol->data_length());
+ break;
+ case ShellProtocol::kIdExit:
+ EXPECT_EQ(-1, exit_code) << "Multiple exit packets received";
+ EXPECT_EQ(1u, protocol->data_length());
+ exit_code = protocol->data()[0];
+ break;
+ default:
+ ADD_FAILURE() << "Unidentified packet ID: " << protocol->id();
+ }
+ }
+ delete protocol;
+
+ return exit_code;
+}
+
+// Checks if each line in |lines| exists in the same order in |output|. Blank
+// lines in |output| are ignored for simplicity.
+bool ExpectLinesEqual(const std::string& output,
+ const std::vector<std::string>& lines) {
+ auto output_lines = android::base::Split(output, "\r\n");
+ size_t i = 0;
+
+ for (const std::string& line : lines) {
+ // Skip empty lines in output.
+ while (i < output_lines.size() && output_lines[i].empty()) {
+ ++i;
+ }
+ if (i >= output_lines.size()) {
+ ADD_FAILURE() << "Ran out of output lines";
+ return false;
+ }
+ EXPECT_EQ(line, output_lines[i]);
+ ++i;
+ }
+
+ while (i < output_lines.size() && output_lines[i].empty()) {
+ ++i;
+ }
+ EXPECT_EQ(i, output_lines.size()) << "Found unmatched output lines";
+ return true;
+}
+
+} // namespace
+
+// Tests a raw subprocess with no protocol.
+TEST_F(ShellServiceTest, RawNoProtocolSubprocess) {
+ // [ -t 0 ] checks if stdin is connected to a terminal.
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
+ SubprocessType::kRaw, SubprocessProtocol::kNone));
+
+ // [ -t 0 ] == 0 means we have a terminal (PTY). Even when requesting a raw subprocess, without
+ // the shell protocol we should always force a PTY to ensure proper cleanup.
+ ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
+}
+
+// Tests a PTY subprocess with no protocol.
+TEST_F(ShellServiceTest, PtyNoProtocolSubprocess) {
+ // [ -t 0 ] checks if stdin is connected to a terminal.
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
+ SubprocessType::kPty, SubprocessProtocol::kNone));
+
+ // [ -t 0 ] == 0 means we have a terminal (PTY).
+ ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
+}
+
+// Tests a raw subprocess with the shell protocol.
+TEST_F(ShellServiceTest, RawShellProtocolSubprocess) {
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "echo foo; echo bar >&2; echo baz; exit 24",
+ SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+ std::string stdout, stderr;
+ EXPECT_EQ(24, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ ExpectLinesEqual(stdout, {"foo", "baz"});
+ ExpectLinesEqual(stderr, {"bar"});
+}
+
+// Tests a PTY subprocess with the shell protocol.
+TEST_F(ShellServiceTest, PtyShellProtocolSubprocess) {
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "echo foo; echo bar >&2; echo baz; exit 50",
+ SubprocessType::kPty, SubprocessProtocol::kShell));
+
+ // PTY always combines stdout and stderr but the shell protocol should
+ // still give us an exit code.
+ std::string stdout, stderr;
+ EXPECT_EQ(50, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ ExpectLinesEqual(stdout, {"foo", "bar", "baz"});
+ ExpectLinesEqual(stderr, {});
+}
+
+// Tests an interactive PTY session.
+TEST_F(ShellServiceTest, InteractivePtySubprocess) {
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "", SubprocessType::kPty, SubprocessProtocol::kShell));
+
+ // Use variable substitution so echoed input is different from output.
+ const char* commands[] = {"TEST_STR=abc123",
+ "echo --${TEST_STR}--",
+ "exit"};
+
+ ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+ for (std::string command : commands) {
+ // Interactive shell requires a newline to complete each command.
+ command.push_back('\n');
+ memcpy(protocol->data(), command.data(), command.length());
+ ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, command.length()));
+ }
+ delete protocol;
+
+ std::string stdout, stderr;
+ EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ // An unpredictable command prompt makes parsing exact output difficult but
+ // it should at least contain echoed input and the expected output.
+ for (const char* command : commands) {
+ EXPECT_FALSE(stdout.find(command) == std::string::npos);
+ }
+ EXPECT_FALSE(stdout.find("--abc123--") == std::string::npos);
+}
+
+// Tests closing raw subprocess stdin.
+TEST_F(ShellServiceTest, CloseClientStdin) {
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "cat; echo TEST_DONE",
+ SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+ std::string input = "foo\nbar";
+ ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+ memcpy(protocol->data(), input.data(), input.length());
+ ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
+ ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
+ delete protocol;
+
+ std::string stdout, stderr;
+ EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
+ ExpectLinesEqual(stderr, {});
+}
+
+// Tests that nothing breaks when the stdin/stdout pipe closes.
+TEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) {
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "exec 0<&-; exec 1>&-; echo bar >&2",
+ SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+ std::string stdout, stderr;
+ EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ ExpectLinesEqual(stdout, {});
+ ExpectLinesEqual(stderr, {"bar"});
+}
+
+// Tests that nothing breaks when the stderr pipe closes.
+TEST_F(ShellServiceTest, CloseStderrSubprocess) {
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "exec 2>&-; echo foo",
+ SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+ std::string stdout, stderr;
+ EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ ExpectLinesEqual(stdout, {"foo"});
+ ExpectLinesEqual(stderr, {});
+}
diff --git a/adb/socket.h b/adb/socket.h
new file mode 100644
index 0000000..4acdf4a
--- /dev/null
+++ b/adb/socket.h
@@ -0,0 +1,126 @@
+/*
+ * 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 __ADB_SOCKET_H
+#define __ADB_SOCKET_H
+
+#include <stddef.h>
+
+#include "fdevent.h"
+
+struct apacket;
+class atransport;
+
+/* An asocket represents one half of a connection between a local and
+** remote entity. A local asocket is bound to a file descriptor. A
+** remote asocket is bound to the protocol engine.
+*/
+struct asocket {
+ /* chain pointers for the local/remote list of
+ ** asockets that this asocket lives in
+ */
+ asocket *next;
+ asocket *prev;
+
+ /* the unique identifier for this asocket
+ */
+ unsigned id;
+
+ /* flag: set when the socket's peer has closed
+ ** but packets are still queued for delivery
+ */
+ int closing;
+
+ // flag: set when the socket failed to write, so the socket will not wait to
+ // write packets and close directly.
+ bool has_write_error;
+
+ /* flag: quit adbd when both ends close the
+ ** local service socket
+ */
+ int exit_on_close;
+
+ /* the asocket we are connected to
+ */
+
+ asocket *peer;
+
+ /* For local asockets, the fde is used to bind
+ ** us to our fd event system. For remote asockets
+ ** these fields are not used.
+ */
+ fdevent fde;
+ int fd;
+
+ /* queue of apackets waiting to be written
+ */
+ apacket *pkt_first;
+ apacket *pkt_last;
+
+ /* enqueue is called by our peer when it has data
+ ** for us. It should return 0 if we can accept more
+ ** data or 1 if not. If we return 1, we must call
+ ** peer->ready() when we once again are ready to
+ ** receive data.
+ */
+ int (*enqueue)(asocket *s, apacket *pkt);
+
+ /* ready is called by the peer when it is ready for
+ ** us to send data via enqueue again
+ */
+ void (*ready)(asocket *s);
+
+ /* shutdown is called by the peer before it goes away.
+ ** the socket should not do any further calls on its peer.
+ ** Always followed by a call to close. Optional, i.e. can be NULL.
+ */
+ void (*shutdown)(asocket *s);
+
+ /* close is called by the peer when it has gone away.
+ ** we are not allowed to make any further calls on the
+ ** peer once our close method is called.
+ */
+ void (*close)(asocket *s);
+
+ /* A socket is bound to atransport */
+ atransport *transport;
+
+ size_t get_max_payload() const;
+};
+
+asocket *find_local_socket(unsigned local_id, unsigned remote_id);
+void install_local_socket(asocket *s);
+void remove_socket(asocket *s);
+void close_all_sockets(atransport *t);
+
+asocket *create_local_socket(int fd);
+asocket *create_local_service_socket(const char* destination,
+ const atransport* transport);
+
+asocket *create_remote_socket(unsigned id, atransport *t);
+void connect_to_remote(asocket *s, const char *destination);
+void connect_to_smartsocket(asocket *s);
+
+// Internal functions that are only made available here for testing purposes.
+namespace internal {
+
+#if ADB_HOST
+char* skip_host_serial(char* service);
+#endif
+
+} // namespace internal
+
+#endif // __ADB_SOCKET_H
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
new file mode 100644
index 0000000..14eb16b
--- /dev/null
+++ b/adb/socket_spec.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "socket_spec.h"
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/parseint.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/sockets.h>
+
+#include "adb.h"
+#include "sysdeps.h"
+
+using android::base::StartsWith;
+using android::base::StringPrintf;
+
+#if defined(__linux__)
+#define ADB_LINUX 1
+#else
+#define ADB_LINUX 0
+#endif
+
+#if defined(_WIN32)
+#define ADB_WINDOWS 1
+#else
+#define ADB_WINDOWS 0
+#endif
+
+// Not static because it is used in commandline.c.
+int gListenAll = 0;
+
+struct LocalSocketType {
+ int socket_namespace;
+ bool available;
+};
+
+static auto& kLocalSocketTypes = *new std::unordered_map<std::string, LocalSocketType>({
+#if ADB_HOST
+ { "local", { ANDROID_SOCKET_NAMESPACE_FILESYSTEM, !ADB_WINDOWS } },
+#else
+ { "local", { ANDROID_SOCKET_NAMESPACE_RESERVED, !ADB_WINDOWS } },
+#endif
+
+ { "localreserved", { ANDROID_SOCKET_NAMESPACE_RESERVED, !ADB_HOST } },
+ { "localabstract", { ANDROID_SOCKET_NAMESPACE_ABSTRACT, ADB_LINUX } },
+ { "localfilesystem", { ANDROID_SOCKET_NAMESPACE_FILESYSTEM, !ADB_WINDOWS } },
+});
+
+bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
+ std::string* error) {
+ if (!StartsWith(spec, "tcp:")) {
+ *error = StringPrintf("specification is not tcp: '%s'", spec.c_str());
+ return false;
+ }
+
+ std::string hostname_value;
+ int port_value;
+
+ // If the spec is tcp:<port>, parse it ourselves.
+ // Otherwise, delegate to android::base::ParseNetAddress.
+ if (android::base::ParseInt(&spec[4], &port_value)) {
+ // Do the range checking ourselves, because ParseInt rejects 'tcp:65536' and 'tcp:foo:1234'
+ // identically.
+ if (port_value < 0 || port_value > 65535) {
+ *error = StringPrintf("bad port number '%d'", port_value);
+ return false;
+ }
+ } else {
+ std::string addr = spec.substr(4);
+ port_value = -1;
+
+ // FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
+ // on an address that isn't 'localhost' is unsupported.
+ if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, nullptr, error)) {
+ return false;
+ }
+
+ if (port_value == -1) {
+ *error = StringPrintf("missing port in specification: '%s'", spec.c_str());
+ return false;
+ }
+ }
+
+ if (hostname) {
+ *hostname = std::move(hostname_value);
+ }
+
+ if (port) {
+ *port = port_value;
+ }
+
+ return true;
+}
+
+static bool tcp_host_is_local(const std::string& hostname) {
+ // FIXME
+ return hostname.empty() || hostname == "localhost";
+}
+
+bool is_socket_spec(const std::string& spec) {
+ for (const auto& it : kLocalSocketTypes) {
+ std::string prefix = it.first + ":";
+ if (StartsWith(spec, prefix.c_str())) {
+ return true;
+ }
+ }
+ return StartsWith(spec, "tcp:");
+}
+
+bool is_local_socket_spec(const std::string& spec) {
+ for (const auto& it : kLocalSocketTypes) {
+ std::string prefix = it.first + ":";
+ if (StartsWith(spec, prefix.c_str())) {
+ return true;
+ }
+ }
+
+ std::string error;
+ std::string hostname;
+ if (!parse_tcp_socket_spec(spec, &hostname, nullptr, &error)) {
+ return false;
+ }
+ return tcp_host_is_local(hostname);
+}
+
+int socket_spec_connect(const std::string& spec, std::string* error) {
+ if (StartsWith(spec, "tcp:")) {
+ std::string hostname;
+ int port;
+ if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
+ return -1;
+ }
+
+ int result;
+ if (tcp_host_is_local(hostname)) {
+ result = network_loopback_client(port, SOCK_STREAM, error);
+ } else {
+#if ADB_HOST
+ result = network_connect(hostname, port, SOCK_STREAM, 0, error);
+#else
+ // Disallow arbitrary connections in adbd.
+ *error = "adbd does not support arbitrary tcp connections";
+ return -1;
+#endif
+ }
+
+ if (result >= 0) {
+ disable_tcp_nagle(result);
+ }
+ return result;
+ }
+
+ for (const auto& it : kLocalSocketTypes) {
+ std::string prefix = it.first + ":";
+ if (StartsWith(spec, prefix.c_str())) {
+ if (!it.second.available) {
+ *error = StringPrintf("socket type %s is unavailable on this platform",
+ it.first.c_str());
+ return -1;
+ }
+
+ return network_local_client(&spec[prefix.length()], it.second.socket_namespace,
+ SOCK_STREAM, error);
+ }
+ }
+
+ *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+ return -1;
+}
+
+int socket_spec_listen(const std::string& spec, std::string* error, int* resolved_tcp_port) {
+ if (StartsWith(spec, "tcp:")) {
+ std::string hostname;
+ int port;
+ if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
+ return -1;
+ }
+
+ int result;
+ if (hostname.empty() && gListenAll) {
+ result = network_inaddr_any_server(port, SOCK_STREAM, error);
+ } else if (tcp_host_is_local(hostname)) {
+ result = network_loopback_server(port, SOCK_STREAM, error);
+ } else {
+ // TODO: Implement me.
+ *error = "listening on specified hostname currently unsupported";
+ return -1;
+ }
+
+ if (result >= 0 && port == 0 && resolved_tcp_port) {
+ *resolved_tcp_port = adb_socket_get_local_port(result);
+ }
+ return result;
+ }
+
+ for (const auto& it : kLocalSocketTypes) {
+ std::string prefix = it.first + ":";
+ if (StartsWith(spec, prefix.c_str())) {
+ if (!it.second.available) {
+ *error = StringPrintf("attempted to listen on unavailable socket type: '%s'",
+ spec.c_str());
+ return -1;
+ }
+
+ return network_local_server(&spec[prefix.length()], it.second.socket_namespace,
+ SOCK_STREAM, error);
+ }
+ }
+
+ *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+ return -1;
+}
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
new file mode 100644
index 0000000..6920e91
--- /dev/null
+++ b/adb/socket_spec.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+// Returns true if the argument starts with a plausible socket prefix.
+bool is_socket_spec(const std::string& spec);
+bool is_local_socket_spec(const std::string& spec);
+
+int socket_spec_connect(const std::string& spec, std::string* error);
+int socket_spec_listen(const std::string& spec, std::string* error,
+ int* resolved_tcp_port = nullptr);
+
+// Exposed for testing.
+bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
+ std::string* error);
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
new file mode 100644
index 0000000..40ce21c
--- /dev/null
+++ b/adb/socket_spec_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "socket_spec.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+TEST(socket_spec, parse_tcp_socket_spec) {
+ std::string hostname, error;
+ int port;
+ EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &error));
+ EXPECT_EQ("", hostname);
+ EXPECT_EQ(5037, port);
+
+ // Bad ports:
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &error));
+
+ EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &error));
+ EXPECT_EQ("localhost", hostname);
+ EXPECT_EQ(1234, port);
+
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &error));
+
+ // IPv6:
+ EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &error));
+ EXPECT_EQ("::1", hostname);
+ EXPECT_EQ(1234, port);
+
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &error));
+}
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
new file mode 100644
index 0000000..f56f7f7
--- /dev/null
+++ b/adb/socket_test.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fdevent.h"
+
+#include <gtest/gtest.h>
+
+#include <array>
+#include <limits>
+#include <queue>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <unistd.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "fdevent_test.h"
+#include "socket.h"
+#include "sysdeps.h"
+#include "sysdeps/chrono.h"
+
+struct ThreadArg {
+ int first_read_fd;
+ int last_write_fd;
+ size_t middle_pipe_count;
+};
+
+class LocalSocketTest : public FdeventTest {};
+
+static void FdEventThreadFunc(void*) {
+ fdevent_loop();
+}
+
+constexpr auto SLEEP_FOR_FDEVENT = 100ms;
+
+TEST_F(LocalSocketTest, smoke) {
+ // Join two socketpairs with a chain of intermediate socketpairs.
+ int first[2];
+ std::vector<std::array<int, 2>> intermediates;
+ int last[2];
+
+ constexpr size_t INTERMEDIATE_COUNT = 50;
+ constexpr size_t MESSAGE_LOOP_COUNT = 100;
+ const std::string MESSAGE = "socket_test";
+
+ intermediates.resize(INTERMEDIATE_COUNT);
+ ASSERT_EQ(0, adb_socketpair(first)) << strerror(errno);
+ ASSERT_EQ(0, adb_socketpair(last)) << strerror(errno);
+ asocket* prev_tail = create_local_socket(first[1]);
+ ASSERT_NE(nullptr, prev_tail);
+
+ auto connect = [](asocket* tail, asocket* head) {
+ tail->peer = head;
+ head->peer = tail;
+ tail->ready(tail);
+ };
+
+ for (auto& intermediate : intermediates) {
+ ASSERT_EQ(0, adb_socketpair(intermediate.data())) << strerror(errno);
+
+ asocket* head = create_local_socket(intermediate[0]);
+ ASSERT_NE(nullptr, head);
+
+ asocket* tail = create_local_socket(intermediate[1]);
+ ASSERT_NE(nullptr, tail);
+
+ connect(prev_tail, head);
+ prev_tail = tail;
+ }
+
+ asocket* end = create_local_socket(last[0]);
+ ASSERT_NE(nullptr, end);
+ connect(prev_tail, end);
+
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(FdEventThreadFunc, nullptr, &thread));
+
+ for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
+ std::string read_buffer = MESSAGE;
+ std::string write_buffer(MESSAGE.size(), 'a');
+ ASSERT_TRUE(WriteFdExactly(first[0], &read_buffer[0], read_buffer.size()));
+ ASSERT_TRUE(ReadFdExactly(last[1], &write_buffer[0], write_buffer.size()));
+ ASSERT_EQ(read_buffer, write_buffer);
+ }
+
+ ASSERT_EQ(0, adb_close(first[0]));
+ ASSERT_EQ(0, adb_close(last[1]));
+
+ // Wait until the local sockets are closed.
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
+ TerminateThread(thread);
+}
+
+struct CloseWithPacketArg {
+ int socket_fd;
+ size_t bytes_written;
+ int cause_close_fd;
+};
+
+static void CloseWithPacketThreadFunc(CloseWithPacketArg* arg) {
+ asocket* s = create_local_socket(arg->socket_fd);
+ ASSERT_TRUE(s != nullptr);
+ arg->bytes_written = 0;
+ while (true) {
+ apacket* p = get_apacket();
+ p->len = sizeof(p->data);
+ arg->bytes_written += p->len;
+ int ret = s->enqueue(s, p);
+ if (ret == 1) {
+ // The writer has one packet waiting to send.
+ break;
+ }
+ }
+
+ asocket* cause_close_s = create_local_socket(arg->cause_close_fd);
+ ASSERT_TRUE(cause_close_s != nullptr);
+ cause_close_s->peer = s;
+ s->peer = cause_close_s;
+ cause_close_s->ready(cause_close_s);
+
+ fdevent_loop();
+}
+
+// This test checks if we can close local socket in the following situation:
+// The socket is closing but having some packets, so it is not closed. Then
+// some write error happens in the socket's file handler, e.g., the file
+// handler is closed.
+TEST_F(LocalSocketTest, close_socket_with_packet) {
+ int socket_fd[2];
+ ASSERT_EQ(0, adb_socketpair(socket_fd));
+ int cause_close_fd[2];
+ ASSERT_EQ(0, adb_socketpair(cause_close_fd));
+ CloseWithPacketArg arg;
+ arg.socket_fd = socket_fd[1];
+ arg.cause_close_fd = cause_close_fd[1];
+
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
+ &arg, &thread));
+ // Wait until the fdevent_loop() starts.
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ ASSERT_EQ(0, adb_close(cause_close_fd[0]));
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
+ ASSERT_EQ(0, adb_close(socket_fd[0]));
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
+ TerminateThread(thread);
+}
+
+// This test checks if we can read packets from a closing local socket.
+TEST_F(LocalSocketTest, read_from_closing_socket) {
+ int socket_fd[2];
+ ASSERT_EQ(0, adb_socketpair(socket_fd));
+ int cause_close_fd[2];
+ ASSERT_EQ(0, adb_socketpair(cause_close_fd));
+ CloseWithPacketArg arg;
+ arg.socket_fd = socket_fd[1];
+ arg.cause_close_fd = cause_close_fd[1];
+
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
+ &arg, &thread));
+ // Wait until the fdevent_loop() starts.
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ ASSERT_EQ(0, adb_close(cause_close_fd[0]));
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
+
+ // Verify if we can read successfully.
+ std::vector<char> buf(arg.bytes_written);
+ ASSERT_NE(0u, arg.bytes_written);
+ ASSERT_EQ(true, ReadFdExactly(socket_fd[0], buf.data(), buf.size()));
+ ASSERT_EQ(0, adb_close(socket_fd[0]));
+
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
+ TerminateThread(thread);
+}
+
+// This test checks if we can close local socket in the following situation:
+// The socket is not closed and has some packets. When it fails to write to
+// the socket's file handler because the other end is closed, we check if the
+// socket is closed.
+TEST_F(LocalSocketTest, write_error_when_having_packets) {
+ int socket_fd[2];
+ ASSERT_EQ(0, adb_socketpair(socket_fd));
+ int cause_close_fd[2];
+ ASSERT_EQ(0, adb_socketpair(cause_close_fd));
+ CloseWithPacketArg arg;
+ arg.socket_fd = socket_fd[1];
+ arg.cause_close_fd = cause_close_fd[1];
+
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
+ &arg, &thread));
+
+ // Wait until the fdevent_loop() starts.
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ EXPECT_EQ(2u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
+ ASSERT_EQ(0, adb_close(socket_fd[0]));
+
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
+ TerminateThread(thread);
+}
+
+#if defined(__linux__)
+
+static void ClientThreadFunc() {
+ std::string error;
+ int fd = network_loopback_client(5038, SOCK_STREAM, &error);
+ ASSERT_GE(fd, 0) << error;
+ std::this_thread::sleep_for(200ms);
+ ASSERT_EQ(0, adb_close(fd));
+}
+
+struct CloseRdHupSocketArg {
+ int socket_fd;
+};
+
+static void CloseRdHupSocketThreadFunc(CloseRdHupSocketArg* arg) {
+ asocket* s = create_local_socket(arg->socket_fd);
+ ASSERT_TRUE(s != nullptr);
+
+ fdevent_loop();
+}
+
+// This test checks if we can close sockets in CLOSE_WAIT state.
+TEST_F(LocalSocketTest, close_socket_in_CLOSE_WAIT_state) {
+ std::string error;
+ int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
+ ASSERT_GE(listen_fd, 0);
+
+ adb_thread_t client_thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(ClientThreadFunc), nullptr,
+ &client_thread));
+
+ int accept_fd = adb_socket_accept(listen_fd, nullptr, nullptr);
+ ASSERT_GE(accept_fd, 0);
+ CloseRdHupSocketArg arg;
+ arg.socket_fd = accept_fd;
+
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseRdHupSocketThreadFunc),
+ &arg, &thread));
+
+ // Wait until the fdevent_loop() starts.
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
+
+ // Wait until the client closes its socket.
+ ASSERT_TRUE(adb_thread_join(client_thread));
+
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
+ TerminateThread(thread);
+}
+
+#endif // defined(__linux__)
+
+#if ADB_HOST
+
+// Checks that skip_host_serial(serial) returns a pointer to the part of |serial| which matches
+// |expected|, otherwise logs the failure to gtest.
+void VerifySkipHostSerial(std::string serial, const char* expected) {
+ char* result = internal::skip_host_serial(&serial[0]);
+ if (expected == nullptr) {
+ EXPECT_EQ(nullptr, result);
+ } else {
+ EXPECT_STREQ(expected, result);
+ }
+}
+
+// Check [tcp:|udp:]<serial>[:<port>]:<command> format.
+TEST(socket_test, test_skip_host_serial) {
+ for (const std::string& protocol : {"", "tcp:", "udp:"}) {
+ VerifySkipHostSerial(protocol, nullptr);
+ VerifySkipHostSerial(protocol + "foo", nullptr);
+
+ VerifySkipHostSerial(protocol + "foo:bar", ":bar");
+ VerifySkipHostSerial(protocol + "foo:bar:baz", ":bar:baz");
+
+ VerifySkipHostSerial(protocol + "foo:123:bar", ":bar");
+ VerifySkipHostSerial(protocol + "foo:123:456", ":456");
+ VerifySkipHostSerial(protocol + "foo:123:bar:baz", ":bar:baz");
+
+ // Don't register a port unless it's all numbers and ends with ':'.
+ VerifySkipHostSerial(protocol + "foo:123", ":123");
+ VerifySkipHostSerial(protocol + "foo:123bar:baz", ":123bar:baz");
+
+ VerifySkipHostSerial(protocol + "100.100.100.100:5555:foo", ":foo");
+ VerifySkipHostSerial(protocol + "[0123:4567:89ab:CDEF:0:9:a:f]:5555:foo", ":foo");
+ VerifySkipHostSerial(protocol + "[::1]:5555:foo", ":foo");
+
+ // If we can't find both [] then treat it as a normal serial with [ in it.
+ VerifySkipHostSerial(protocol + "[0123:foo", ":foo");
+
+ // Don't be fooled by random IPv6 addresses in the command string.
+ VerifySkipHostSerial(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
+ ":ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+ }
+}
+
+// Check <prefix>:<serial>:<command> format.
+TEST(socket_test, test_skip_host_serial_prefix) {
+ for (const std::string& prefix : {"usb:", "product:", "model:", "device:"}) {
+ VerifySkipHostSerial(prefix, nullptr);
+ VerifySkipHostSerial(prefix + "foo", nullptr);
+
+ VerifySkipHostSerial(prefix + "foo:bar", ":bar");
+ VerifySkipHostSerial(prefix + "foo:bar:baz", ":bar:baz");
+ VerifySkipHostSerial(prefix + "foo:123:bar", ":123:bar");
+ }
+}
+
+#endif // ADB_HOST
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index d8ea2ee..c05903f 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_SOCKETS
+#define TRACE_TAG SOCKETS
#include "sysdeps.h"
@@ -25,23 +25,25 @@
#include <string.h>
#include <unistd.h>
+#include <algorithm>
+#include <mutex>
+#include <string>
+#include <vector>
+
#if !ADB_HOST
-#include "cutils/properties.h"
+#include <android-base/properties.h>
+#include <private/android_logger.h>
#endif
#include "adb.h"
#include "adb_io.h"
#include "transport.h"
-ADB_MUTEX_DEFINE( socket_list_lock );
-
-static void local_socket_close_locked(asocket *s);
-
+static std::recursive_mutex& local_socket_list_lock = *new std::recursive_mutex();
static unsigned local_socket_next_id = 1;
static asocket local_socket_list = {
- .next = &local_socket_list,
- .prev = &local_socket_list,
+ .next = &local_socket_list, .prev = &local_socket_list,
};
/* the the list of currently closing local sockets.
@@ -49,62 +51,53 @@
** write to their fd.
*/
static asocket local_socket_closing_list = {
- .next = &local_socket_closing_list,
- .prev = &local_socket_closing_list,
+ .next = &local_socket_closing_list, .prev = &local_socket_closing_list,
};
// Parse the global list of sockets to find one with id |local_id|.
// If |peer_id| is not 0, also check that it is connected to a peer
// with id |peer_id|. Returns an asocket handle on success, NULL on failure.
-asocket *find_local_socket(unsigned local_id, unsigned peer_id)
-{
- asocket *s;
- asocket *result = NULL;
+asocket* find_local_socket(unsigned local_id, unsigned peer_id) {
+ asocket* s;
+ asocket* result = NULL;
- adb_mutex_lock(&socket_list_lock);
+ std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
- if (s->id != local_id)
+ if (s->id != local_id) {
continue;
+ }
if (peer_id == 0 || (s->peer && s->peer->id == peer_id)) {
result = s;
}
break;
}
- adb_mutex_unlock(&socket_list_lock);
return result;
}
-static void
-insert_local_socket(asocket* s, asocket* list)
-{
- s->next = list;
- s->prev = s->next->prev;
+static void insert_local_socket(asocket* s, asocket* list) {
+ s->next = list;
+ s->prev = s->next->prev;
s->prev->next = s;
s->next->prev = s;
}
-
-void install_local_socket(asocket *s)
-{
- adb_mutex_lock(&socket_list_lock);
+void install_local_socket(asocket* s) {
+ std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
s->id = local_socket_next_id++;
// Socket ids should never be 0.
- if (local_socket_next_id == 0)
- local_socket_next_id = 1;
+ if (local_socket_next_id == 0) {
+ fatal("local socket id overflow");
+ }
insert_local_socket(s, &local_socket_list);
-
- adb_mutex_unlock(&socket_list_lock);
}
-void remove_socket(asocket *s)
-{
+void remove_socket(asocket* s) {
// socket_list_lock should already be held
- if (s->prev && s->next)
- {
+ if (s->prev && s->next) {
s->prev->next = s->next;
s->next->prev = s->prev;
s->next = 0;
@@ -113,50 +106,49 @@
}
}
-void close_all_sockets(atransport *t)
-{
- asocket *s;
+void close_all_sockets(atransport* t) {
+ asocket* s;
- /* this is a little gross, but since s->close() *will* modify
- ** the list out from under you, your options are limited.
- */
- adb_mutex_lock(&socket_list_lock);
+ /* this is a little gross, but since s->close() *will* modify
+ ** the list out from under you, your options are limited.
+ */
+ std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
restart:
- for(s = local_socket_list.next; s != &local_socket_list; s = s->next){
- if(s->transport == t || (s->peer && s->peer->transport == t)) {
- local_socket_close_locked(s);
+ for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
+ if (s->transport == t || (s->peer && s->peer->transport == t)) {
+ s->close(s);
goto restart;
}
}
- adb_mutex_unlock(&socket_list_lock);
}
-static int local_socket_enqueue(asocket *s, apacket *p)
-{
- D("LS(%d): enqueue %d\n", s->id, p->len);
+static int local_socket_enqueue(asocket* s, apacket* p) {
+ D("LS(%d): enqueue %zu", s->id, p->len);
p->ptr = p->data;
- /* if there is already data queue'd, we will receive
- ** events when it's time to write. just add this to
- ** the tail
- */
- if(s->pkt_first) {
+ /* if there is already data queue'd, we will receive
+ ** events when it's time to write. just add this to
+ ** the tail
+ */
+ if (s->pkt_first) {
goto enqueue;
}
- /* write as much as we can, until we
- ** would block or there is an error/eof
- */
- while(p->len > 0) {
+ /* write as much as we can, until we
+ ** would block or there is an error/eof
+ */
+ while (p->len > 0) {
int r = adb_write(s->fd, p->ptr, p->len);
- if(r > 0) {
+ if (r > 0) {
p->len -= r;
p->ptr += r;
continue;
}
- if((r == 0) || (errno != EAGAIN)) {
- D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
+ if ((r == 0) || (errno != EAGAIN)) {
+ D("LS(%d): not ready, errno=%d: %s", s->id, errno, strerror(errno));
+ put_apacket(p);
+ s->has_write_error = true;
s->close(s);
return 1; /* not ready (error) */
} else {
@@ -164,56 +156,47 @@
}
}
- if(p->len == 0) {
+ if (p->len == 0) {
put_apacket(p);
return 0; /* ready for more data */
}
enqueue:
p->next = 0;
- if(s->pkt_first) {
+ if (s->pkt_first) {
s->pkt_last->next = p;
} else {
s->pkt_first = p;
}
s->pkt_last = p;
- /* make sure we are notified when we can drain the queue */
+ /* make sure we are notified when we can drain the queue */
fdevent_add(&s->fde, FDE_WRITE);
return 1; /* not ready (backlog) */
}
-static void local_socket_ready(asocket *s)
-{
+static void local_socket_ready(asocket* s) {
/* far side is ready for data, pay attention to
readable events */
fdevent_add(&s->fde, FDE_READ);
}
-static void local_socket_close(asocket *s)
-{
- adb_mutex_lock(&socket_list_lock);
- local_socket_close_locked(s);
- adb_mutex_unlock(&socket_list_lock);
-}
-
// be sure to hold the socket list lock when calling this
-static void local_socket_destroy(asocket *s)
-{
+static void local_socket_destroy(asocket* s) {
apacket *p, *n;
int exit_on_close = s->exit_on_close;
- D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd);
+ D("LS(%d): destroying fde.fd=%d", s->id, s->fde.fd);
- /* IMPORTANT: the remove closes the fd
- ** that belongs to this socket
- */
+ /* IMPORTANT: the remove closes the fd
+ ** that belongs to this socket
+ */
fdevent_remove(&s->fde);
- /* dispose of any unwritten data */
- for(p = s->pkt_first; p; p = n) {
- D("LS(%d): discarding %d bytes\n", s->id, p->len);
+ /* dispose of any unwritten data */
+ for (p = s->pkt_first; p; p = n) {
+ D("LS(%d): discarding %zu bytes", s->id, p->len);
n = p->next;
put_apacket(p);
}
@@ -221,58 +204,52 @@
free(s);
if (exit_on_close) {
- D("local_socket_destroy: exiting\n");
+ D("local_socket_destroy: exiting");
exit(1);
}
}
-
-static void local_socket_close_locked(asocket *s)
-{
- D("entered local_socket_close_locked. LS(%d) fd=%d\n", s->id, s->fd);
- if(s->peer) {
- D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
- s->id, s->peer->id, s->peer->fd);
+static void local_socket_close(asocket* s) {
+ D("entered local_socket_close. LS(%d) fd=%d", s->id, s->fd);
+ std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
+ if (s->peer) {
+ D("LS(%d): closing peer. peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
/* Note: it's important to call shutdown before disconnecting from
* the peer, this ensures that remote sockets can still get the id
* of the local socket they're connected to, to send a CLOSE()
* protocol event. */
- if (s->peer->shutdown)
- s->peer->shutdown(s->peer);
- s->peer->peer = 0;
- // tweak to avoid deadlock
- if (s->peer->close == local_socket_close) {
- local_socket_close_locked(s->peer);
- } else {
- s->peer->close(s->peer);
+ if (s->peer->shutdown) {
+ s->peer->shutdown(s->peer);
}
- s->peer = 0;
+ s->peer->peer = nullptr;
+ s->peer->close(s->peer);
+ s->peer = nullptr;
}
- /* If we are already closing, or if there are no
- ** pending packets, destroy immediately
- */
- if (s->closing || s->pkt_first == NULL) {
- int id = s->id;
+ /* If we are already closing, or if there are no
+ ** pending packets, destroy immediately
+ */
+ if (s->closing || s->has_write_error || s->pkt_first == NULL) {
+ int id = s->id;
local_socket_destroy(s);
- D("LS(%d): closed\n", id);
+ D("LS(%d): closed", id);
return;
}
- /* otherwise, put on the closing list
- */
- D("LS(%d): closing\n", s->id);
+ /* otherwise, put on the closing list
+ */
+ D("LS(%d): closing", s->id);
s->closing = 1;
fdevent_del(&s->fde, FDE_READ);
remove_socket(s);
- D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd);
+ D("LS(%d): put on socket_closing_list fd=%d", s->id, s->fd);
insert_local_socket(s, &local_socket_closing_list);
+ CHECK_EQ(FDE_WRITE, s->fde.state & FDE_WRITE);
}
-static void local_socket_event_func(int fd, unsigned ev, void* _s)
-{
+static void local_socket_event_func(int fd, unsigned ev, void* _s) {
asocket* s = reinterpret_cast<asocket*>(_s);
- D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
+ D("LS(%d): event_func(fd=%d(==%d), ev=%04x)", s->id, s->fd, fd, ev);
/* put the FDE_WRITE processing before the FDE_READ
** in order to simplify the code.
@@ -295,7 +272,8 @@
continue;
}
- D(" closing after write because r=%d and errno is %d\n", r, errno);
+ D(" closing after write because r=%d and errno is %d", r, errno);
+ s->has_write_error = true;
s->close(s);
return;
}
@@ -313,7 +291,7 @@
** we can now destroy it.
*/
if (s->closing) {
- D(" closing because 'closing' is set after write\n");
+ D(" closing because 'closing' is set after write");
s->close(s);
return;
}
@@ -326,10 +304,9 @@
s->peer->ready(s->peer);
}
-
if (ev & FDE_READ) {
- apacket *p = get_apacket();
- unsigned char *x = p->data;
+ apacket* p = get_apacket();
+ char* x = p->data;
const size_t max_payload = s->get_max_payload();
size_t avail = max_payload;
int r = 0;
@@ -337,8 +314,8 @@
while (avail > 0) {
r = adb_read(fd, x, avail);
- D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu\n",
- s->id, s->fd, r, r < 0 ? errno : 0, avail);
+ D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu", s->id, s->fd, r,
+ r < 0 ? errno : 0, avail);
if (r == -1) {
if (errno == EAGAIN) {
break;
@@ -353,60 +330,63 @@
is_eof = 1;
break;
}
- D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
- s->id, s->fd, r, is_eof, s->fde.force_eof);
+ 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);
if ((avail == max_payload) || (s->peer == 0)) {
put_apacket(p);
} else {
p->len = max_payload - avail;
+ // s->peer->enqueue() may call s->close() and free s,
+ // so save variables for debug printing below.
+ unsigned saved_id = s->id;
+ int saved_fd = s->fd;
r = s->peer->enqueue(s->peer, p);
- D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd,
- r);
+ D("LS(%u): fd=%d post peer->enqueue(). r=%d", saved_id, saved_fd, r);
if (r < 0) {
- /* error return means they closed us as a side-effect
- ** and we must return immediately.
- **
- ** note that if we still have buffered packets, the
- ** socket will be placed on the closing socket list.
- ** this handler function will be called again
- ** to process FDE_WRITE events.
- */
+ /* error return means they closed us as a side-effect
+ ** and we must return immediately.
+ **
+ ** note that if we still have buffered packets, the
+ ** socket will be placed on the closing socket list.
+ ** this handler function will be called again
+ ** to process FDE_WRITE events.
+ */
return;
}
if (r > 0) {
- /* if the remote cannot accept further events,
- ** we disable notification of READs. They'll
- ** be enabled again when we get a call to ready()
- */
+ /* if the remote cannot accept further events,
+ ** we disable notification of READs. They'll
+ ** be enabled again when we get a call to ready()
+ */
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\n",
- is_eof, r, s->fde.force_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;
}
}
- if (ev & FDE_ERROR){
- /* this should be caught be the next read or write
- ** catching it here means we may skip the last few
- ** bytes of readable data.
- */
- D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
-
+ if (ev & FDE_ERROR) {
+ /* this should be caught be the next read or write
+ ** catching it here means we may skip the last few
+ ** bytes of readable data.
+ */
+ D("LS(%d): FDE_ERROR (fd=%d)", s->id, s->fd);
return;
}
}
-asocket *create_local_socket(int fd)
-{
- asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
- if (s == NULL) fatal("cannot allocate socket");
+asocket* create_local_socket(int fd) {
+ asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
+ if (s == NULL) {
+ fatal("cannot allocate socket");
+ }
s->fd = fd;
s->enqueue = local_socket_enqueue;
s->ready = local_socket_ready;
@@ -415,36 +395,33 @@
install_local_socket(s);
fdevent_install(&s->fde, fd, local_socket_event_func, s);
- D("LS(%d): created (fd=%d)\n", s->id, s->fd);
+ D("LS(%d): created (fd=%d)", s->id, s->fd);
return s;
}
-asocket *create_local_service_socket(const char *name)
-{
+asocket* create_local_service_socket(const char* name, const atransport* transport) {
#if !ADB_HOST
- if (!strcmp(name,"jdwp")) {
+ if (!strcmp(name, "jdwp")) {
return create_jdwp_service_socket();
}
- if (!strcmp(name,"track-jdwp")) {
+ if (!strcmp(name, "track-jdwp")) {
return create_jdwp_tracker_service_socket();
}
#endif
- int fd = service_to_fd(name);
- if(fd < 0) return 0;
+ int fd = service_to_fd(name, transport);
+ if (fd < 0) {
+ return nullptr;
+ }
asocket* s = create_local_socket(fd);
- D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
+ D("LS(%d): bound to '%s' via %d", s->id, name, fd);
#if !ADB_HOST
- char debug[PROPERTY_VALUE_MAX];
- if (!strncmp(name, "root:", 5))
- property_get("ro.debuggable", debug, "");
-
- if ((!strncmp(name, "root:", 5) && getuid() != 0 && strcmp(debug, "1") == 0)
- || (!strncmp(name, "unroot:", 7) && getuid() == 0)
- || !strncmp(name, "usb:", 4)
- || !strncmp(name, "tcpip:", 6)) {
- D("LS(%d): enabling exit_on_close\n", s->id);
+ if ((!strncmp(name, "root:", 5) && getuid() != 0 && __android_log_is_debuggable()) ||
+ (!strncmp(name, "unroot:", 7) && getuid() == 0) ||
+ !strncmp(name, "usb:", 4) ||
+ !strncmp(name, "tcpip:", 6)) {
+ D("LS(%d): enabling exit_on_close", s->id);
s->exit_on_close = 1;
}
#endif
@@ -453,14 +430,13 @@
}
#if ADB_HOST
-static asocket *create_host_service_socket(const char *name, const char* serial)
-{
- asocket *s;
+static asocket* create_host_service_socket(const char* name, const char* serial) {
+ asocket* s;
s = host_service_to_socket(name, serial);
if (s != NULL) {
- D("LS(%d) bound to '%s'\n", s->id, name);
+ D("LS(%d) bound to '%s'", s->id, name);
return s;
}
@@ -468,18 +444,8 @@
}
#endif /* ADB_HOST */
-/* a Remote socket is used to send/receive data to/from a given transport object
-** it needs to be closed when the transport is forcibly destroyed by the user
-*/
-struct aremotesocket {
- asocket socket;
- adisconnect disconnect;
-};
-
-static int remote_socket_enqueue(asocket *s, apacket *p)
-{
- D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
- s->id, s->fd, s->peer->fd);
+static int remote_socket_enqueue(asocket* s, apacket* p) {
+ D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d", s->id, s->fd, s->peer->fd);
p->msg.command = A_WRTE;
p->msg.arg0 = s->peer->id;
p->msg.arg1 = s->id;
@@ -488,70 +454,52 @@
return 1;
}
-static void remote_socket_ready(asocket *s)
-{
- D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n",
- s->id, s->fd, s->peer->fd);
- apacket *p = get_apacket();
+static void remote_socket_ready(asocket* s) {
+ D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d", s->id, s->fd, s->peer->fd);
+ apacket* p = get_apacket();
p->msg.command = A_OKAY;
p->msg.arg0 = s->peer->id;
p->msg.arg1 = s->id;
send_packet(p, s->transport);
}
-static void remote_socket_shutdown(asocket *s)
-{
- D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d\n",
- s->id, s->fd, s->peer?s->peer->fd:-1);
- apacket *p = get_apacket();
+static void remote_socket_shutdown(asocket* s) {
+ D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d", s->id, s->fd,
+ s->peer ? s->peer->fd : -1);
+ apacket* p = get_apacket();
p->msg.command = A_CLSE;
- if(s->peer) {
+ if (s->peer) {
p->msg.arg0 = s->peer->id;
}
p->msg.arg1 = s->id;
send_packet(p, s->transport);
}
-static void remote_socket_close(asocket *s)
-{
+static void remote_socket_close(asocket* s) {
if (s->peer) {
s->peer->peer = 0;
- D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
- s->id, s->peer->id, s->peer->fd);
+ D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
s->peer->close(s->peer);
}
- D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
- s->id, s->fd, s->peer?s->peer->fd:-1);
- D("RS(%d): closed\n", s->id);
- remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+ D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d", s->id, s->fd,
+ s->peer ? s->peer->fd : -1);
+ D("RS(%d): closed", s->id);
free(s);
}
-static void remote_socket_disconnect(void* _s, atransport* t)
-{
- asocket* s = reinterpret_cast<asocket*>(_s);
- asocket* peer = s->peer;
-
- D("remote_socket_disconnect RS(%d)\n", s->id);
- if (peer) {
- peer->peer = NULL;
- peer->close(peer);
+// Create a remote socket to exchange packets with a remote service through transport
+// |t|. Where |id| is the socket id of the corresponding service on the other
+// side of the transport (it is allocated by the remote side and _cannot_ be 0).
+// Returns a new non-NULL asocket handle.
+asocket* create_remote_socket(unsigned id, atransport* t) {
+ if (id == 0) {
+ fatal("invalid remote socket id (0)");
}
- remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
- free(s);
-}
+ asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-/* Create an asocket to exchange packets with a remote service through transport
- |t|. Where |id| is the socket id of the corresponding service on the other
- side of the transport (it is allocated by the remote side and _cannot_ be 0).
- Returns a new non-NULL asocket handle. */
-asocket *create_remote_socket(unsigned id, atransport *t)
-{
- if (id == 0) fatal("invalid remote socket id (0)");
- asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(aremotesocket)));
- adisconnect* dis = &reinterpret_cast<aremotesocket*>(s)->disconnect;
-
- if (s == NULL) fatal("cannot allocate socket");
+ if (s == NULL) {
+ fatal("cannot allocate socket");
+ }
s->id = id;
s->enqueue = remote_socket_enqueue;
s->ready = remote_socket_ready;
@@ -559,36 +507,30 @@
s->close = remote_socket_close;
s->transport = t;
- dis->func = remote_socket_disconnect;
- dis->opaque = s;
- add_transport_disconnect( t, dis );
- D("RS(%d): created\n", s->id);
+ D("RS(%d): created", s->id);
return s;
}
-void connect_to_remote(asocket *s, const char *destination)
-{
- D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd);
- apacket *p = get_apacket();
+void connect_to_remote(asocket* s, const char* destination) {
+ D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
+ apacket* p = get_apacket();
size_t len = strlen(destination) + 1;
- if(len > (s->get_max_payload()-1)) {
+ if (len > (s->get_max_payload() - 1)) {
fatal("destination oversized");
}
- D("LS(%d): connect('%s')\n", s->id, destination);
+ D("LS(%d): connect('%s')", s->id, destination);
p->msg.command = A_OPEN;
p->msg.arg0 = s->id;
p->msg.data_length = len;
- strcpy((char*) p->data, destination);
+ strcpy((char*)p->data, destination);
send_packet(p, s->transport);
}
-
/* this is used by magic sockets to rig local sockets to
send the go-ahead message when they connect */
-static void local_socket_ready_notify(asocket *s)
-{
+static void local_socket_ready_notify(asocket* s) {
s->ready = local_socket_ready;
s->shutdown = NULL;
s->close = local_socket_close;
@@ -599,8 +541,7 @@
/* this is used by magic sockets to rig local sockets to
send the failure message if they are closed before
connected (to avoid closing them without a status message) */
-static void local_socket_close_notify(asocket *s)
-{
+static void local_socket_close_notify(asocket* s) {
s->ready = local_socket_ready;
s->shutdown = NULL;
s->close = local_socket_close;
@@ -608,28 +549,41 @@
s->close(s);
}
-static unsigned unhex(unsigned char *s, int len)
-{
+static unsigned unhex(char* s, int len) {
unsigned n = 0, c;
- while(len-- > 0) {
- switch((c = *s++)) {
- case '0': case '1': case '2':
- case '3': case '4': case '5':
- case '6': case '7': case '8':
- case '9':
- c -= '0';
- break;
- case 'a': case 'b': case 'c':
- case 'd': case 'e': case 'f':
- c = c - 'a' + 10;
- break;
- case 'A': case 'B': case 'C':
- case 'D': case 'E': case 'F':
- c = c - 'A' + 10;
- break;
- default:
- return 0xffffffff;
+ while (len-- > 0) {
+ switch ((c = *s++)) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ c -= '0';
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ c = c - 'a' + 10;
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ c = c - 'A' + 10;
+ break;
+ default:
+ return 0xffffffff;
}
n = (n << 4) | c;
@@ -640,111 +594,124 @@
#if ADB_HOST
-#define PREFIX(str) { str, sizeof(str) - 1 }
-static const struct prefix_struct {
- const char *str;
- const size_t len;
-} prefixes[] = {
- PREFIX("usb:"),
- PREFIX("product:"),
- PREFIX("model:"),
- PREFIX("device:"),
-};
-static const int num_prefixes = (sizeof(prefixes) / sizeof(prefixes[0]));
+namespace internal {
-/* skip_host_serial return the position in a string
- skipping over the 'serial' parameter in the ADB protocol,
- where parameter string may be a host:port string containing
- the protocol delimiter (colon). */
-static char *skip_host_serial(char *service) {
- char *first_colon, *serial_end;
- int i;
+// Returns the position in |service| following the target serial parameter. Serial format can be
+// any of:
+// * [tcp:|udp:]<serial>[:<port>]:<command>
+// * <prefix>:<serial>:<command>
+// Where <port> must be a base-10 number and <prefix> may be any of {usb,product,model,device}.
+//
+// The returned pointer will point to the ':' just before <command>, or nullptr if not found.
+char* skip_host_serial(char* service) {
+ static const std::vector<std::string>& prefixes =
+ *(new std::vector<std::string>{"usb:", "product:", "model:", "device:"});
- for (i = 0; i < num_prefixes; i++) {
- if (!strncmp(service, prefixes[i].str, prefixes[i].len))
- return strchr(service + prefixes[i].len, ':');
+ for (const std::string& prefix : prefixes) {
+ if (!strncmp(service, prefix.c_str(), prefix.length())) {
+ return strchr(service + prefix.length(), ':');
+ }
}
- first_colon = strchr(service, ':');
- if (!first_colon) {
- /* No colon in service string. */
- return NULL;
+ // For fastboot compatibility, ignore protocol prefixes.
+ if (!strncmp(service, "tcp:", 4) || !strncmp(service, "udp:", 4)) {
+ service += 4;
}
- serial_end = first_colon;
+
+ // Check for an IPv6 address. `adb connect` creates the serial number from the canonical
+ // network address so it will always have the [] delimiters.
+ if (service[0] == '[') {
+ char* ipv6_end = strchr(service, ']');
+ if (ipv6_end != nullptr) {
+ service = ipv6_end;
+ }
+ }
+
+ // The next colon we find must either begin the port field or the command field.
+ char* colon_ptr = strchr(service, ':');
+ if (!colon_ptr) {
+ // No colon in service string.
+ return nullptr;
+ }
+
+ // If the next field is only decimal digits and ends with another colon, it's a port.
+ char* serial_end = colon_ptr;
if (isdigit(serial_end[1])) {
serial_end++;
- while ((*serial_end) && isdigit(*serial_end)) {
+ while (*serial_end && isdigit(*serial_end)) {
serial_end++;
}
- if ((*serial_end) != ':') {
- // Something other than numbers was found, reset the end.
- serial_end = first_colon;
+ if (*serial_end != ':') {
+ // Something other than "<port>:" was found, this must be the command field instead.
+ serial_end = colon_ptr;
}
}
return serial_end;
}
-#endif // ADB_HOST
+} // namespace internal
-static int smart_socket_enqueue(asocket *s, apacket *p)
-{
+#endif // ADB_HOST
+
+static int smart_socket_enqueue(asocket* s, apacket* p) {
unsigned len;
#if ADB_HOST
- char *service = NULL;
- char* serial = NULL;
+ char* service = nullptr;
+ char* serial = nullptr;
TransportType type = kTransportAny;
#endif
- D("SS(%d): enqueue %d\n", s->id, p->len);
+ D("SS(%d): enqueue %zu", s->id, p->len);
- if(s->pkt_first == 0) {
+ if (s->pkt_first == 0) {
s->pkt_first = p;
s->pkt_last = p;
} else {
- if((s->pkt_first->len + p->len) > s->get_max_payload()) {
- D("SS(%d): overflow\n", s->id);
+ if ((s->pkt_first->len + p->len) > s->get_max_payload()) {
+ D("SS(%d): overflow", s->id);
put_apacket(p);
goto fail;
}
- memcpy(s->pkt_first->data + s->pkt_first->len,
- p->data, p->len);
+ memcpy(s->pkt_first->data + s->pkt_first->len, p->data, p->len);
s->pkt_first->len += p->len;
put_apacket(p);
p = s->pkt_first;
}
- /* don't bother if we can't decode the length */
- if(p->len < 4) return 0;
+ /* don't bother if we can't decode the length */
+ if (p->len < 4) {
+ return 0;
+ }
len = unhex(p->data, 4);
- if((len < 1) || (len > 1024)) {
- D("SS(%d): bad size (%d)\n", s->id, len);
+ if ((len < 1) || (len > MAX_PAYLOAD_V1)) {
+ D("SS(%d): bad size (%d)", s->id, len);
goto fail;
}
- D("SS(%d): len is %d\n", s->id, len );
- /* can't do anything until we have the full header */
- if((len + 4) > p->len) {
- D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
+ D("SS(%d): len is %d", s->id, len);
+ /* can't do anything until we have the full header */
+ if ((len + 4) > p->len) {
+ D("SS(%d): waiting for %zu more bytes", s->id, len + 4 - p->len);
return 0;
}
p->data[len + 4] = 0;
- D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
+ D("SS(%d): '%s'", s->id, (char*)(p->data + 4));
#if ADB_HOST
- service = (char *)p->data + 4;
- if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {
+ service = (char*)p->data + 4;
+ if (!strncmp(service, "host-serial:", strlen("host-serial:"))) {
char* serial_end;
service += strlen("host-serial:");
// serial number should follow "host:" and could be a host:port string.
- serial_end = skip_host_serial(service);
+ serial_end = internal::skip_host_serial(service);
if (serial_end) {
- *serial_end = 0; // terminate string
+ *serial_end = 0; // terminate string
serial = service;
service = serial_end + 1;
}
@@ -758,121 +725,119 @@
type = kTransportAny;
service += strlen("host:");
} else {
- service = NULL;
+ service = nullptr;
}
if (service) {
- asocket *s2;
+ asocket* s2;
- /* some requests are handled immediately -- in that
- ** case the handle_host_request() routine has sent
- ** the OKAY or FAIL message and all we have to do
- ** is clean up.
- */
- if(handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
- /* XXX fail message? */
- D( "SS(%d): handled host service '%s'\n", s->id, service );
+ /* some requests are handled immediately -- in that
+ ** case the handle_host_request() routine has sent
+ ** the OKAY or FAIL message and all we have to do
+ ** is clean up.
+ */
+ if (handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
+ /* XXX fail message? */
+ D("SS(%d): handled host service '%s'", s->id, service);
goto fail;
}
if (!strncmp(service, "transport", strlen("transport"))) {
- D( "SS(%d): okay transport\n", s->id );
+ D("SS(%d): okay transport", s->id);
p->len = 0;
return 0;
}
- /* try to find a local service with this name.
- ** if no such service exists, we'll fail out
- ** and tear down here.
- */
+ /* try to find a local service with this name.
+ ** if no such service exists, we'll fail out
+ ** and tear down here.
+ */
s2 = create_host_service_socket(service, serial);
- if(s2 == 0) {
- D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
+ if (s2 == 0) {
+ D("SS(%d): couldn't create host service '%s'", s->id, service);
SendFail(s->peer->fd, "unknown host service");
goto fail;
}
- /* we've connected to a local host service,
- ** so we make our peer back into a regular
- ** local socket and bind it to the new local
- ** service socket, acknowledge the successful
- ** connection, and close this smart socket now
- ** that its work is done.
- */
+ /* we've connected to a local host service,
+ ** so we make our peer back into a regular
+ ** local socket and bind it to the new local
+ ** service socket, acknowledge the successful
+ ** connection, and close this smart socket now
+ ** that its work is done.
+ */
SendOkay(s->peer->fd);
s->peer->ready = local_socket_ready;
- s->peer->shutdown = NULL;
+ s->peer->shutdown = nullptr;
s->peer->close = local_socket_close;
s->peer->peer = s2;
s2->peer = s->peer;
s->peer = 0;
- D( "SS(%d): okay\n", s->id );
+ D("SS(%d): okay", s->id);
s->close(s);
- /* initial state is "ready" */
+ /* initial state is "ready" */
s2->ready(s2);
return 0;
}
#else /* !ADB_HOST */
- if (s->transport == NULL) {
+ if (s->transport == nullptr) {
std::string error_msg = "unknown failure";
- s->transport =
- acquire_one_transport(kCsAny, kTransportAny, NULL, &error_msg);
-
- if (s->transport == NULL) {
+ s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg);
+ if (s->transport == nullptr) {
SendFail(s->peer->fd, error_msg);
goto fail;
}
}
#endif
- if(!(s->transport) || (s->transport->connection_state == kCsOffline)) {
- /* if there's no remote we fail the connection
- ** right here and terminate it
- */
- SendFail(s->peer->fd, "device offline (x)");
+ if (!s->transport) {
+ SendFail(s->peer->fd, "device offline (no transport)");
+ goto fail;
+ } else if (s->transport->connection_state == kCsOffline) {
+ /* if there's no remote we fail the connection
+ ** right here and terminate it
+ */
+ SendFail(s->peer->fd, "device offline (transport offline)");
goto fail;
}
-
- /* instrument our peer to pass the success or fail
- ** message back once it connects or closes, then
- ** detach from it, request the connection, and
- ** tear down
- */
+ /* instrument our peer to pass the success or fail
+ ** message back once it connects or closes, then
+ ** detach from it, request the connection, and
+ ** tear down
+ */
s->peer->ready = local_socket_ready_notify;
- s->peer->shutdown = NULL;
+ s->peer->shutdown = nullptr;
s->peer->close = local_socket_close_notify;
s->peer->peer = 0;
- /* give him our transport and upref it */
+ /* give him our transport and upref it */
s->peer->transport = s->transport;
- connect_to_remote(s->peer, (char*) (p->data + 4));
+ connect_to_remote(s->peer, (char*)(p->data + 4));
s->peer = 0;
s->close(s);
return 1;
fail:
- /* we're going to close our peer as a side-effect, so
- ** return -1 to signal that state to the local socket
- ** who is enqueueing against us
- */
+ /* we're going to close our peer as a side-effect, so
+ ** return -1 to signal that state to the local socket
+ ** who is enqueueing against us
+ */
s->close(s);
return -1;
}
-static void smart_socket_ready(asocket *s)
-{
- D("SS(%d): ready\n", s->id);
+static void smart_socket_ready(asocket* s) {
+ D("SS(%d): ready", s->id);
}
-static void smart_socket_close(asocket *s)
-{
- D("SS(%d): closed\n", s->id);
- if(s->pkt_first){
+static void smart_socket_close(asocket* s) {
+ D("SS(%d): closed", s->id);
+ if (s->pkt_first) {
put_apacket(s->pkt_first);
}
- if(s->peer) {
+ if (s->peer) {
s->peer->peer = 0;
s->peer->close(s->peer);
s->peer = 0;
@@ -880,24 +845,22 @@
free(s);
}
-static asocket *create_smart_socket(void)
-{
- D("Creating smart socket \n");
- asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
+static asocket* create_smart_socket(void) {
+ D("Creating smart socket");
+ asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
if (s == NULL) fatal("cannot allocate socket");
s->enqueue = smart_socket_enqueue;
s->ready = smart_socket_ready;
s->shutdown = NULL;
s->close = smart_socket_close;
- D("SS(%d)\n", s->id);
+ D("SS(%d)", s->id);
return s;
}
-void connect_to_smartsocket(asocket *s)
-{
- D("Connecting to smart socket \n");
- asocket *ss = create_smart_socket();
+void connect_to_smartsocket(asocket* s) {
+ D("Connecting to smart socket");
+ asocket* ss = create_smart_socket();
s->peer = ss;
ss->peer = s;
s->ready(s);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 6f3c443..654072c 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -27,6 +27,15 @@
#include <errno.h>
#include <string>
+#include <vector>
+
+// Include this before open/close/unlink are defined as macros below.
+#include <android-base/errors.h>
+#include <android-base/unique_fd.h>
+#include <android-base/utf8.h>
+
+#include "sysdeps/errno.h"
+#include "sysdeps/stat.h"
/*
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
@@ -58,6 +67,10 @@
#ifdef _WIN32
+// Clang-only nullability specifiers
+#define _Nonnull
+#define _Nullable
+
#include <ctype.h>
#include <direct.h>
#include <dirent.h>
@@ -71,7 +84,8 @@
#include <windows.h>
#include <ws2tcpip.h>
-#include <string> // Prototypes for narrow() and widen() use std::(w)string.
+#include <memory> // unique_ptr
+#include <string>
#include "fdevent.h"
@@ -80,34 +94,81 @@
#define OS_PATH_SEPARATOR_STR "\\"
#define ENV_PATH_SEPARATOR_STR ";"
-typedef CRITICAL_SECTION adb_mutex_t;
-
-#define ADB_MUTEX_DEFINE(x) adb_mutex_t x
-
-/* declare all mutexes */
-/* For win32, adb_sysdeps_init() will do the mutex runtime initialization. */
-#define ADB_MUTEX(x) extern adb_mutex_t x;
-#include "mutex_list.h"
-
-extern void adb_sysdeps_init(void);
-
-static __inline__ void adb_mutex_lock( adb_mutex_t* lock )
-{
- EnterCriticalSection( lock );
+static __inline__ bool adb_is_separator(char c) {
+ return c == '\\' || c == '/';
}
-static __inline__ void adb_mutex_unlock( adb_mutex_t* lock )
-{
- LeaveCriticalSection( lock );
+typedef void (*adb_thread_func_t)(void* arg);
+typedef HANDLE adb_thread_t;
+
+struct adb_winthread_args {
+ adb_thread_func_t func;
+ void* arg;
+};
+
+static unsigned __stdcall adb_winthread_wrapper(void* heap_args) {
+ // Move the arguments from the heap onto the thread's stack.
+ adb_winthread_args thread_args = *static_cast<adb_winthread_args*>(heap_args);
+ delete static_cast<adb_winthread_args*>(heap_args);
+ thread_args.func(thread_args.arg);
+ return 0;
}
-typedef void* (*adb_thread_func_t)(void* arg);
+static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg,
+ adb_thread_t* thread = nullptr) {
+ adb_winthread_args* args = new adb_winthread_args{.func = func, .arg = arg};
+ uintptr_t handle = _beginthreadex(nullptr, 0, adb_winthread_wrapper, args, 0, nullptr);
+ if (handle != static_cast<uintptr_t>(0)) {
+ if (thread) {
+ *thread = reinterpret_cast<HANDLE>(handle);
+ } else {
+ CloseHandle(thread);
+ }
+ return true;
+ }
+ return false;
+}
-typedef void (*win_thread_func_t)(void* arg);
+static __inline__ bool adb_thread_join(adb_thread_t thread) {
+ switch (WaitForSingleObject(thread, INFINITE)) {
+ case WAIT_OBJECT_0:
+ CloseHandle(thread);
+ return true;
-static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg) {
- uintptr_t tid = _beginthread((win_thread_func_t)func, 0, arg);
- return (tid != static_cast<uintptr_t>(-1L));
+ case WAIT_FAILED:
+ fprintf(stderr, "adb_thread_join failed: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ break;
+
+ default:
+ abort();
+ }
+
+ return false;
+}
+
+static __inline__ bool adb_thread_detach(adb_thread_t thread) {
+ CloseHandle(thread);
+ return true;
+}
+
+static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
+ _endthreadex(0);
+}
+
+static __inline__ int adb_thread_setname(const std::string& name) {
+ // TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
+ // the thread name in Windows. Unfortunately, it only works during debugging, but
+ // our build process doesn't generate PDB files needed for debugging.
+ return 0;
+}
+
+static __inline__ adb_thread_t adb_thread_self() {
+ return GetCurrentThread();
+}
+
+static __inline__ bool adb_thread_equal(adb_thread_t lhs, adb_thread_t rhs) {
+ return GetThreadId(lhs) == GetThreadId(rhs);
}
static __inline__ unsigned long adb_thread_id()
@@ -120,10 +181,6 @@
/* nothing really */
}
-#define lstat stat /* no symlinks on Win32 */
-
-#define S_ISLNK(m) 0 /* no symlinks on Win32 */
-
extern int adb_unlink(const char* path);
#undef unlink
#define unlink ___xxx_unlink
@@ -149,8 +206,13 @@
#undef close
#define close ____xxx_close
+// Like unix_read(), but may return EINTR.
+extern int unix_read_interruptible(int fd, void* buf, size_t len);
+
// See the comments for the !defined(_WIN32) version of unix_read().
-extern int unix_read(int fd, void* buf, size_t len);
+static __inline__ int unix_read(int fd, void* buf, size_t len) {
+ return TEMP_FAILURE_RETRY(unix_read_interruptible(fd, buf, len));
+}
#undef read
#define read ___xxx_read
@@ -173,36 +235,30 @@
extern int unix_open(const char* path, int options, ...);
#define open ___xxx_unix_open
-
-/* normally provided by <cutils/misc.h> */
-extern void* load_file(const char* pathname, unsigned* psize);
-
-/* normally provided by "fdevent.h" */
-
-#define FDE_READ 0x0001
-#define FDE_WRITE 0x0002
-#define FDE_ERROR 0x0004
-#define FDE_DONT_CLOSE 0x0080
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg);
-void fdevent_destroy(fdevent *fde);
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
-void fdevent_remove(fdevent *item);
-void fdevent_set(fdevent *fde, unsigned events);
-void fdevent_add(fdevent *fde, unsigned events);
-void fdevent_del(fdevent *fde, unsigned events);
-void fdevent_loop();
-
-static __inline__ void adb_sleep_ms( int mseconds )
-{
- Sleep( mseconds );
-}
+// Checks if |fd| corresponds to a console.
+// Standard Windows isatty() returns 1 for both console FDs and character
+// devices like NUL. unix_isatty() performs some extra checking to only match
+// console FDs.
+// |fd| must be a real file descriptor, meaning STDxx_FILENO or unix_open() FDs
+// will work but adb_open() FDs will not. Additionally the OS handle associated
+// with |fd| must have GENERIC_READ access (which console FDs have by default).
+// Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
+// calling this function is unreliable and should not be used.
+int unix_isatty(int fd);
+#define isatty ___xxx_isatty
int network_loopback_client(int port, int type, std::string* error);
int network_loopback_server(int port, int type, std::string* error);
int network_inaddr_any_server(int port, int type, std::string* error);
+
+inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
+ abort();
+}
+
+inline int network_local_server(const char* name, int namespace_id, int type, std::string* error) {
+ abort();
+}
+
int network_connect(const std::string& host, int port, int type, int timeout,
std::string* error);
@@ -211,53 +267,32 @@
#undef accept
#define accept ___xxx_accept
+int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen);
+#undef getsockname
+#define getsockname(...) ___xxx_getsockname(__VA__ARGS__)
+
+// Returns the local port number of a bound socket, or -1 on failure.
+int adb_socket_get_local_port(int fd);
+
extern int adb_setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen);
#undef setsockopt
#define setsockopt ___xxx_setsockopt
-static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
-{
- int opt = bufsize;
- return adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void*)&opt, sizeof(opt));
-}
-
-static __inline__ void disable_tcp_nagle( int fd )
-{
- int on = 1;
- adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void*)&on, sizeof(on));
-}
-
extern int adb_socketpair( int sv[2] );
+struct adb_pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout);
+#define poll ___xxx_poll
+
static __inline__ int adb_is_absolute_host_path(const char* path) {
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
-// Like strerror(), but for Win32 error codes.
-std::string SystemErrorCodeToString(DWORD error_code);
-
-// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
-// struct stat s;
-// stat(filename, &s);
-// To turn into the following:
-// struct adb_stat s;
-// adb_stat(filename, &s);
-// To get this to work, we need to make 'struct adb_stat' the same as
-// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
-// *current* macro definition of stat, so it may actually be inheriting from
-// struct _stat32i64 (or some other remapping).
-struct adb_stat : public stat {};
-
-static_assert(sizeof(struct adb_stat) == sizeof(struct stat),
- "structures should be the same");
-
-extern int adb_stat(const char* f, struct adb_stat* s);
-
-// stat is already a macro, undefine it so we can redefine it.
-#undef stat
-#define stat adb_stat
-
// UTF-8 versions of POSIX APIs.
extern DIR* adb_opendir(const char* dirname);
extern struct dirent* adb_readdir(DIR* dir);
@@ -268,6 +303,8 @@
extern int adb_vfprintf(FILE *stream, const char *format, va_list ap)
__attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 0)));
+extern int adb_vprintf(const char *format, va_list ap)
+ __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 0)));
extern int adb_fprintf(FILE *stream, const char *format, ...)
__attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3)));
extern int adb_printf(const char *format, ...)
@@ -275,6 +312,8 @@
extern int adb_fputs(const char* buf, FILE* stream);
extern int adb_fputc(int ch, FILE* stream);
+extern int adb_putchar(int ch);
+extern int adb_puts(const char* buf);
extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb,
FILE* stream);
@@ -290,19 +329,31 @@
#define closedir adb_closedir
#define rewinddir rewinddir_utf8_not_yet_implemented
#define telldir telldir_utf8_not_yet_implemented
-#define seekdir seekdir_utf8_not_yet_implemented
+// Some compiler's C++ headers have members named seekdir, so we can't do the
+// macro technique and instead cause a link error if seekdir is called.
+inline void seekdir(DIR*, long) {
+ extern int seekdir_utf8_not_yet_implemented;
+ seekdir_utf8_not_yet_implemented = 1;
+}
#define utime adb_utime
#define chmod adb_chmod
#define vfprintf adb_vfprintf
+#define vprintf adb_vprintf
#define fprintf adb_fprintf
#define printf adb_printf
#define fputs adb_fputs
#define fputc adb_fputc
+// putc may be a macro, so if so, undefine it, so that we can redefine it.
+#undef putc
+#define putc(c, s) adb_fputc(c, s)
+#define putchar adb_putchar
+#define puts adb_puts
#define fwrite adb_fwrite
#define fopen adb_fopen
+#define freopen freopen_utf8_not_yet_implemented
#define getenv adb_getenv
#define putenv putenv_utf8_not_yet_implemented
@@ -311,18 +362,6 @@
#define getcwd adb_getcwd
-// Convert from UTF-8 to UTF-16, typically used to convert char strings into
-// wchar_t strings that can be passed to wchar_t-based OS and C Runtime APIs
-// on Windows.
-extern std::wstring widen(const std::string& utf8);
-extern std::wstring widen(const char* utf8);
-
-// Convert from UTF-16 to UTF-8, typically used to convert strings from OS and
-// C Runtime APIs that return wchar_t, to a format for our char-based data
-// structures.
-extern std::string narrow(const std::wstring& utf16);
-extern std::string narrow(const wchar_t* utf16);
-
// Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
// passed to main().
class NarrowArgs {
@@ -355,16 +394,36 @@
return reinterpret_cast<HANDLE>(static_cast<INT_PTR>(fd));
}
+// Deleter for unique_handle. Adapted from many sources, including:
+// http://stackoverflow.com/questions/14841396/stdunique-ptr-deleters-and-the-win32-api
+// https://visualstudiomagazine.com/articles/2013/09/01/get-a-handle-on-the-windows-api.aspx
+class handle_deleter {
+public:
+ typedef HANDLE pointer;
+
+ void operator()(HANDLE h);
+};
+
+// Like std::unique_ptr, but for Windows HANDLE objects that should be
+// CloseHandle()'d. Operator bool() only checks if the handle != nullptr,
+// but does not check if the handle != INVALID_HANDLE_VALUE.
+typedef std::unique_ptr<HANDLE, handle_deleter> unique_handle;
+
+namespace internal {
+
+size_t ParseCompleteUTF8(const char* first, const char* last, std::vector<char>* remaining_bytes);
+
+}
+
#else /* !_WIN32 a.k.a. Unix */
-#include "fdevent.h"
-#include <cutils/misc.h>
#include <cutils/sockets.h>
#include <cutils/threads.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
#include <pthread.h>
#include <unistd.h>
@@ -383,26 +442,9 @@
#define OS_PATH_SEPARATOR_STR "/"
#define ENV_PATH_SEPARATOR_STR ":"
-typedef pthread_mutex_t adb_mutex_t;
-
-#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
-#define adb_mutex_init pthread_mutex_init
-#define adb_mutex_lock pthread_mutex_lock
-#define adb_mutex_unlock pthread_mutex_unlock
-#define adb_mutex_destroy pthread_mutex_destroy
-
-#define ADB_MUTEX_DEFINE(m) adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER
-
-#define adb_cond_t pthread_cond_t
-#define adb_cond_init pthread_cond_init
-#define adb_cond_wait pthread_cond_wait
-#define adb_cond_broadcast pthread_cond_broadcast
-#define adb_cond_signal pthread_cond_signal
-#define adb_cond_destroy pthread_cond_destroy
-
-/* declare all mutexes */
-#define ADB_MUTEX(x) extern adb_mutex_t x;
-#include "mutex_list.h"
+static __inline__ bool adb_is_separator(char c) {
+ return c == '/';
+}
static __inline__ void close_on_exec(int fd)
{
@@ -465,14 +507,17 @@
{
return shutdown(fd, SHUT_RDWR);
}
+static __inline__ int adb_shutdown(int fd, int direction)
+{
+ return shutdown(fd, direction);
+}
#undef shutdown
#define shutdown ____xxx_shutdown
// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
// not designed to take a file descriptor from unix_open(). See the comments
// for adb_open() for more info.
-static __inline__ int adb_close(int fd)
-{
+__inline__ int adb_close(int fd) {
return close(fd);
}
#undef close
@@ -484,6 +529,11 @@
return TEMP_FAILURE_RETRY( read( fd, buf, len ) );
}
+// Like unix_read(), but does not handle EINTR.
+static __inline__ int unix_read_interruptible(int fd, void* buf, size_t len) {
+ return read(fd, buf, len);
+}
+
#undef read
#define read ___xxx_read
@@ -521,6 +571,11 @@
#undef creat
#define creat ___xxx_creat
+static __inline__ int unix_isatty(int fd) {
+ return isatty(fd);
+}
+#define isatty ___xxx_isatty
+
// Helper for network_* functions.
inline int _fd_set_error_str(int fd, std::string* error) {
if (fd == -1) {
@@ -530,21 +585,26 @@
}
inline int network_loopback_client(int port, int type, std::string* error) {
- return _fd_set_error_str(socket_loopback_client(port, type), error);
+ return _fd_set_error_str(socket_network_client("localhost", port, type), error);
}
inline int network_loopback_server(int port, int type, std::string* error) {
- return _fd_set_error_str(socket_loopback_server(port, type), error);
+ int fd = socket_loopback_server(port, type);
+ if (fd < 0 && errno == EAFNOSUPPORT)
+ return _fd_set_error_str(socket_loopback_server6(port, type), error);
+ return _fd_set_error_str(fd, error);
}
inline int network_inaddr_any_server(int port, int type, std::string* error) {
return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
}
-inline int network_local_server(const char *name, int namespace_id, int type,
- std::string* error) {
- return _fd_set_error_str(socket_local_server(name, namespace_id, type),
- error);
+inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
+ return _fd_set_error_str(socket_local_client(name, namespace_id, type), error);
+}
+
+inline int network_local_server(const char* name, int namespace_id, int type, std::string* error) {
+ return _fd_set_error_str(socket_local_server(name, namespace_id, type), error);
}
inline int network_connect(const std::string& host, int port, int type,
@@ -577,6 +637,10 @@
#undef accept
#define accept ___xxx_accept
+inline int adb_socket_get_local_port(int fd) {
+ return socket_get_local_port(fd);
+}
+
// Operate on a file descriptor returned from unix_open() or a well-known file
// descriptor such as STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
//
@@ -589,28 +653,72 @@
#define unix_write adb_write
#define unix_close adb_close
-typedef void* (*adb_thread_func_t)( void* arg );
+// Win32 is limited to DWORDs for thread return values; limit the POSIX systems to this as well to
+// ensure compatibility.
+typedef void (*adb_thread_func_t)(void* arg);
+typedef pthread_t adb_thread_t;
-static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg) {
+struct adb_pthread_args {
+ adb_thread_func_t func;
+ void* arg;
+};
+
+static void* adb_pthread_wrapper(void* heap_args) {
+ // Move the arguments from the heap onto the thread's stack.
+ adb_pthread_args thread_args = *reinterpret_cast<adb_pthread_args*>(heap_args);
+ delete static_cast<adb_pthread_args*>(heap_args);
+ thread_args.func(thread_args.arg);
+ return nullptr;
+}
+
+static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg,
+ adb_thread_t* thread = nullptr) {
+ pthread_t temp;
pthread_attr_t attr;
pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
- pthread_t thread;
- errno = pthread_create(&thread, &attr, start, arg);
- return (errno == 0);
+ pthread_attr_setdetachstate(&attr, thread ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);
+ auto* pthread_args = new adb_pthread_args{.func = start, .arg = arg};
+ errno = pthread_create(&temp, &attr, adb_pthread_wrapper, pthread_args);
+ if (errno == 0) {
+ if (thread) {
+ *thread = temp;
+ }
+ return true;
+ }
+ return false;
}
-static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
-{
- int opt = bufsize;
- return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+static __inline__ bool adb_thread_join(adb_thread_t thread) {
+ errno = pthread_join(thread, nullptr);
+ return errno == 0;
}
-static __inline__ void disable_tcp_nagle(int fd)
-{
- int on = 1;
- setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
+static __inline__ bool adb_thread_detach(adb_thread_t thread) {
+ errno = pthread_detach(thread);
+ return errno == 0;
+}
+
+static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
+ pthread_exit(nullptr);
+}
+
+static __inline__ int adb_thread_setname(const std::string& name) {
+#ifdef __APPLE__
+ return pthread_setname_np(name.c_str());
+#else
+ const char *s = name.c_str();
+
+ // pthread_setname_np fails rather than truncating long strings.
+ const int max_task_comm_len = 16; // including the null terminator
+ if (name.length() > (max_task_comm_len - 1)) {
+ char buf[max_task_comm_len];
+ strncpy(buf, name.c_str(), sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ s = buf;
+ }
+
+ return pthread_setname_np(pthread_self(), s) ;
+#endif
}
static __inline__ int adb_setsockopt( int fd, int level, int optname, const void* optval, socklen_t optlen )
@@ -642,11 +750,13 @@
#undef socketpair
#define socketpair ___xxx_socketpair
-static __inline__ void adb_sleep_ms( int mseconds )
-{
- usleep( mseconds*1000 );
+typedef struct pollfd adb_pollfd;
+static __inline__ int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
+ return TEMP_FAILURE_RETRY(poll(fds, nfds, timeout));
}
+#define poll ___xxx_poll
+
static __inline__ int adb_mkdir(const std::string& path, int mode)
{
return mkdir(path.c_str(), mode);
@@ -655,10 +765,6 @@
#undef mkdir
#define mkdir ___xxx_mkdir
-static __inline__ void adb_sysdeps_init(void)
-{
-}
-
static __inline__ int adb_is_absolute_host_path(const char* path) {
return path[0] == '/';
}
@@ -670,4 +776,19 @@
#endif /* !_WIN32 */
+static inline void disable_tcp_nagle(int fd) {
+ int off = 1;
+ adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
+}
+
+// Sets TCP socket |fd| to send a keepalive TCP message every |interval_sec| seconds. Set
+// |interval_sec| to 0 to disable keepalives. If keepalives are enabled, the connection will be
+// configured to drop after 10 missed keepalives. Returns true on success.
+bool set_tcp_keepalive(int fd, int interval_sec);
+
+#if defined(_WIN32)
+// Win32 defines ERROR, which we don't need, but which conflicts with google3 logging.
+#undef ERROR
+#endif
+
#endif /* _ADB_SYSDEPS_H */
diff --git a/adb/sysdeps/chrono.h b/adb/sysdeps/chrono.h
new file mode 100644
index 0000000..c73a638
--- /dev/null
+++ b/adb/sysdeps/chrono.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+#if defined(_WIN32)
+// We don't have C++14 on Windows yet.
+// Reimplement std::chrono_literals ourselves until we do.
+
+// Silence the following warning (which gets promoted to an error):
+// error: literal operator suffixes not preceded by ‘_’ are reserved for future standardization
+#pragma GCC system_header
+
+constexpr std::chrono::seconds operator"" s(unsigned long long s) {
+ return std::chrono::seconds(s);
+}
+
+constexpr std::chrono::duration<long double> operator"" s(long double s) {
+ return std::chrono::duration<long double>(s);
+}
+
+constexpr std::chrono::milliseconds operator"" ms(unsigned long long ms) {
+ return std::chrono::milliseconds(ms);
+}
+
+constexpr std::chrono::duration<long double, std::milli> operator"" ms(long double ms) {
+ return std::chrono::duration<long double, std::milli>(ms);
+}
+#else
+using namespace std::chrono_literals;
+#endif
diff --git a/adb/sysdeps/errno.cpp b/adb/sysdeps/errno.cpp
new file mode 100644
index 0000000..6869947
--- /dev/null
+++ b/adb/sysdeps/errno.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps/errno.h"
+
+#include <errno.h>
+
+#include <thread>
+#include <unordered_map>
+#include <utility>
+
+#include "adb.h"
+
+#if defined(_WIN32)
+#define ETXTBSY EBUSY
+#endif
+
+// Use the linux asm-generic values for errno (which are used on all android archs but mips).
+#define ERRNO_VALUES() \
+ ERRNO_VALUE(EACCES, 13); \
+ ERRNO_VALUE(EEXIST, 17); \
+ ERRNO_VALUE(EFAULT, 14); \
+ ERRNO_VALUE(EFBIG, 27); \
+ ERRNO_VALUE(EINTR, 4); \
+ ERRNO_VALUE(EINVAL, 22); \
+ ERRNO_VALUE(EIO, 5); \
+ ERRNO_VALUE(EISDIR, 21); \
+ ERRNO_VALUE(ELOOP, 40); \
+ ERRNO_VALUE(EMFILE, 24); \
+ ERRNO_VALUE(ENAMETOOLONG, 36); \
+ ERRNO_VALUE(ENFILE, 23); \
+ ERRNO_VALUE(ENOENT, 2); \
+ ERRNO_VALUE(ENOMEM, 12); \
+ ERRNO_VALUE(ENOSPC, 28); \
+ ERRNO_VALUE(ENOTDIR, 20); \
+ ERRNO_VALUE(EOVERFLOW, 75); \
+ ERRNO_VALUE(EPERM, 1); \
+ ERRNO_VALUE(EROFS, 30); \
+ ERRNO_VALUE(ETXTBSY, 26)
+
+// Make sure these values are actually correct.
+#if defined(__linux__) && !defined(__mips__)
+#define ERRNO_VALUE(error_name, wire_value) static_assert((error_name) == (wire_value), "")
+ERRNO_VALUES();
+#undef ERRNO_VALUE
+#endif
+
+static std::unordered_map<int, int>* generate_host_to_wire() {
+ auto result = new std::unordered_map<int, int>();
+#define ERRNO_VALUE(error_name, wire_value) \
+ result->insert(std::make_pair((error_name), (wire_value)))
+ ERRNO_VALUES();
+#undef ERRNO_VALUE
+ return result;
+}
+
+static std::unordered_map<int, int>* generate_wire_to_host() {
+ auto result = new std::unordered_map<int, int>();
+#define ERRNO_VALUE(error_name, wire_value) \
+ result->insert(std::make_pair((wire_value), (error_name)))
+ ERRNO_VALUES();
+#undef ERRNO_VALUE
+ return result;
+}
+
+static std::unordered_map<int, int>& host_to_wire = *generate_host_to_wire();
+static std::unordered_map<int, int>& wire_to_host = *generate_wire_to_host();
+
+int errno_to_wire(int error) {
+ auto it = host_to_wire.find(error);
+ if (it == host_to_wire.end()) {
+ LOG(ERROR) << "failed to convert errno " << error << " (" << strerror(error) << ") to wire";
+
+ // Return EIO;
+ return 5;
+ }
+ return it->second;
+}
+
+int errno_from_wire(int error) {
+ auto it = host_to_wire.find(error);
+ if (it == host_to_wire.end()) {
+ LOG(ERROR) << "failed to convert errno " << error << " from wire";
+ return EIO;
+ }
+ return it->second;
+}
diff --git a/adb/sysdeps/errno.h b/adb/sysdeps/errno.h
new file mode 100644
index 0000000..72816b1
--- /dev/null
+++ b/adb/sysdeps/errno.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <string.h>
+
+#if defined(_WIN32)
+char* adb_strerror(int err);
+#define strerror adb_strerror
+#endif
+
+// errno values differ between operating systems and between Linux architectures.
+// Arbitrarily select the Linux asm-generic values to use in the wire protocol.
+int errno_to_wire(int error);
+int errno_from_wire(int error);
diff --git a/adb/sysdeps/stat.h b/adb/sysdeps/stat.h
new file mode 100644
index 0000000..ed2cf25
--- /dev/null
+++ b/adb/sysdeps/stat.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#if defined(_WIN32)
+// stat is broken on Win32: stat on a path with a trailing slash or backslash will always fail with
+// ENOENT.
+int adb_stat(const char* path, struct adb_stat* buf);
+
+// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
+// struct stat s;
+// stat(filename, &s);
+// To turn into the following:
+// struct adb_stat s;
+// adb_stat(filename, &s);
+// To get this to work, we need to make 'struct adb_stat' the same as
+// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
+// *current* macro definition of stat, so it may actually be inheriting from
+// struct _stat32i64 (or some other remapping).
+struct adb_stat : public stat {};
+
+#undef stat
+#define stat adb_stat
+
+// Windows doesn't have lstat.
+#define lstat adb_stat
+
+// mingw doesn't define S_IFLNK or S_ISLNK.
+#define S_IFLNK 0120000
+#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
+
+// mingw defines S_IFBLK to a different value from bionic.
+#undef S_IFBLK
+#define S_IFBLK 0060000
+#undef S_ISBLK
+#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
+#endif
+
+// Make sure that host file mode values match the ones on the device.
+static_assert(S_IFMT == 00170000, "");
+static_assert(S_IFLNK == 0120000, "");
+static_assert(S_IFREG == 0100000, "");
+static_assert(S_IFBLK == 0060000, "");
+static_assert(S_IFDIR == 0040000, "");
+static_assert(S_IFCHR == 0020000, "");
diff --git a/adb/sysdeps/stat_test.cpp b/adb/sysdeps/stat_test.cpp
new file mode 100644
index 0000000..2c2e0ee
--- /dev/null
+++ b/adb/sysdeps/stat_test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "adb_utils.h"
+#include "sysdeps.h"
+
+TEST(sysdeps, stat) {
+ TemporaryDir td;
+ TemporaryFile tf;
+
+ struct stat st;
+ ASSERT_EQ(0, stat(td.path, &st));
+ ASSERT_FALSE(S_ISREG(st.st_mode));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+ ASSERT_EQ(0, stat((std::string(td.path) + '/').c_str(), &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+#if defined(_WIN32)
+ ASSERT_EQ(0, stat((std::string(td.path) + '\\').c_str(), &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+#endif
+
+ std::string nonexistent_path = std::string(td.path) + "/nonexistent";
+ ASSERT_EQ(-1, stat(nonexistent_path.c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+
+ ASSERT_EQ(-1, stat((nonexistent_path + "/").c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+
+#if defined(_WIN32)
+ ASSERT_EQ(-1, stat((nonexistent_path + "\\").c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+#endif
+
+ ASSERT_EQ(0, stat(tf.path, &st));
+ ASSERT_TRUE(S_ISREG(st.st_mode));
+ ASSERT_FALSE(S_ISDIR(st.st_mode));
+
+ ASSERT_EQ(-1, stat((std::string(tf.path) + '/').c_str(), &st));
+ ASSERT_EQ(ENOTDIR, errno);
+
+#if defined(_WIN32)
+ ASSERT_EQ(-1, stat((std::string(tf.path) + '\\').c_str(), &st));
+ ASSERT_EQ(ENOTDIR, errno);
+#endif
+}
diff --git a/adb/sysdeps/win32/errno.cpp b/adb/sysdeps/win32/errno.cpp
new file mode 100644
index 0000000..a3b9d9b
--- /dev/null
+++ b/adb/sysdeps/win32/errno.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps/errno.h"
+
+#include <windows.h>
+
+#include <string>
+
+// Overrides strerror() to handle error codes not supported by the Windows C
+// Runtime (MSVCRT.DLL).
+char* adb_strerror(int err) {
+// sysdeps.h defines strerror to adb_strerror, but in this function, we
+// want to call the real C Runtime strerror().
+#pragma push_macro("strerror")
+#undef strerror
+ const int saved_err = errno; // Save because we overwrite it later.
+
+ // Lookup the string for an unknown error.
+ char* errmsg = strerror(-1);
+ const std::string unknown_error = (errmsg == nullptr) ? "" : errmsg;
+
+ // Lookup the string for this error to see if the C Runtime has it.
+ errmsg = strerror(err);
+ if (errmsg != nullptr && unknown_error != errmsg) {
+ // The CRT returned an error message and it is different than the error
+ // message for an unknown error, so it is probably valid, so use it.
+ } else {
+ // Check if we have a string for this error code.
+ const char* custom_msg = nullptr;
+ switch (err) {
+#pragma push_macro("ERR")
+#undef ERR
+#define ERR(errnum, desc) case errnum: custom_msg = desc; break
+ // These error strings are from AOSP bionic/libc/include/sys/_errdefs.h.
+ // Note that these cannot be longer than 94 characters because we
+ // pass this to _strerror() which has that requirement.
+ ERR(ECONNRESET, "Connection reset by peer");
+ ERR(EHOSTUNREACH, "No route to host");
+ ERR(ENETDOWN, "Network is down");
+ ERR(ENETRESET, "Network dropped connection because of reset");
+ ERR(ENOBUFS, "No buffer space available");
+ ERR(ENOPROTOOPT, "Protocol not available");
+ ERR(ENOTCONN, "Transport endpoint is not connected");
+ ERR(ENOTSOCK, "Socket operation on non-socket");
+ ERR(EOPNOTSUPP, "Operation not supported on transport endpoint");
+#pragma pop_macro("ERR")
+ }
+
+ if (custom_msg != nullptr) {
+ // Use _strerror() to write our string into the writable per-thread
+ // buffer used by strerror()/_strerror(). _strerror() appends the
+ // msg for the current value of errno, so set errno to a consistent
+ // value for every call so that our code-path is always the same.
+ errno = 0;
+ errmsg = _strerror(custom_msg);
+ const size_t custom_msg_len = strlen(custom_msg);
+ // Just in case _strerror() returned a read-only string, check if
+ // the returned string starts with our custom message because that
+ // implies that the string is not read-only.
+ if ((errmsg != nullptr) && !strncmp(custom_msg, errmsg, custom_msg_len)) {
+ // _strerror() puts other text after our custom message, so
+ // remove that by terminating after our message.
+ errmsg[custom_msg_len] = '\0';
+ } else {
+ // For some reason nullptr was returned or a pointer to a
+ // read-only string was returned, so fallback to whatever
+ // strerror() can muster (probably "Unknown error" or some
+ // generic CRT error string).
+ errmsg = strerror(err);
+ }
+ } else {
+ // We don't have a custom message, so use whatever strerror(err)
+ // returned earlier.
+ }
+ }
+
+ errno = saved_err; // restore
+
+ return errmsg;
+#pragma pop_macro("strerror")
+}
diff --git a/adb/sysdeps/win32/errno_test.cpp b/adb/sysdeps/win32/errno_test.cpp
new file mode 100644
index 0000000..09ec52c
--- /dev/null
+++ b/adb/sysdeps/win32/errno_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps/errno.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+void TestAdbStrError(int err, const char* expected) {
+ errno = 12345;
+ const char* result = adb_strerror(err);
+ // Check that errno is not overwritten.
+ EXPECT_EQ(12345, errno);
+ EXPECT_STREQ(expected, result);
+}
+
+TEST(sysdeps_win32, adb_strerror) {
+ // Test an error code that should not have a mapped string. Use an error
+ // code that is not used by the internal implementation of adb_strerror().
+ TestAdbStrError(-2, "Unknown error");
+ // adb_strerror() uses -1 internally, so test that it can still be passed
+ // as a parameter.
+ TestAdbStrError(-1, "Unknown error");
+ // Test very big, positive unknown error.
+ TestAdbStrError(1000000, "Unknown error");
+
+ // Test success case.
+ // Wine returns "Success" for strerror(0), Windows returns "No error", so accept both.
+ std::string success = adb_strerror(0);
+ EXPECT_TRUE(success == "Success" || success == "No error") << "strerror(0) = " << success;
+
+ // Test error that regular strerror() should have a string for.
+ TestAdbStrError(EPERM, "Operation not permitted");
+ // Test error that regular strerror() doesn't have a string for, but that
+ // adb_strerror() returns.
+ TestAdbStrError(ECONNRESET, "Connection reset by peer");
+}
diff --git a/adb/sysdeps/win32/stat.cpp b/adb/sysdeps/win32/stat.cpp
new file mode 100644
index 0000000..844c1ce
--- /dev/null
+++ b/adb/sysdeps/win32/stat.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps/stat.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/utf8.h>
+
+// Version of stat() that takes a UTF-8 path.
+int adb_stat(const char* path, struct adb_stat* s) {
+// This definition of wstat seems to be missing from <sys/stat.h>.
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#ifdef _USE_32BIT_TIME_T
+#define wstat _wstat32i64
+#else
+#define wstat _wstat64
+#endif
+#else
+// <sys/stat.h> has a function prototype for wstat() that should be available.
+#endif
+
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ // If the path has a trailing slash, stat will fail with ENOENT regardless of whether the path
+ // is a directory or not.
+ bool expected_directory = false;
+ while (*path_wide.rbegin() == u'/' || *path_wide.rbegin() == u'\\') {
+ path_wide.pop_back();
+ expected_directory = true;
+ }
+
+ struct adb_stat st;
+ int result = wstat(path_wide.c_str(), &st);
+ if (result == 0 && expected_directory) {
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+
+ memcpy(s, &st, sizeof(st));
+ return result;
+}
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
new file mode 100644
index 0000000..9007e75
--- /dev/null
+++ b/adb/sysdeps_test.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <thread>
+
+#include "adb_io.h"
+#include "sysdeps.h"
+#include "sysdeps/chrono.h"
+
+static void increment_atomic_int(void* c) {
+ std::this_thread::sleep_for(1s);
+ reinterpret_cast<std::atomic<int>*>(c)->fetch_add(1);
+}
+
+TEST(sysdeps_thread, smoke) {
+ std::atomic<int> counter(0);
+
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter));
+ }
+
+ std::this_thread::sleep_for(2s);
+ ASSERT_EQ(100, counter.load());
+}
+
+TEST(sysdeps_thread, join) {
+ std::atomic<int> counter(0);
+ std::vector<adb_thread_t> threads(500);
+ for (size_t i = 0; i < threads.size(); ++i) {
+ ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter, &threads[i]));
+ }
+
+ int current = counter.load();
+ ASSERT_GE(current, 0);
+ // Make sure that adb_thread_create actually creates threads, and doesn't do something silly
+ // like synchronously run the function passed in. The sleep in increment_atomic_int should be
+ // enough to keep this from being flakey.
+ ASSERT_LT(current, 500);
+
+ for (const auto& thread : threads) {
+ ASSERT_TRUE(adb_thread_join(thread));
+ }
+
+ ASSERT_EQ(500, counter.load());
+}
+
+TEST(sysdeps_thread, exit) {
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(
+ [](void*) {
+ adb_thread_exit();
+ for (;;) continue;
+ },
+ nullptr, &thread));
+ ASSERT_TRUE(adb_thread_join(thread));
+}
+
+TEST(sysdeps_socketpair, smoke) {
+ int fds[2];
+ ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
+ ASSERT_TRUE(WriteFdExactly(fds[0], "foo", 4));
+ ASSERT_TRUE(WriteFdExactly(fds[1], "bar", 4));
+
+ char buf[4];
+ ASSERT_TRUE(ReadFdExactly(fds[1], buf, 4));
+ ASSERT_STREQ(buf, "foo");
+ ASSERT_TRUE(ReadFdExactly(fds[0], buf, 4));
+ ASSERT_STREQ(buf, "bar");
+ ASSERT_EQ(0, adb_close(fds[0]));
+ ASSERT_EQ(0, adb_close(fds[1]));
+}
+
+TEST(sysdeps_fd, exhaustion) {
+ std::vector<int> fds;
+ int socketpair[2];
+
+ while (adb_socketpair(socketpair) == 0) {
+ fds.push_back(socketpair[0]);
+ fds.push_back(socketpair[1]);
+ }
+
+ ASSERT_EQ(EMFILE, errno) << strerror(errno);
+ for (int fd : fds) {
+ ASSERT_EQ(0, adb_close(fd));
+ }
+ ASSERT_EQ(0, adb_socketpair(socketpair));
+ ASSERT_EQ(socketpair[0], fds[0]);
+ ASSERT_EQ(socketpair[1], fds[1]);
+ ASSERT_EQ(0, adb_close(socketpair[0]));
+ ASSERT_EQ(0, adb_close(socketpair[1]));
+}
+
+class sysdeps_poll : public ::testing::Test {
+ protected:
+ int fds[2];
+ void SetUp() override {
+ ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
+ }
+
+ void TearDown() override {
+ if (fds[0] >= 0) {
+ ASSERT_EQ(0, adb_close(fds[0]));
+ }
+ if (fds[1] >= 0) {
+ ASSERT_EQ(0, adb_close(fds[1]));
+ }
+ }
+};
+
+TEST_F(sysdeps_poll, smoke) {
+ adb_pollfd pfd[2] = {};
+ pfd[0].fd = fds[0];
+ pfd[0].events = POLLRDNORM;
+ pfd[1].fd = fds[1];
+ pfd[1].events = POLLWRNORM;
+
+ pfd[0].revents = -1;
+ pfd[1].revents = -1;
+ EXPECT_EQ(1, adb_poll(pfd, 2, 0));
+ EXPECT_EQ(0, pfd[0].revents);
+ EXPECT_EQ(POLLWRNORM, pfd[1].revents);
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ // Wait for the socketpair to be flushed.
+ pfd[0].revents = -1;
+ EXPECT_EQ(1, adb_poll(pfd, 1, 100));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ pfd[0].revents = -1;
+ pfd[1].revents = -1;
+ EXPECT_EQ(2, adb_poll(pfd, 2, 0));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ EXPECT_EQ(POLLWRNORM, pfd[1].revents);
+}
+
+TEST_F(sysdeps_poll, timeout) {
+ adb_pollfd pfd = {};
+ pfd.fd = fds[0];
+ pfd.events = POLLRDNORM;
+
+ EXPECT_EQ(0, adb_poll(&pfd, 1, 100));
+ EXPECT_EQ(0, pfd.revents);
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
+ EXPECT_EQ(POLLRDNORM, pfd.revents);
+}
+
+TEST_F(sysdeps_poll, invalid_fd) {
+ adb_pollfd pfd[3] = {};
+ pfd[0].fd = fds[0];
+ pfd[0].events = POLLRDNORM;
+ pfd[1].fd = INT_MAX;
+ pfd[1].events = POLLRDNORM;
+ pfd[2].fd = fds[1];
+ pfd[2].events = POLLWRNORM;
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ // Wait for the socketpair to be flushed.
+ EXPECT_EQ(1, adb_poll(pfd, 1, 100));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+
+ EXPECT_EQ(3, adb_poll(pfd, 3, 0));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ EXPECT_EQ(POLLNVAL, pfd[1].revents);
+ EXPECT_EQ(POLLWRNORM, pfd[2].revents);
+}
+
+TEST_F(sysdeps_poll, duplicate_fd) {
+ adb_pollfd pfd[2] = {};
+ pfd[0].fd = fds[0];
+ pfd[0].events = POLLRDNORM;
+ pfd[1] = pfd[0];
+
+ EXPECT_EQ(0, adb_poll(pfd, 2, 0));
+ EXPECT_EQ(0, pfd[0].revents);
+ EXPECT_EQ(0, pfd[1].revents);
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ EXPECT_EQ(2, adb_poll(pfd, 2, 100));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ EXPECT_EQ(POLLRDNORM, pfd[1].revents);
+}
+
+TEST_F(sysdeps_poll, disconnect) {
+ adb_pollfd pfd = {};
+ pfd.fd = fds[0];
+ pfd.events = POLLIN;
+
+ EXPECT_EQ(0, adb_poll(&pfd, 1, 0));
+ EXPECT_EQ(0, pfd.revents);
+
+ EXPECT_EQ(0, adb_close(fds[1]));
+ fds[1] = -1;
+
+ EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
+
+ // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
+ EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+}
+
+TEST_F(sysdeps_poll, fd_count) {
+ // https://code.google.com/p/android/issues/detail?id=12141
+ static constexpr int num_sockets = 256;
+ std::vector<int> sockets;
+ std::vector<adb_pollfd> pfds;
+ sockets.resize(num_sockets * 2);
+ for (int32_t i = 0; i < num_sockets; ++i) {
+ ASSERT_EQ(0, adb_socketpair(&sockets[i * 2])) << strerror(errno);
+ ASSERT_TRUE(WriteFdExactly(sockets[i * 2], &i, sizeof(i)));
+ adb_pollfd pfd;
+ pfd.events = POLLIN;
+ pfd.fd = sockets[i * 2 + 1];
+ pfds.push_back(pfd);
+ }
+
+ ASSERT_EQ(num_sockets, adb_poll(pfds.data(), pfds.size(), 0));
+ for (int i = 0; i < num_sockets; ++i) {
+ ASSERT_NE(0, pfds[i].revents & POLLIN);
+
+ int32_t buf[2] = { -1, -1 };
+ ASSERT_EQ(adb_read(pfds[i].fd, buf, sizeof(buf)), static_cast<ssize_t>(sizeof(int32_t)));
+ ASSERT_EQ(i, buf[0]);
+ }
+
+ for (int fd : sockets) {
+ adb_close(fd);
+ }
+}
+
+TEST(sysdeps_mutex, mutex_smoke) {
+ static std::atomic<bool> finished(false);
+ static std::mutex &m = *new std::mutex();
+ m.lock();
+ ASSERT_FALSE(m.try_lock());
+ adb_thread_create([](void*) {
+ ASSERT_FALSE(m.try_lock());
+ m.lock();
+ finished.store(true);
+ std::this_thread::sleep_for(200ms);
+ m.unlock();
+ }, nullptr);
+
+ ASSERT_FALSE(finished.load());
+ std::this_thread::sleep_for(100ms);
+ ASSERT_FALSE(finished.load());
+ m.unlock();
+ std::this_thread::sleep_for(100ms);
+ m.lock();
+ ASSERT_TRUE(finished.load());
+ m.unlock();
+}
+
+TEST(sysdeps_mutex, recursive_mutex_smoke) {
+ static std::recursive_mutex &m = *new std::recursive_mutex();
+
+ m.lock();
+ ASSERT_TRUE(m.try_lock());
+ m.unlock();
+
+ adb_thread_create([](void*) {
+ ASSERT_FALSE(m.try_lock());
+ m.lock();
+ std::this_thread::sleep_for(500ms);
+ m.unlock();
+ }, nullptr);
+
+ std::this_thread::sleep_for(100ms);
+ m.unlock();
+ std::this_thread::sleep_for(100ms);
+ ASSERT_FALSE(m.try_lock());
+ m.lock();
+ m.unlock();
+}
+
+TEST(sysdeps_condition_variable, smoke) {
+ static std::mutex &m = *new std::mutex;
+ static std::condition_variable &cond = *new std::condition_variable;
+ static volatile bool flag = false;
+
+ std::unique_lock<std::mutex> lock(m);
+ adb_thread_create([](void*) {
+ m.lock();
+ flag = true;
+ cond.notify_one();
+ m.unlock();
+ }, nullptr);
+
+ while (!flag) {
+ cond.wait(lock);
+ }
+}
diff --git a/adb/sysdeps_unix.cpp b/adb/sysdeps_unix.cpp
new file mode 100644
index 0000000..4445a44
--- /dev/null
+++ b/adb/sysdeps_unix.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps.h"
+
+bool set_tcp_keepalive(int fd, int interval_sec) {
+ int enable = (interval_sec > 0);
+ if (adb_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable))) {
+ return false;
+ }
+
+ if (!enable) {
+ return true;
+ }
+
+ // Idle time before sending the first keepalive is TCP_KEEPIDLE on Linux, TCP_KEEPALIVE on Mac.
+#if defined(TCP_KEEPIDLE)
+ if (adb_setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &interval_sec, sizeof(interval_sec))) {
+ return false;
+ }
+#elif defined(TCP_KEEPALIVE)
+ if (adb_setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &interval_sec, sizeof(interval_sec))) {
+ return false;
+ }
+#endif
+
+ // TCP_KEEPINTVL and TCP_KEEPCNT are available on Linux 2.4+ and OS X 10.8+ (Mountain Lion).
+#if defined(TCP_KEEPINTVL)
+ if (adb_setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval_sec, sizeof(interval_sec))) {
+ return false;
+ }
+#endif
+
+#if defined(TCP_KEEPCNT)
+ // On Windows this value is hardcoded to 10. This is a reasonable value, so we do the same here
+ // to match behavior. See SO_KEEPALIVE documentation at
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ee470551(v=vs.85).aspx.
+ const int keepcnt = 10;
+ if (adb_setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt))) {
+ return false;
+ }
+#endif
+
+ return true;
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index f534d61..a4b5e69 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_SYSDEPS
+#define TRACE_TAG SYSDEPS
#include "sysdeps.h"
@@ -25,17 +25,23 @@
#include <stdio.h>
#include <stdlib.h>
+#include <algorithm>
#include <memory>
+#include <mutex>
#include <string>
#include <unordered_map>
+#include <vector>
#include <cutils/sockets.h>
-#include <base/logging.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/errors.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/utf8.h>
#include "adb.h"
+#include "adb_utils.h"
extern void fatal(const char *fmt, ...);
@@ -51,7 +57,6 @@
int (*_fh_lseek)(FH, int, int);
int (*_fh_read)(FH, void*, int);
int (*_fh_write)(FH, const void*, int);
- void (*_fh_hook)(FH, int, EventHook);
} FHClassRec;
static void _fh_file_init(FH);
@@ -59,7 +64,6 @@
static int _fh_file_lseek(FH, int, int);
static int _fh_file_read(FH, void*, int);
static int _fh_file_write(FH, const void*, int);
-static void _fh_file_hook(FH, int, EventHook);
static const FHClassRec _fh_file_class = {
_fh_file_init,
@@ -67,7 +71,6 @@
_fh_file_lseek,
_fh_file_read,
_fh_file_write,
- _fh_file_hook
};
static void _fh_socket_init(FH);
@@ -75,7 +78,6 @@
static int _fh_socket_lseek(FH, int, int);
static int _fh_socket_read(FH, void*, int);
static int _fh_socket_write(FH, const void*, int);
-static void _fh_socket_hook(FH, int, EventHook);
static const FHClassRec _fh_socket_class = {
_fh_socket_init,
@@ -83,85 +85,27 @@
_fh_socket_lseek,
_fh_socket_read,
_fh_socket_write,
- _fh_socket_hook
};
-#define assert(cond) do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
+#define assert(cond) \
+ do { \
+ if (!(cond)) fatal("assertion failed '%s' on %s:%d\n", #cond, __FILE__, __LINE__); \
+ } while (0)
-std::string SystemErrorCodeToString(const DWORD error_code) {
- const int kErrorMessageBufferSize = 256;
- WCHAR msgbuf[kErrorMessageBufferSize];
- DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
- DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
- arraysize(msgbuf), nullptr);
- if (len == 0) {
- return android::base::StringPrintf(
- "Error (%lu) while retrieving error. (%lu)", GetLastError(),
- error_code);
- }
-
- // Convert UTF-16 to UTF-8.
- std::string msg(narrow(msgbuf));
- // Messages returned by the system end with line breaks.
- msg = android::base::Trim(msg);
- // There are many Windows error messages compared to POSIX, so include the
- // numeric error code for easier, quicker, accurate identification. Use
- // decimal instead of hex because there are decimal ranges like 10000-11999
- // for Winsock.
- android::base::StringAppendF(&msg, " (%lu)", error_code);
- return msg;
-}
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** replaces libs/cutils/load_file.c *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-void *load_file(const char *fn, unsigned *_sz)
-{
- HANDLE file;
- char *data;
- DWORD file_size;
-
- file = CreateFileW( widen(fn).c_str(),
- GENERIC_READ,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL );
-
- if (file == INVALID_HANDLE_VALUE)
- return NULL;
-
- file_size = GetFileSize( file, NULL );
- data = NULL;
-
- if (file_size > 0) {
- data = (char*) malloc( file_size + 1 );
- if (data == NULL) {
- D("load_file: could not allocate %ld bytes\n", file_size );
- file_size = 0;
- } else {
- DWORD out_bytes;
-
- if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
- out_bytes != file_size )
- {
- D("load_file: could not read %ld bytes from '%s'\n", file_size, fn);
- free(data);
- data = NULL;
- file_size = 0;
- }
+void handle_deleter::operator()(HANDLE h) {
+ // CreateFile() is documented to return INVALID_HANDLE_FILE on error,
+ // implying that NULL is a valid handle, but this is probably impossible.
+ // Other APIs like CreateEvent() are documented to return NULL on error,
+ // implying that INVALID_HANDLE_VALUE is a valid handle, but this is also
+ // probably impossible. Thus, consider both NULL and INVALID_HANDLE_VALUE
+ // as invalid handles. std::unique_ptr won't call a deleter with NULL, so we
+ // only need to check for INVALID_HANDLE_VALUE.
+ if (h != INVALID_HANDLE_VALUE) {
+ if (!CloseHandle(h)) {
+ D("CloseHandle(%p) failed: %s", h,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
}
}
- CloseHandle( file );
-
- *_sz = (unsigned) file_size;
- return data;
}
/**************************************************************************/
@@ -172,9 +116,6 @@
/**************************************************************************/
/**************************************************************************/
-/* used to emulate unix-domain socket pairs */
-typedef struct SocketPairRec_* SocketPair;
-
typedef struct FHRec_
{
FHClass clazz;
@@ -183,10 +124,8 @@
union {
HANDLE handle;
SOCKET socket;
- SocketPair pair;
} u;
- HANDLE event;
int mask;
char name[32];
@@ -195,13 +134,11 @@
#define fh_handle u.handle
#define fh_socket u.socket
-#define fh_pair u.pair
-#define WIN32_FH_BASE 100
+#define WIN32_FH_BASE 2048
+#define WIN32_MAX_FHS 2048
-#define WIN32_MAX_FHS 128
-
-static adb_mutex_t _win32_lock;
+static std::mutex& _win32_lock = *new std::mutex();
static FHRec _win32_fhs[ WIN32_MAX_FHS ];
static int _win32_fh_next; // where to start search for free FHRec
@@ -213,7 +150,7 @@
fd -= WIN32_FH_BASE;
if (fd < 0 || fd >= WIN32_MAX_FHS) {
- D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
+ D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
func );
errno = EBADF;
return NULL;
@@ -222,7 +159,7 @@
f = &_win32_fhs[fd];
if (f->used == 0) {
- D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
+ D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
func );
errno = EBADF;
return NULL;
@@ -246,34 +183,24 @@
{
FH f = NULL;
- adb_mutex_lock( &_win32_lock );
+ std::lock_guard<std::mutex> lock(_win32_lock);
- // Search entire array, starting from _win32_fh_next.
- for (int nn = 0; nn < WIN32_MAX_FHS; nn++) {
- // Keep incrementing _win32_fh_next to avoid giving out an index that
- // was recently closed, to try to avoid use-after-free.
- const int index = _win32_fh_next++;
- // Handle wrap-around of _win32_fh_next.
- if (_win32_fh_next == WIN32_MAX_FHS) {
- _win32_fh_next = 0;
- }
- if (_win32_fhs[index].clazz == NULL) {
- f = &_win32_fhs[index];
- goto Exit;
+ for (int i = _win32_fh_next; i < WIN32_MAX_FHS; ++i) {
+ if (_win32_fhs[i].clazz == NULL) {
+ f = &_win32_fhs[i];
+ _win32_fh_next = i + 1;
+ f->clazz = clazz;
+ f->used = 1;
+ f->eof = 0;
+ f->name[0] = '\0';
+ clazz->_fh_init(f);
+ return f;
}
}
- D( "_fh_alloc: no more free file descriptors\n" );
- errno = EMFILE; // Too many open files
-Exit:
- if (f) {
- f->clazz = clazz;
- f->used = 1;
- f->eof = 0;
- f->name[0] = '\0';
- clazz->_fh_init(f);
- }
- adb_mutex_unlock( &_win32_lock );
- return f;
+
+ D("_fh_alloc: no more free file descriptors");
+ errno = EMFILE; // Too many open files
+ return nullptr;
}
@@ -282,7 +209,13 @@
{
// Use lock so that closing only happens once and so that _fh_alloc can't
// allocate a FH that we're in the middle of closing.
- adb_mutex_lock(&_win32_lock);
+ std::lock_guard<std::mutex> lock(_win32_lock);
+
+ int offset = f - _win32_fhs;
+ if (_win32_fh_next > offset) {
+ _win32_fh_next = offset;
+ }
+
if (f->used) {
f->clazz->_fh_close( f );
f->name[0] = '\0';
@@ -290,7 +223,6 @@
f->used = 0;
f->clazz = NULL;
}
- adb_mutex_unlock(&_win32_lock);
return 0;
}
@@ -333,7 +265,7 @@
DWORD read_bytes;
if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) {
- D( "adb_read: could not read %d bytes from %s\n", len, f->name );
+ D( "adb_read: could not read %d bytes from %s", len, f->name );
errno = EIO;
return -1;
} else if (read_bytes < (DWORD)len) {
@@ -346,7 +278,7 @@
DWORD wrote_bytes;
if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) {
- D( "adb_file_write: could not write %d bytes from %s\n", len, f->name );
+ D( "adb_file_write: could not write %d bytes from %s", len, f->name );
errno = EIO;
return -1;
} else if (wrote_bytes < (DWORD)len) {
@@ -406,7 +338,7 @@
desiredAccess = GENERIC_READ | GENERIC_WRITE;
break;
default:
- D("adb_open: invalid options (0x%0x)\n", options);
+ D("adb_open: invalid options (0x%0x)", options);
errno = EINVAL;
return -1;
}
@@ -416,7 +348,11 @@
return -1;
}
- f->fh_handle = CreateFileW( widen(path).c_str(), desiredAccess, shareMode,
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+ f->fh_handle = CreateFileW( path_wide.c_str(), desiredAccess, shareMode,
NULL, OPEN_EXISTING, 0, NULL );
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
@@ -425,25 +361,24 @@
D( "adb_open: could not open '%s': ", path );
switch (err) {
case ERROR_FILE_NOT_FOUND:
- D( "file not found\n" );
+ D( "file not found" );
errno = ENOENT;
return -1;
case ERROR_PATH_NOT_FOUND:
- D( "path not found\n" );
+ D( "path not found" );
errno = ENOTDIR;
return -1;
default:
- D( "unknown error: %s\n",
- SystemErrorCodeToString( err ).c_str() );
+ D("unknown error: %s", android::base::SystemErrorCodeToString(err).c_str());
errno = ENOENT;
return -1;
}
}
snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
- D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) );
+ D( "adb_open: '%s' => fd %d", path, _fh_to_int(f) );
return _fh_to_int(f);
}
@@ -457,7 +392,11 @@
return -1;
}
- f->fh_handle = CreateFileW( widen(path).c_str(), GENERIC_WRITE,
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+ f->fh_handle = CreateFileW( path_wide.c_str(), GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL );
@@ -468,24 +407,23 @@
D( "adb_creat: could not open '%s': ", path );
switch (err) {
case ERROR_FILE_NOT_FOUND:
- D( "file not found\n" );
+ D( "file not found" );
errno = ENOENT;
return -1;
case ERROR_PATH_NOT_FOUND:
- D( "path not found\n" );
+ D( "path not found" );
errno = ENOTDIR;
return -1;
default:
- D( "unknown error: %s\n",
- SystemErrorCodeToString( err ).c_str() );
+ D("unknown error: %s", android::base::SystemErrorCodeToString(err).c_str());
errno = ENOENT;
return -1;
}
}
snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
- D( "adb_creat: '%s' => fd %d\n", path, _fh_to_int(f) );
+ D( "adb_creat: '%s' => fd %d", path, _fh_to_int(f) );
return _fh_to_int(f);
}
@@ -534,7 +472,7 @@
return -1;
}
- D( "adb_close: %s\n", f->name);
+ D( "adb_close: %s", f->name);
_fh_close(f);
return 0;
}
@@ -550,38 +488,95 @@
#undef setsockopt
static void _socket_set_errno( const DWORD err ) {
- // The Windows C Runtime (MSVCRT.DLL) strerror() does not support a lot of
- // POSIX and socket error codes, so this can only meaningfully map so much.
+ // Because the Windows C Runtime (MSVCRT.DLL) strerror() does not support a
+ // lot of POSIX and socket error codes, some of the resulting error codes
+ // are mapped to strings by adb_strerror().
switch ( err ) {
case 0: errno = 0; break;
+ // Don't map WSAEINTR since that is only for Winsock 1.1 which we don't use.
+ // case WSAEINTR: errno = EINTR; break;
+ case WSAEFAULT: errno = EFAULT; break;
+ case WSAEINVAL: errno = EINVAL; break;
+ case WSAEMFILE: errno = EMFILE; break;
// Mapping WSAEWOULDBLOCK to EAGAIN is absolutely critical because
// non-blocking sockets can cause an error code of WSAEWOULDBLOCK and
// callers check specifically for EAGAIN.
case WSAEWOULDBLOCK: errno = EAGAIN; break;
- case WSAEINTR: errno = EINTR; break;
- case WSAEFAULT: errno = EFAULT; break;
- case WSAEINVAL: errno = EINVAL; break;
- case WSAEMFILE: errno = EMFILE; break;
+ case WSAENOTSOCK: errno = ENOTSOCK; break;
+ case WSAENOPROTOOPT: errno = ENOPROTOOPT; break;
+ case WSAEOPNOTSUPP: errno = EOPNOTSUPP; break;
+ case WSAENETDOWN: errno = ENETDOWN; break;
+ case WSAENETRESET: errno = ENETRESET; break;
+ // Map WSAECONNABORTED to EPIPE instead of ECONNABORTED because POSIX seems
+ // to use EPIPE for these situations and there are some callers that look
+ // for EPIPE.
+ case WSAECONNABORTED: errno = EPIPE; break;
+ case WSAECONNRESET: errno = ECONNRESET; break;
+ case WSAENOBUFS: errno = ENOBUFS; break;
+ case WSAENOTCONN: errno = ENOTCONN; break;
+ // Don't map WSAETIMEDOUT because we don't currently use SO_RCVTIMEO or
+ // SO_SNDTIMEO which would cause WSAETIMEDOUT to be returned. Future
+ // considerations: Reportedly send() can return zero on timeout, and POSIX
+ // code may expect EAGAIN instead of ETIMEDOUT on timeout.
+ // case WSAETIMEDOUT: errno = ETIMEDOUT; break;
+ case WSAEHOSTUNREACH: errno = EHOSTUNREACH; break;
default:
errno = EINVAL;
- D( "_socket_set_errno: mapping Windows error code %lu to errno %d\n",
+ D( "_socket_set_errno: mapping Windows error code %lu to errno %d",
err, errno );
}
}
-static void _fh_socket_init( FH f ) {
- f->fh_socket = INVALID_SOCKET;
- f->event = WSACreateEvent();
- if (f->event == WSA_INVALID_EVENT) {
- D("WSACreateEvent failed: %s\n",
- SystemErrorCodeToString(WSAGetLastError()).c_str());
-
- // _event_socket_start assumes that this field is INVALID_HANDLE_VALUE
- // on failure, instead of NULL which is what Windows really returns on
- // error. It might be better to change all the other code to look for
- // NULL, but that is a much riskier change.
- f->event = INVALID_HANDLE_VALUE;
+extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
+ // WSAPoll doesn't handle invalid/non-socket handles, so we need to handle them ourselves.
+ int skipped = 0;
+ std::vector<WSAPOLLFD> sockets;
+ std::vector<adb_pollfd*> original;
+ for (size_t i = 0; i < nfds; ++i) {
+ FH fh = _fh_from_int(fds[i].fd, __func__);
+ if (!fh || !fh->used || fh->clazz != &_fh_socket_class) {
+ D("adb_poll received bad FD %d", fds[i].fd);
+ fds[i].revents = POLLNVAL;
+ ++skipped;
+ } else {
+ WSAPOLLFD wsapollfd = {
+ .fd = fh->u.socket,
+ .events = static_cast<short>(fds[i].events)
+ };
+ sockets.push_back(wsapollfd);
+ original.push_back(&fds[i]);
+ }
}
+
+ if (sockets.empty()) {
+ return skipped;
+ }
+
+ int result = WSAPoll(sockets.data(), sockets.size(), timeout);
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno(WSAGetLastError());
+ return -1;
+ }
+
+ // Map the results back onto the original set.
+ for (size_t i = 0; i < sockets.size(); ++i) {
+ original[i]->revents = sockets[i].revents;
+ }
+
+ // WSAPoll appears to return the number of unique FDs with avaiable events, instead of how many
+ // of the pollfd elements have a non-zero revents field, which is what it and poll are specified
+ // to do. Ignore its result and calculate the proper return value.
+ result = 0;
+ for (size_t i = 0; i < nfds; ++i) {
+ if (fds[i].revents != 0) {
+ ++result;
+ }
+ }
+ return result;
+}
+
+static void _fh_socket_init(FH f) {
+ f->fh_socket = INVALID_SOCKET;
f->mask = 0;
}
@@ -592,23 +587,17 @@
// If the socket is not connected, this returns an error. We want to
// minimize logging spam, so don't log these errors for now.
#if 0
- D("socket shutdown failed: %s\n",
- SystemErrorCodeToString(WSAGetLastError()).c_str());
+ D("socket shutdown failed: %s",
+ android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
#endif
}
if (closesocket(f->fh_socket) == SOCKET_ERROR) {
- D("closesocket failed: %s\n",
- SystemErrorCodeToString(WSAGetLastError()).c_str());
+ // Don't set errno here, since adb_close will ignore it.
+ const DWORD err = WSAGetLastError();
+ D("closesocket failed: %s", android::base::SystemErrorCodeToString(err).c_str());
}
f->fh_socket = INVALID_SOCKET;
}
- if (f->event != NULL) {
- if (!CloseHandle(f->event)) {
- D("CloseHandle failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
- f->event = NULL;
- }
f->mask = 0;
return 0;
}
@@ -625,8 +614,8 @@
// WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
// that to reduce spam and confusion.
if (err != WSAEWOULDBLOCK) {
- D("recv fd %d failed: %s\n", _fh_to_int(f),
- SystemErrorCodeToString(err).c_str());
+ D("recv fd %d failed: %s", _fh_to_int(f),
+ android::base::SystemErrorCodeToString(err).c_str());
}
_socket_set_errno(err);
result = -1;
@@ -638,10 +627,20 @@
int result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
if (result == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
- D("send fd %d failed: %s\n", _fh_to_int(f),
- SystemErrorCodeToString(err).c_str());
+ // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
+ // that to reduce spam and confusion.
+ if (err != WSAEWOULDBLOCK) {
+ D("send fd %d failed: %s", _fh_to_int(f),
+ android::base::SystemErrorCodeToString(err).c_str());
+ }
_socket_set_errno(err);
result = -1;
+ } else {
+ // According to https://code.google.com/p/chromium/issues/detail?id=27870
+ // Winsock Layered Service Providers may cause this.
+ CHECK_LE(result, len) << "Tried to write " << len << " bytes to "
+ << f->name << ", but " << result
+ << " bytes reportedly written";
}
return result;
}
@@ -667,8 +666,8 @@
WSADATA wsaData;
int rc = WSAStartup( MAKEWORD(2,2), &wsaData);
if (rc != 0) {
- fatal( "adb: could not initialize Winsock: %s",
- SystemErrorCodeToString( rc ).c_str());
+ fatal("adb: could not initialize Winsock: %s",
+ android::base::SystemErrorCodeToString(rc).c_str());
}
_winsock_init = 1;
@@ -688,49 +687,67 @@
}
}
+// Map a socket type to an explicit socket protocol instead of using the socket
+// protocol of 0. Explicit socket protocols are used by most apps and we should
+// do the same to reduce the chance of exercising uncommon code-paths that might
+// have problems or that might load different Winsock service providers that
+// have problems.
+static int GetSocketProtocolFromSocketType(int type) {
+ switch (type) {
+ case SOCK_STREAM:
+ return IPPROTO_TCP;
+ case SOCK_DGRAM:
+ return IPPROTO_UDP;
+ default:
+ LOG(FATAL) << "Unknown socket type: " << type;
+ return 0;
+ }
+}
+
int network_loopback_client(int port, int type, std::string* error) {
struct sockaddr_in addr;
- SOCKET s;
+ SOCKET s;
- unique_fh f(_fh_alloc(&_fh_socket_class));
+ unique_fh f(_fh_alloc(&_fh_socket_class));
if (!f) {
*error = strerror(errno);
return -1;
}
- if (!_winsock_init)
- _init_winsock();
+ if (!_winsock_init) _init_winsock();
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- s = socket(AF_INET, type, 0);
- if(s == INVALID_SOCKET) {
+ s = socket(AF_INET, type, GetSocketProtocolFromSocketType(type));
+ if (s == INVALID_SOCKET) {
+ const DWORD err = WSAGetLastError();
*error = android::base::StringPrintf("cannot create socket: %s",
- SystemErrorCodeToString(WSAGetLastError()).c_str());
- D("%s\n", error->c_str());
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
f->fh_socket = s;
- if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+ if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
// Save err just in case inet_ntoa() or ntohs() changes the last error.
const DWORD err = WSAGetLastError();
*error = android::base::StringPrintf("cannot connect to %s:%u: %s",
- inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
- SystemErrorCodeToString(err).c_str());
- D("could not connect to %s:%d: %s\n",
- type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("could not connect to %s:%d: %s", type != SOCK_STREAM ? "udp" : "tcp", port,
+ error->c_str());
+ _socket_set_errno(err);
return -1;
}
const int fd = _fh_to_int(f.get());
- snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", fd,
- type != SOCK_STREAM ? "udp:" : "", port );
- D( "port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp",
- fd );
+ snprintf(f->name, sizeof(f->name), "%d(lo-client:%s%d)", fd, type != SOCK_STREAM ? "udp:" : "",
+ port);
+ D("port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp", fd);
f.release();
return fd;
}
@@ -738,20 +755,18 @@
#define LISTEN_BACKLOG 4
// interface_address is INADDR_LOOPBACK or INADDR_ANY.
-static int _network_server(int port, int type, u_long interface_address,
- std::string* error) {
+static int _network_server(int port, int type, u_long interface_address, std::string* error) {
struct sockaddr_in addr;
- SOCKET s;
- int n;
+ SOCKET s;
+ int n;
- unique_fh f(_fh_alloc(&_fh_socket_class));
+ unique_fh f(_fh_alloc(&_fh_socket_class));
if (!f) {
*error = strerror(errno);
return -1;
}
- if (!_winsock_init)
- _init_winsock();
+ if (!_winsock_init) _init_winsock();
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
@@ -760,11 +775,13 @@
// TODO: Consider using dual-stack socket that can simultaneously listen on
// IPv4 and IPv6.
- s = socket(AF_INET, type, 0);
+ s = socket(AF_INET, type, GetSocketProtocolFromSocketType(type));
if (s == INVALID_SOCKET) {
+ const DWORD err = WSAGetLastError();
*error = android::base::StringPrintf("cannot create socket: %s",
- SystemErrorCodeToString(WSAGetLastError()).c_str());
- D("%s\n", error->c_str());
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
@@ -773,40 +790,41 @@
// Note: SO_REUSEADDR on Windows allows multiple processes to bind to the
// same port, so instead use SO_EXCLUSIVEADDRUSE.
n = 1;
- if (setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n,
- sizeof(n)) == SOCKET_ERROR) {
- *error = android::base::StringPrintf(
- "cannot set socket option SO_EXCLUSIVEADDRUSE: %s",
- SystemErrorCodeToString(WSAGetLastError()).c_str());
- D("%s\n", error->c_str());
+ if (setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n)) == SOCKET_ERROR) {
+ const DWORD err = WSAGetLastError();
+ *error = android::base::StringPrintf("cannot set socket option SO_EXCLUSIVEADDRUSE: %s",
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
- if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+ if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
// Save err just in case inet_ntoa() or ntohs() changes the last error.
const DWORD err = WSAGetLastError();
- *error = android::base::StringPrintf("cannot bind to %s:%u: %s",
- inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
- SystemErrorCodeToString(err).c_str());
- D("could not bind to %s:%d: %s\n",
- type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+ *error = android::base::StringPrintf("cannot bind to %s:%u: %s", inet_ntoa(addr.sin_addr),
+ ntohs(addr.sin_port),
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("could not bind to %s:%d: %s", type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+ _socket_set_errno(err);
return -1;
}
if (type == SOCK_STREAM) {
if (listen(s, LISTEN_BACKLOG) == SOCKET_ERROR) {
- *error = android::base::StringPrintf("cannot listen on socket: %s",
- SystemErrorCodeToString(WSAGetLastError()).c_str());
- D("could not listen on %s:%d: %s\n",
- type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+ const DWORD err = WSAGetLastError();
+ *error = android::base::StringPrintf(
+ "cannot listen on socket: %s", android::base::SystemErrorCodeToString(err).c_str());
+ D("could not listen on %s:%d: %s", type != SOCK_STREAM ? "udp" : "tcp", port,
+ error->c_str());
+ _socket_set_errno(err);
return -1;
}
}
const int fd = _fh_to_int(f.get());
- snprintf( f->name, sizeof(f->name), "%d(%s-server:%s%d)", fd,
- interface_address == INADDR_LOOPBACK ? "lo" : "any",
- type != SOCK_STREAM ? "udp:" : "", port );
- D( "port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp",
- fd );
+ snprintf(f->name, sizeof(f->name), "%d(%s-server:%s%d)", fd,
+ interface_address == INADDR_LOOPBACK ? "lo" : "any", type != SOCK_STREAM ? "udp:" : "",
+ port);
+ D("port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp", fd);
f.release();
return fd;
}
@@ -832,6 +850,7 @@
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = type;
+ hints.ai_protocol = GetSocketProtocolFromSocketType(type);
char port_str[16];
snprintf(port_str, sizeof(port_str), "%d", port);
@@ -839,54 +858,57 @@
struct addrinfo* addrinfo_ptr = nullptr;
#if (NTDDI_VERSION >= NTDDI_WINXPSP2) || (_WIN32_WINNT >= _WIN32_WINNT_WS03)
- // TODO: When the Android SDK tools increases the Windows system
- // requirements >= WinXP SP2, switch to GetAddrInfoW(widen(host).c_str()).
+// TODO: When the Android SDK tools increases the Windows system
+// requirements >= WinXP SP2, switch to android::base::UTF8ToWide() + GetAddrInfoW().
#else
- // Otherwise, keep using getaddrinfo(), or do runtime API detection
- // with GetProcAddress("GetAddrInfoW").
+// Otherwise, keep using getaddrinfo(), or do runtime API detection
+// with GetProcAddress("GetAddrInfoW").
#endif
if (getaddrinfo(host.c_str(), port_str, &hints, &addrinfo_ptr) != 0) {
- *error = android::base::StringPrintf(
- "cannot resolve host '%s' and port %s: %s", host.c_str(),
- port_str, SystemErrorCodeToString(WSAGetLastError()).c_str());
- D("%s\n", error->c_str());
+ const DWORD err = WSAGetLastError();
+ *error = android::base::StringPrintf("cannot resolve host '%s' and port %s: %s",
+ host.c_str(), port_str,
+ android::base::SystemErrorCodeToString(err).c_str());
+
+ D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
- std::unique_ptr<struct addrinfo, decltype(freeaddrinfo)*>
- addrinfo(addrinfo_ptr, freeaddrinfo);
+ std::unique_ptr<struct addrinfo, decltype(&freeaddrinfo)> addrinfo(addrinfo_ptr, freeaddrinfo);
addrinfo_ptr = nullptr;
// TODO: Try all the addresses if there's more than one? This just uses
// the first. Or, could call WSAConnectByName() (Windows Vista and newer)
// which tries all addresses, takes a timeout and more.
- SOCKET s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
- addrinfo->ai_protocol);
- if(s == INVALID_SOCKET) {
+ SOCKET s = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
+ if (s == INVALID_SOCKET) {
+ const DWORD err = WSAGetLastError();
*error = android::base::StringPrintf("cannot create socket: %s",
- SystemErrorCodeToString(WSAGetLastError()).c_str());
- D("%s\n", error->c_str());
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
f->fh_socket = s;
// TODO: Implement timeouts for Windows. Seems like the default in theory
// (according to http://serverfault.com/a/671453) and in practice is 21 sec.
- if(connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == SOCKET_ERROR) {
+ if (connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == SOCKET_ERROR) {
// TODO: Use WSAAddressToString or inet_ntop on address.
- *error = android::base::StringPrintf("cannot connect to %s:%s: %s",
- host.c_str(), port_str,
- SystemErrorCodeToString(WSAGetLastError()).c_str());
- D("could not connect to %s:%s:%s: %s\n",
- type != SOCK_STREAM ? "udp" : "tcp", host.c_str(), port_str,
- error->c_str());
+ const DWORD err = WSAGetLastError();
+ *error = android::base::StringPrintf("cannot connect to %s:%s: %s", host.c_str(), port_str,
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("could not connect to %s:%s:%s: %s", type != SOCK_STREAM ? "udp" : "tcp", host.c_str(),
+ port_str, error->c_str());
+ _socket_set_errno(err);
return -1;
}
const int fd = _fh_to_int(f.get());
- snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", fd,
- type != SOCK_STREAM ? "udp:" : "", port );
- D( "host '%s' port %d type %s => fd %d\n", host.c_str(), port,
- type != SOCK_STREAM ? "udp" : "tcp", fd );
+ snprintf(f->name, sizeof(f->name), "%d(net-client:%s%d)", fd, type != SOCK_STREAM ? "udp:" : "",
+ port);
+ D("host '%s' port %d type %s => fd %d", host.c_str(), port, type != SOCK_STREAM ? "udp" : "tcp",
+ fd);
f.release();
return fd;
}
@@ -897,7 +919,7 @@
FH serverfh = _fh_from_int(serverfd, __func__);
if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
- D("adb_socket_accept: invalid fd %d\n", serverfd);
+ D("adb_socket_accept: invalid fd %d", serverfd);
errno = EBADF;
return -1;
}
@@ -913,14 +935,14 @@
if (fh->fh_socket == INVALID_SOCKET) {
const DWORD err = WSAGetLastError();
LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd <<
- " failed: " + SystemErrorCodeToString(err);
+ " failed: " + android::base::SystemErrorCodeToString(err);
_socket_set_errno( err );
return -1;
}
const int fd = _fh_to_int(fh.get());
snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", fd, serverfh->name );
- D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, fd );
+ D( "adb_socket_accept on fd %d returns fd %d", serverfd, fd );
fh.release();
return fd;
}
@@ -931,1392 +953,207 @@
FH fh = _fh_from_int(fd, __func__);
if ( !fh || fh->clazz != &_fh_socket_class ) {
- D("adb_setsockopt: invalid fd %d\n", fd);
+ D("adb_setsockopt: invalid fd %d", fd);
errno = EBADF;
return -1;
}
+
+ // TODO: Once we can assume Windows Vista or later, if the caller is trying
+ // to set SOL_SOCKET, SO_SNDBUF/SO_RCVBUF, ignore it since the OS has
+ // auto-tuning.
+
int result = setsockopt( fh->fh_socket, level, optname,
reinterpret_cast<const char*>(optval), optlen );
if ( result == SOCKET_ERROR ) {
const DWORD err = WSAGetLastError();
- D( "adb_setsockopt: setsockopt on fd %d level %d optname %d "
- "failed: %s\n", fd, level, optname,
- SystemErrorCodeToString(err).c_str() );
+ D("adb_setsockopt: setsockopt on fd %d level %d optname %d failed: %s\n",
+ fd, level, optname, android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno( err );
result = -1;
}
return result;
}
+int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen) {
+ FH fh = _fh_from_int(fd, __func__);
+
+ if (!fh || fh->clazz != &_fh_socket_class) {
+ D("adb_getsockname: invalid fd %d", fd);
+ errno = EBADF;
+ return -1;
+ }
+
+ int result = (getsockname)(fh->fh_socket, sockaddr, optlen);
+ if (result == SOCKET_ERROR) {
+ const DWORD err = WSAGetLastError();
+ D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
+ android::base::SystemErrorCodeToString(err).c_str());
+ _socket_set_errno(err);
+ result = -1;
+ }
+ return result;
+}
+
+int adb_socket_get_local_port(int fd) {
+ sockaddr_storage addr_storage;
+ socklen_t addr_len = sizeof(addr_storage);
+
+ if (adb_getsockname(fd, reinterpret_cast<sockaddr*>(&addr_storage), &addr_len) < 0) {
+ D("adb_socket_get_local_port: adb_getsockname failed: %s", strerror(errno));
+ return -1;
+ }
+
+ if (!(addr_storage.ss_family == AF_INET || addr_storage.ss_family == AF_INET6)) {
+ D("adb_socket_get_local_port: unknown address family received: %d", addr_storage.ss_family);
+ errno = ECONNABORTED;
+ return -1;
+ }
+
+ return ntohs(reinterpret_cast<sockaddr_in*>(&addr_storage)->sin_port);
+}
int adb_shutdown(int fd)
{
FH f = _fh_from_int(fd, __func__);
if (!f || f->clazz != &_fh_socket_class) {
- D("adb_shutdown: invalid fd %d\n", fd);
+ D("adb_shutdown: invalid fd %d", fd);
errno = EBADF;
return -1;
}
- D( "adb_shutdown: %s\n", f->name);
+ D( "adb_shutdown: %s", f->name);
if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
- D("socket shutdown fd %d failed: %s\n", fd,
- SystemErrorCodeToString(err).c_str());
+ D("socket shutdown fd %d failed: %s", fd,
+ android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno(err);
return -1;
}
return 0;
}
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** emulated socketpairs *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
+// Emulate socketpair(2) by binding and connecting to a socket.
+int adb_socketpair(int sv[2]) {
+ int server = -1;
+ int client = -1;
+ int accepted = -1;
+ int local_port = -1;
+ std::string error;
-/* we implement socketpairs directly in use space for the following reasons:
- * - it avoids copying data from/to the Nt kernel
- * - it allows us to implement fdevent hooks easily and cheaply, something
- * that is not possible with standard Win32 pipes !!
- *
- * basically, we use two circular buffers, each one corresponding to a given
- * direction.
- *
- * each buffer is implemented as two regions:
- *
- * region A which is (a_start,a_end)
- * region B which is (0, b_end) with b_end <= a_start
- *
- * an empty buffer has: a_start = a_end = b_end = 0
- *
- * a_start is the pointer where we start reading data
- * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE,
- * then you start writing at b_end
- *
- * the buffer is full when b_end == a_start && a_end == BUFFER_SIZE
- *
- * there is room when b_end < a_start || a_end < BUFER_SIZE
- *
- * when reading, a_start is incremented, it a_start meets a_end, then
- * we do: a_start = 0, a_end = b_end, b_end = 0, and keep going on..
- */
+ struct sockaddr_storage peer_addr = {};
+ struct sockaddr_storage client_addr = {};
+ socklen_t peer_socklen = sizeof(peer_addr);
+ socklen_t client_socklen = sizeof(client_addr);
-#define BIP_BUFFER_SIZE 4096
-
-#if 0
-#include <stdio.h>
-# define BIPD(x) D x
-# define BIPDUMP bip_dump_hex
-
-static void bip_dump_hex( const unsigned char* ptr, size_t len )
-{
- int nn, len2 = len;
-
- if (len2 > 8) len2 = 8;
-
- for (nn = 0; nn < len2; nn++)
- printf("%02x", ptr[nn]);
- printf(" ");
-
- for (nn = 0; nn < len2; nn++) {
- int c = ptr[nn];
- if (c < 32 || c > 127)
- c = '.';
- printf("%c", c);
- }
- printf("\n");
- fflush(stdout);
-}
-
-#else
-# define BIPD(x) do {} while (0)
-# define BIPDUMP(p,l) BIPD(p)
-#endif
-
-typedef struct BipBufferRec_
-{
- int a_start;
- int a_end;
- int b_end;
- int fdin;
- int fdout;
- int closed;
- int can_write; /* boolean */
- HANDLE evt_write; /* event signaled when one can write to a buffer */
- int can_read; /* boolean */
- HANDLE evt_read; /* event signaled when one can read from a buffer */
- CRITICAL_SECTION lock;
- unsigned char buff[ BIP_BUFFER_SIZE ];
-
-} BipBufferRec, *BipBuffer;
-
-static void
-bip_buffer_init( BipBuffer buffer )
-{
- D( "bit_buffer_init %p\n", buffer );
- buffer->a_start = 0;
- buffer->a_end = 0;
- buffer->b_end = 0;
- buffer->can_write = 1;
- buffer->can_read = 0;
- buffer->fdin = 0;
- buffer->fdout = 0;
- buffer->closed = 0;
- buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL );
- buffer->evt_read = CreateEvent( NULL, TRUE, FALSE, NULL );
- InitializeCriticalSection( &buffer->lock );
-}
-
-static void
-bip_buffer_close( BipBuffer bip )
-{
- bip->closed = 1;
-
- if (!bip->can_read) {
- SetEvent( bip->evt_read );
- }
- if (!bip->can_write) {
- SetEvent( bip->evt_write );
- }
-}
-
-static void
-bip_buffer_done( BipBuffer bip )
-{
- BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout ));
- CloseHandle( bip->evt_read );
- CloseHandle( bip->evt_write );
- DeleteCriticalSection( &bip->lock );
-}
-
-static int
-bip_buffer_write( BipBuffer bip, const void* src, int len )
-{
- int avail, count = 0;
-
- if (len <= 0)
- return 0;
-
- BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
- BIPDUMP( src, len );
-
- EnterCriticalSection( &bip->lock );
-
- while (!bip->can_write) {
- int ret;
- LeaveCriticalSection( &bip->lock );
-
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
- /* spinlocking here is probably unfair, but let's live with it */
- ret = WaitForSingleObject( bip->evt_write, INFINITE );
- if (ret != WAIT_OBJECT_0) { /* buffer probably closed */
- D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError() );
- return 0;
- }
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
- EnterCriticalSection( &bip->lock );
+ server = network_loopback_server(0, SOCK_STREAM, &error);
+ if (server < 0) {
+ D("adb_socketpair: failed to create server: %s", error.c_str());
+ goto fail;
}
- BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+ local_port = adb_socket_get_local_port(server);
+ if (local_port < 0) {
+ D("adb_socketpair: failed to get server port number: %s", error.c_str());
+ goto fail;
+ }
+ D("adb_socketpair: bound on port %d", local_port);
- avail = BIP_BUFFER_SIZE - bip->a_end;
- if (avail > 0)
- {
- /* we can append to region A */
- if (avail > len)
- avail = len;
-
- memcpy( bip->buff + bip->a_end, src, avail );
- src = (const char *)src + avail;
- count += avail;
- len -= avail;
-
- bip->a_end += avail;
- if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) {
- bip->can_write = 0;
- ResetEvent( bip->evt_write );
- goto Exit;
- }
+ client = network_loopback_client(local_port, SOCK_STREAM, &error);
+ if (client < 0) {
+ D("adb_socketpair: failed to connect client: %s", error.c_str());
+ goto fail;
}
- if (len == 0)
- goto Exit;
-
- avail = bip->a_start - bip->b_end;
- assert( avail > 0 ); /* since can_write is TRUE */
-
- if (avail > len)
- avail = len;
-
- memcpy( bip->buff + bip->b_end, src, avail );
- count += avail;
- bip->b_end += avail;
-
- if (bip->b_end == bip->a_start) {
- bip->can_write = 0;
- ResetEvent( bip->evt_write );
+ // Make sure that the peer that connected to us and the client are the same.
+ accepted = adb_socket_accept(server, reinterpret_cast<sockaddr*>(&peer_addr), &peer_socklen);
+ if (accepted < 0) {
+ D("adb_socketpair: failed to accept: %s", strerror(errno));
+ goto fail;
}
-Exit:
- assert( count > 0 );
-
- if ( !bip->can_read ) {
- bip->can_read = 1;
- SetEvent( bip->evt_read );
+ if (adb_getsockname(client, reinterpret_cast<sockaddr*>(&client_addr), &client_socklen) != 0) {
+ D("adb_socketpair: failed to getpeername: %s", strerror(errno));
+ goto fail;
}
- BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
- bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
- LeaveCriticalSection( &bip->lock );
-
- return count;
- }
-
-static int
-bip_buffer_read( BipBuffer bip, void* dst, int len )
-{
- int avail, count = 0;
-
- if (len <= 0)
- return 0;
-
- BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
-
- EnterCriticalSection( &bip->lock );
- while ( !bip->can_read )
- {
-#if 0
- LeaveCriticalSection( &bip->lock );
- errno = EAGAIN;
- return -1;
-#else
- int ret;
- LeaveCriticalSection( &bip->lock );
-
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
-
- ret = WaitForSingleObject( bip->evt_read, INFINITE );
- if (ret != WAIT_OBJECT_0) { /* probably closed buffer */
- D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError());
- return 0;
- }
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
- EnterCriticalSection( &bip->lock );
-#endif
+ if (peer_socklen != client_socklen) {
+ D("adb_socketpair: client and peer sockaddrs have different lengths");
+ errno = EIO;
+ goto fail;
}
- BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
-
- avail = bip->a_end - bip->a_start;
- assert( avail > 0 ); /* since can_read is TRUE */
-
- if (avail > len)
- avail = len;
-
- memcpy( dst, bip->buff + bip->a_start, avail );
- dst = (char *)dst + avail;
- count += avail;
- len -= avail;
-
- bip->a_start += avail;
- if (bip->a_start < bip->a_end)
- goto Exit;
-
- bip->a_start = 0;
- bip->a_end = bip->b_end;
- bip->b_end = 0;
-
- avail = bip->a_end;
- if (avail > 0) {
- if (avail > len)
- avail = len;
- memcpy( dst, bip->buff, avail );
- count += avail;
- bip->a_start += avail;
-
- if ( bip->a_start < bip->a_end )
- goto Exit;
-
- bip->a_start = bip->a_end = 0;
+ if (memcmp(&peer_addr, &client_addr, peer_socklen) != 0) {
+ D("adb_socketpair: client and peer sockaddrs don't match");
+ errno = EIO;
+ goto fail;
}
- bip->can_read = 0;
- ResetEvent( bip->evt_read );
+ adb_close(server);
-Exit:
- assert( count > 0 );
-
- if (!bip->can_write ) {
- bip->can_write = 1;
- SetEvent( bip->evt_write );
- }
-
- BIPDUMP( (const unsigned char*)dst - count, count );
- BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
- bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
- LeaveCriticalSection( &bip->lock );
-
- return count;
-}
-
-typedef struct SocketPairRec_
-{
- BipBufferRec a2b_bip;
- BipBufferRec b2a_bip;
- FH a_fd;
- int used;
-
-} SocketPairRec;
-
-void _fh_socketpair_init( FH f )
-{
- f->fh_pair = NULL;
-}
-
-static int
-_fh_socketpair_close( FH f )
-{
- if ( f->fh_pair ) {
- SocketPair pair = f->fh_pair;
-
- if ( f == pair->a_fd ) {
- pair->a_fd = NULL;
- }
-
- bip_buffer_close( &pair->b2a_bip );
- bip_buffer_close( &pair->a2b_bip );
-
- if ( --pair->used == 0 ) {
- bip_buffer_done( &pair->b2a_bip );
- bip_buffer_done( &pair->a2b_bip );
- free( pair );
- }
- f->fh_pair = NULL;
- }
+ sv[0] = client;
+ sv[1] = accepted;
return 0;
-}
-static int
-_fh_socketpair_lseek( FH f, int pos, int origin )
-{
- errno = ESPIPE;
+fail:
+ if (server >= 0) {
+ adb_close(server);
+ }
+ if (client >= 0) {
+ adb_close(client);
+ }
+ if (accepted >= 0) {
+ adb_close(accepted);
+ }
return -1;
}
-static int
-_fh_socketpair_read( FH f, void* buf, int len )
-{
- SocketPair pair = f->fh_pair;
- BipBuffer bip;
+bool set_file_block_mode(int fd, bool block) {
+ FH fh = _fh_from_int(fd, __func__);
- if (!pair)
- return -1;
-
- if ( f == pair->a_fd )
- bip = &pair->b2a_bip;
- else
- bip = &pair->a2b_bip;
-
- return bip_buffer_read( bip, buf, len );
-}
-
-static int
-_fh_socketpair_write( FH f, const void* buf, int len )
-{
- SocketPair pair = f->fh_pair;
- BipBuffer bip;
-
- if (!pair)
- return -1;
-
- if ( f == pair->a_fd )
- bip = &pair->a2b_bip;
- else
- bip = &pair->b2a_bip;
-
- return bip_buffer_write( bip, buf, len );
-}
-
-
-static void _fh_socketpair_hook( FH f, int event, EventHook hook ); /* forward */
-
-static const FHClassRec _fh_socketpair_class =
-{
- _fh_socketpair_init,
- _fh_socketpair_close,
- _fh_socketpair_lseek,
- _fh_socketpair_read,
- _fh_socketpair_write,
- _fh_socketpair_hook
-};
-
-
-int adb_socketpair(int sv[2]) {
- SocketPair pair;
-
- unique_fh fa(_fh_alloc(&_fh_socketpair_class));
- if (!fa) {
- return -1;
- }
- unique_fh fb(_fh_alloc(&_fh_socketpair_class));
- if (!fb) {
- return -1;
+ if (!fh || !fh->used) {
+ errno = EBADF;
+ return false;
}
- pair = reinterpret_cast<SocketPair>(malloc(sizeof(*pair)));
- if (pair == NULL) {
- D("adb_socketpair: not enough memory to allocate pipes\n" );
- return -1;
- }
-
- bip_buffer_init( &pair->a2b_bip );
- bip_buffer_init( &pair->b2a_bip );
-
- fa->fh_pair = pair;
- fb->fh_pair = pair;
- pair->used = 2;
- pair->a_fd = fa.get();
-
- sv[0] = _fh_to_int(fa.get());
- sv[1] = _fh_to_int(fb.get());
-
- pair->a2b_bip.fdin = sv[0];
- pair->a2b_bip.fdout = sv[1];
- pair->b2a_bip.fdin = sv[1];
- pair->b2a_bip.fdout = sv[0];
-
- snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
- snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
- D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
- fa.release();
- fb.release();
- return 0;
-}
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** fdevents emulation *****/
-/***** *****/
-/***** this is a very simple implementation, we rely on the fact *****/
-/***** that ADB doesn't use FDE_ERROR. *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-#define FATAL(x...) fatal(__FUNCTION__, x)
-
-#if DEBUG
-static void dump_fde(fdevent *fde, const char *info)
-{
- fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
- fde->state & FDE_READ ? 'R' : ' ',
- fde->state & FDE_WRITE ? 'W' : ' ',
- fde->state & FDE_ERROR ? 'E' : ' ',
- info);
-}
-#else
-#define dump_fde(fde, info) do { } while(0)
-#endif
-
-#define FDE_EVENTMASK 0x00ff
-#define FDE_STATEMASK 0xff00
-
-#define FDE_ACTIVE 0x0100
-#define FDE_PENDING 0x0200
-#define FDE_CREATED 0x0400
-
-static void fdevent_plist_enqueue(fdevent *node);
-static void fdevent_plist_remove(fdevent *node);
-static fdevent *fdevent_plist_dequeue(void);
-
-static fdevent list_pending = {
- .next = &list_pending,
- .prev = &list_pending,
-};
-
-static fdevent **fd_table = 0;
-static int fd_table_max = 0;
-
-typedef struct EventLooperRec_* EventLooper;
-
-typedef struct EventHookRec_
-{
- EventHook next;
- FH fh;
- HANDLE h;
- int wanted; /* wanted event flags */
- int ready; /* ready event flags */
- void* aux;
- void (*prepare)( EventHook hook );
- int (*start) ( EventHook hook );
- void (*stop) ( EventHook hook );
- int (*check) ( EventHook hook );
- int (*peek) ( EventHook hook );
-} EventHookRec;
-
-static EventHook _free_hooks;
-
-static EventHook
-event_hook_alloc(FH fh) {
- EventHook hook = _free_hooks;
- if (hook != NULL) {
- _free_hooks = hook->next;
+ if (fh->clazz == &_fh_socket_class) {
+ u_long x = !block;
+ if (ioctlsocket(fh->u.socket, FIONBIO, &x) != 0) {
+ _socket_set_errno(WSAGetLastError());
+ return false;
+ }
+ return true;
} else {
- hook = reinterpret_cast<EventHook>(malloc(sizeof(*hook)));
- if (hook == NULL)
- fatal( "could not allocate event hook\n" );
- }
- hook->next = NULL;
- hook->fh = fh;
- hook->wanted = 0;
- hook->ready = 0;
- hook->h = INVALID_HANDLE_VALUE;
- hook->aux = NULL;
-
- hook->prepare = NULL;
- hook->start = NULL;
- hook->stop = NULL;
- hook->check = NULL;
- hook->peek = NULL;
-
- return hook;
-}
-
-static void
-event_hook_free( EventHook hook )
-{
- hook->fh = NULL;
- hook->wanted = 0;
- hook->ready = 0;
- hook->next = _free_hooks;
- _free_hooks = hook;
-}
-
-
-static void
-event_hook_signal( EventHook hook )
-{
- FH f = hook->fh;
- int fd = _fh_to_int(f);
- fdevent* fde = fd_table[ fd - WIN32_FH_BASE ];
-
- if (fde != NULL && fde->fd == fd) {
- if ((fde->state & FDE_PENDING) == 0) {
- fde->state |= FDE_PENDING;
- fdevent_plist_enqueue( fde );
- }
- fde->events |= hook->wanted;
+ errno = ENOTSOCK;
+ return false;
}
}
+bool set_tcp_keepalive(int fd, int interval_sec) {
+ FH fh = _fh_from_int(fd, __func__);
-#define MAX_LOOPER_HANDLES WIN32_MAX_FHS
-
-typedef struct EventLooperRec_
-{
- EventHook hooks;
- HANDLE htab[ MAX_LOOPER_HANDLES ];
- int htab_count;
-
-} EventLooperRec;
-
-static EventHook*
-event_looper_find_p( EventLooper looper, FH fh )
-{
- EventHook *pnode = &looper->hooks;
- EventHook node = *pnode;
- for (;;) {
- if ( node == NULL || node->fh == fh )
- break;
- pnode = &node->next;
- node = *pnode;
- }
- return pnode;
-}
-
-static void
-event_looper_hook( EventLooper looper, int fd, int events )
-{
- FH f = _fh_from_int(fd, __func__);
- EventHook *pnode;
- EventHook node;
-
- if (f == NULL) /* invalid arg */ {
- D("event_looper_hook: invalid fd=%d\n", fd);
- return;
+ if (!fh || fh->clazz != &_fh_socket_class) {
+ D("set_tcp_keepalive(%d) failed: invalid fd", fd);
+ errno = EBADF;
+ return false;
}
- pnode = event_looper_find_p( looper, f );
- node = *pnode;
- if ( node == NULL ) {
- node = event_hook_alloc( f );
- node->next = *pnode;
- *pnode = node;
+ tcp_keepalive keepalive;
+ keepalive.onoff = (interval_sec > 0);
+ keepalive.keepalivetime = interval_sec * 1000;
+ keepalive.keepaliveinterval = interval_sec * 1000;
+
+ DWORD bytes_returned = 0;
+ if (WSAIoctl(fh->fh_socket, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), nullptr, 0,
+ &bytes_returned, nullptr, nullptr) != 0) {
+ const DWORD err = WSAGetLastError();
+ D("set_tcp_keepalive(%d) failed: %s", fd,
+ android::base::SystemErrorCodeToString(err).c_str());
+ _socket_set_errno(err);
+ return false;
}
- if ( (node->wanted & events) != events ) {
- /* this should update start/stop/check/peek */
- D("event_looper_hook: call hook for %d (new=%x, old=%x)\n",
- fd, node->wanted, events);
- f->clazz->_fh_hook( f, events & ~node->wanted, node );
- node->wanted |= events;
- } else {
- D("event_looper_hook: ignoring events %x for %d wanted=%x)\n",
- events, fd, node->wanted);
- }
-}
-
-static void
-event_looper_unhook( EventLooper looper, int fd, int events )
-{
- FH fh = _fh_from_int(fd, __func__);
- EventHook *pnode = event_looper_find_p( looper, fh );
- EventHook node = *pnode;
-
- if (node != NULL) {
- int events2 = events & node->wanted;
- if ( events2 == 0 ) {
- D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd );
- return;
- }
- node->wanted &= ~events2;
- if (!node->wanted) {
- *pnode = node->next;
- event_hook_free( node );
- }
- }
-}
-
-/*
- * A fixer for WaitForMultipleObjects on condition that there are more than 64
- * handles to wait on.
- *
- * In cetain cases DDMS may establish more than 64 connections with ADB. For
- * instance, this may happen if there are more than 64 processes running on a
- * device, or there are multiple devices connected (including the emulator) with
- * the combined number of running processes greater than 64. In this case using
- * WaitForMultipleObjects to wait on connection events simply wouldn't cut,
- * because of the API limitations (64 handles max). So, we need to provide a way
- * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The
- * easiest (and "Microsoft recommended") way to do that would be dividing the
- * handle array into chunks with the chunk size less than 64, and fire up as many
- * waiting threads as there are chunks. Then each thread would wait on a chunk of
- * handles, and will report back to the caller which handle has been set.
- * Here is the implementation of that algorithm.
- */
-
-/* Number of handles to wait on in each wating thread. */
-#define WAIT_ALL_CHUNK_SIZE 63
-
-/* Descriptor for a wating thread */
-typedef struct WaitForAllParam {
- /* A handle to an event to signal when waiting is over. This handle is shared
- * accross all the waiting threads, so each waiting thread knows when any
- * other thread has exited, so it can exit too. */
- HANDLE main_event;
- /* Upon exit from a waiting thread contains the index of the handle that has
- * been signaled. The index is an absolute index of the signaled handle in
- * the original array. This pointer is shared accross all the waiting threads
- * and it's not guaranteed (due to a race condition) that when all the
- * waiting threads exit, the value contained here would indicate the first
- * handle that was signaled. This is fine, because the caller cares only
- * about any handle being signaled. It doesn't care about the order, nor
- * about the whole list of handles that were signaled. */
- LONG volatile *signaled_index;
- /* Array of handles to wait on in a waiting thread. */
- HANDLE* handles;
- /* Number of handles in 'handles' array to wait on. */
- int handles_count;
- /* Index inside the main array of the first handle in the 'handles' array. */
- int first_handle_index;
- /* Waiting thread handle. */
- HANDLE thread;
-} WaitForAllParam;
-
-/* Waiting thread routine. */
-static unsigned __stdcall
-_in_waiter_thread(void* arg)
-{
- HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1];
- int res;
- WaitForAllParam* const param = (WaitForAllParam*)arg;
-
- /* We have to wait on the main_event in order to be notified when any of the
- * sibling threads is exiting. */
- wait_on[0] = param->main_event;
- /* The rest of the handles go behind the main event handle. */
- memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE));
-
- res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE);
- if (res > 0 && res < (param->handles_count + 1)) {
- /* One of the original handles got signaled. Save its absolute index into
- * the output variable. */
- InterlockedCompareExchange(param->signaled_index,
- res - 1L + param->first_handle_index, -1L);
- }
-
- /* Notify the caller (and the siblings) that the wait is over. */
- SetEvent(param->main_event);
-
- _endthreadex(0);
- return 0;
-}
-
-/* WaitForMultipeObjects fixer routine.
- * Param:
- * handles Array of handles to wait on.
- * handles_count Number of handles in the array.
- * Return:
- * (>= 0 && < handles_count) - Index of the signaled handle in the array, or
- * WAIT_FAILED on an error.
- */
-static int
-_wait_for_all(HANDLE* handles, int handles_count)
-{
- WaitForAllParam* threads;
- HANDLE main_event;
- int chunks, chunk, remains;
-
- /* This variable is going to be accessed by several threads at the same time,
- * this is bound to fail randomly when the core is run on multi-core machines.
- * To solve this, we need to do the following (1 _and_ 2):
- * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize
- * out the reads/writes in this function unexpectedly.
- * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap
- * all accesses inside a critical section. But we can also use
- * InterlockedCompareExchange() which always provide a full memory barrier
- * on Win32.
- */
- volatile LONG sig_index = -1;
-
- /* Calculate number of chunks, and allocate thread param array. */
- chunks = handles_count / WAIT_ALL_CHUNK_SIZE;
- remains = handles_count % WAIT_ALL_CHUNK_SIZE;
- threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
- sizeof(WaitForAllParam));
- if (threads == NULL) {
- D("Unable to allocate thread array for %d handles.\n", handles_count);
- return (int)WAIT_FAILED;
- }
-
- /* Create main event to wait on for all waiting threads. This is a "manualy
- * reset" event that will remain set once it was set. */
- main_event = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (main_event == NULL) {
- D("Unable to create main event. Error: %ld\n", GetLastError());
- free(threads);
- return (int)WAIT_FAILED;
- }
-
- /*
- * Initialize waiting thread parameters.
- */
-
- for (chunk = 0; chunk < chunks; chunk++) {
- threads[chunk].main_event = main_event;
- threads[chunk].signaled_index = &sig_index;
- threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
- threads[chunk].handles = handles + threads[chunk].first_handle_index;
- threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE;
- }
- if (remains) {
- threads[chunk].main_event = main_event;
- threads[chunk].signaled_index = &sig_index;
- threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
- threads[chunk].handles = handles + threads[chunk].first_handle_index;
- threads[chunk].handles_count = remains;
- chunks++;
- }
-
- /* Start the waiting threads. */
- for (chunk = 0; chunk < chunks; chunk++) {
- /* Note that using adb_thread_create is not appropriate here, since we
- * need a handle to wait on for thread termination. */
- threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread,
- &threads[chunk], 0, NULL);
- if (threads[chunk].thread == NULL) {
- /* Unable to create a waiter thread. Collapse. */
- D("Unable to create a waiting thread %d of %d. errno=%d\n",
- chunk, chunks, errno);
- chunks = chunk;
- SetEvent(main_event);
- break;
- }
- }
-
- /* Wait on any of the threads to get signaled. */
- WaitForSingleObject(main_event, INFINITE);
-
- /* Wait on all the waiting threads to exit. */
- for (chunk = 0; chunk < chunks; chunk++) {
- WaitForSingleObject(threads[chunk].thread, INFINITE);
- CloseHandle(threads[chunk].thread);
- }
-
- CloseHandle(main_event);
- free(threads);
-
-
- const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1);
- return (ret >= 0) ? ret : (int)WAIT_FAILED;
-}
-
-static EventLooperRec win32_looper;
-
-static void fdevent_init(void)
-{
- win32_looper.htab_count = 0;
- win32_looper.hooks = NULL;
-}
-
-static void fdevent_connect(fdevent *fde)
-{
- EventLooper looper = &win32_looper;
- int events = fde->state & FDE_EVENTMASK;
-
- if (events != 0)
- event_looper_hook( looper, fde->fd, events );
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- EventLooper looper = &win32_looper;
- int events = fde->state & FDE_EVENTMASK;
-
- if (events != 0)
- event_looper_unhook( looper, fde->fd, events );
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- EventLooper looper = &win32_looper;
- unsigned events0 = fde->state & FDE_EVENTMASK;
-
- if (events != events0) {
- int removes = events0 & ~events;
- int adds = events & ~events0;
- if (removes) {
- D("fdevent_update: remove %x from %d\n", removes, fde->fd);
- event_looper_unhook( looper, fde->fd, removes );
- }
- if (adds) {
- D("fdevent_update: add %x to %d\n", adds, fde->fd);
- event_looper_hook ( looper, fde->fd, adds );
- }
- }
-}
-
-static void fdevent_process()
-{
- EventLooper looper = &win32_looper;
- EventHook hook;
- int gotone = 0;
-
- /* if we have at least one ready hook, execute it/them */
- for (hook = looper->hooks; hook; hook = hook->next) {
- hook->ready = 0;
- if (hook->prepare) {
- hook->prepare(hook);
- if (hook->ready != 0) {
- event_hook_signal( hook );
- gotone = 1;
- }
- }
- }
-
- /* nothing's ready yet, so wait for something to happen */
- if (!gotone)
- {
- looper->htab_count = 0;
-
- for (hook = looper->hooks; hook; hook = hook->next)
- {
- if (hook->start && !hook->start(hook)) {
- D( "fdevent_process: error when starting a hook\n" );
- return;
- }
- if (hook->h != INVALID_HANDLE_VALUE) {
- int nn;
-
- for (nn = 0; nn < looper->htab_count; nn++)
- {
- if ( looper->htab[nn] == hook->h )
- goto DontAdd;
- }
- looper->htab[ looper->htab_count++ ] = hook->h;
- DontAdd:
- ;
- }
- }
-
- if (looper->htab_count == 0) {
- D( "fdevent_process: nothing to wait for !!\n" );
- return;
- }
-
- do
- {
- int wait_ret;
-
- D( "adb_win32: waiting for %d events\n", looper->htab_count );
- if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
- D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.\n", looper->htab_count);
- wait_ret = _wait_for_all(looper->htab, looper->htab_count);
- } else {
- wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
- }
- if (wait_ret == (int)WAIT_FAILED) {
- D( "adb_win32: wait failed, error %ld\n", GetLastError() );
- } else {
- D( "adb_win32: got one (index %d)\n", wait_ret );
-
- /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
- * like mouse movements. we need to filter these with the "check" function
- */
- if ((unsigned)wait_ret < (unsigned)looper->htab_count)
- {
- for (hook = looper->hooks; hook; hook = hook->next)
- {
- if ( looper->htab[wait_ret] == hook->h &&
- (!hook->check || hook->check(hook)) )
- {
- D( "adb_win32: signaling %s for %x\n", hook->fh->name, hook->ready );
- event_hook_signal( hook );
- gotone = 1;
- break;
- }
- }
- }
- }
- }
- while (!gotone);
-
- for (hook = looper->hooks; hook; hook = hook->next) {
- if (hook->stop)
- hook->stop( hook );
- }
- }
-
- for (hook = looper->hooks; hook; hook = hook->next) {
- if (hook->peek && hook->peek(hook))
- event_hook_signal( hook );
- }
-}
-
-
-static void fdevent_register(fdevent *fde)
-{
- int fd = fde->fd - WIN32_FH_BASE;
-
- if(fd < 0) {
- FATAL("bogus negative fd (%d)\n", fde->fd);
- }
-
- if(fd >= fd_table_max) {
- int oldmax = fd_table_max;
- if(fde->fd > 32000) {
- FATAL("bogus huuuuge fd (%d)\n", fde->fd);
- }
- if(fd_table_max == 0) {
- fdevent_init();
- fd_table_max = 256;
- }
- while(fd_table_max <= fd) {
- fd_table_max *= 2;
- }
- fd_table = reinterpret_cast<fdevent**>(realloc(fd_table, sizeof(fdevent*) * fd_table_max));
- if(fd_table == 0) {
- FATAL("could not expand fd_table to %d entries\n", fd_table_max);
- }
- memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
- }
-
- fd_table[fd] = fde;
-}
-
-static void fdevent_unregister(fdevent *fde)
-{
- int fd = fde->fd - WIN32_FH_BASE;
-
- if((fd < 0) || (fd >= fd_table_max)) {
- FATAL("fd out of range (%d)\n", fde->fd);
- }
-
- if(fd_table[fd] != fde) {
- FATAL("fd_table out of sync");
- }
-
- fd_table[fd] = 0;
-
- if(!(fde->state & FDE_DONT_CLOSE)) {
- dump_fde(fde, "close");
- adb_close(fde->fd);
- }
-}
-
-static void fdevent_plist_enqueue(fdevent *node)
-{
- fdevent *list = &list_pending;
-
- node->next = list;
- node->prev = list->prev;
- node->prev->next = node;
- list->prev = node;
-}
-
-static void fdevent_plist_remove(fdevent *node)
-{
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->next = 0;
- node->prev = 0;
-}
-
-static fdevent *fdevent_plist_dequeue(void)
-{
- fdevent *list = &list_pending;
- fdevent *node = list->next;
-
- if(node == list) return 0;
-
- list->next = node->next;
- list->next->prev = list;
- node->next = 0;
- node->prev = 0;
-
- return node;
-}
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
- 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)
-{
- if(fde == 0) return;
- if(!(fde->state & FDE_CREATED)) {
- FATAL("fde %p not created by fdevent_create()\n", fde);
- }
- fdevent_remove(fde);
-}
-
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
-{
- memset(fde, 0, sizeof(fdevent));
- fde->state = FDE_ACTIVE;
- fde->fd = fd;
- fde->func = func;
- fde->arg = arg;
-
- fdevent_register(fde);
- dump_fde(fde, "connect");
- fdevent_connect(fde);
- fde->state |= FDE_ACTIVE;
-}
-
-void fdevent_remove(fdevent *fde)
-{
- if(fde->state & FDE_PENDING) {
- fdevent_plist_remove(fde);
- }
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_disconnect(fde);
- dump_fde(fde, "disconnect");
- fdevent_unregister(fde);
- }
-
- fde->state = 0;
- fde->events = 0;
-}
-
-
-void fdevent_set(fdevent *fde, unsigned events)
-{
- events &= FDE_EVENTMASK;
-
- if((fde->state & FDE_EVENTMASK) == (int)events) return;
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_update(fde, events);
- dump_fde(fde, "update");
- }
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-
- if(fde->state & FDE_PENDING) {
- /* if we're pending, make sure
- ** we don't signal an event that
- ** is no longer wanted.
- */
- fde->events &= (~events);
- if(fde->events == 0) {
- fdevent_plist_remove(fde);
- fde->state &= (~FDE_PENDING);
- }
- }
-}
-
-void fdevent_add(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
-}
-
-void fdevent_del(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
-}
-
-void fdevent_loop()
-{
- fdevent *fde;
-
- for(;;) {
-#if DEBUG
- fprintf(stderr,"--- ---- waiting for events\n");
-#endif
- fdevent_process();
-
- while((fde = fdevent_plist_dequeue())) {
- unsigned events = fde->events;
- fde->events = 0;
- fde->state &= (~FDE_PENDING);
- dump_fde(fde, "callback");
- fde->func(fde->fd, events, fde->arg);
- }
- }
-}
-
-/** FILE EVENT HOOKS
- **/
-
-static void _event_file_prepare( EventHook hook )
-{
- if (hook->wanted & (FDE_READ|FDE_WRITE)) {
- /* we can always read/write */
- hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
- }
-}
-
-static int _event_file_peek( EventHook hook )
-{
- return (hook->wanted & (FDE_READ|FDE_WRITE));
-}
-
-static void _fh_file_hook( FH f, int events, EventHook hook )
-{
- hook->h = f->fh_handle;
- hook->prepare = _event_file_prepare;
- hook->peek = _event_file_peek;
-}
-
-/** SOCKET EVENT HOOKS
- **/
-
-static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts )
-{
- if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
- if (hook->wanted & FDE_READ)
- hook->ready |= FDE_READ;
- if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
- if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
- if (hook->wanted & FDE_WRITE)
- hook->ready |= FDE_WRITE;
- if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
- if ( evts->lNetworkEvents & FD_OOB ) {
- if (hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
-}
-
-static void _event_socket_prepare( EventHook hook )
-{
- WSANETWORKEVENTS evts;
-
- /* look if some of the events we want already happened ? */
- if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
- _event_socket_verify( hook, &evts );
-}
-
-static int _socket_wanted_to_flags( int wanted )
-{
- int flags = 0;
- if (wanted & FDE_READ)
- flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
-
- if (wanted & FDE_WRITE)
- flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
-
- if (wanted & FDE_ERROR)
- flags |= FD_OOB;
-
- return flags;
-}
-
-static int _event_socket_start( EventHook hook )
-{
- /* create an event which we're going to wait for */
- FH fh = hook->fh;
- long flags = _socket_wanted_to_flags( hook->wanted );
-
- hook->h = fh->event;
- if (hook->h == INVALID_HANDLE_VALUE) {
- D( "_event_socket_start: no event for %s\n", fh->name );
- return 0;
- }
-
- if ( flags != fh->mask ) {
- D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags );
- if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
- D( "_event_socket_start: WSAEventSelect() for %s failed, error %d\n", hook->fh->name, WSAGetLastError() );
- CloseHandle( hook->h );
- hook->h = INVALID_HANDLE_VALUE;
- exit(1);
- return 0;
- }
- fh->mask = flags;
- }
- return 1;
-}
-
-static void _event_socket_stop( EventHook hook )
-{
- hook->h = INVALID_HANDLE_VALUE;
-}
-
-static int _event_socket_check( EventHook hook )
-{
- int result = 0;
- FH fh = hook->fh;
- WSANETWORKEVENTS evts;
-
- if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
- _event_socket_verify( hook, &evts );
- result = (hook->ready != 0);
- if (result) {
- ResetEvent( hook->h );
- }
- }
- D( "_event_socket_check %s returns %d\n", fh->name, result );
- return result;
-}
-
-static int _event_socket_peek( EventHook hook )
-{
- WSANETWORKEVENTS evts;
- FH fh = hook->fh;
-
- /* look if some of the events we want already happened ? */
- if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
- _event_socket_verify( hook, &evts );
- if (hook->ready)
- ResetEvent( hook->h );
- }
-
- return hook->ready != 0;
-}
-
-
-
-static void _fh_socket_hook( FH f, int events, EventHook hook )
-{
- hook->prepare = _event_socket_prepare;
- hook->start = _event_socket_start;
- hook->stop = _event_socket_stop;
- hook->check = _event_socket_check;
- hook->peek = _event_socket_peek;
-
- // TODO: check return value?
- _event_socket_start( hook );
-}
-
-/** SOCKETPAIR EVENT HOOKS
- **/
-
-static void _event_socketpair_prepare( EventHook hook )
-{
- FH fh = hook->fh;
- SocketPair pair = fh->fh_pair;
- BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
- BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
- if (hook->wanted & FDE_READ && rbip->can_read)
- hook->ready |= FDE_READ;
-
- if (hook->wanted & FDE_WRITE && wbip->can_write)
- hook->ready |= FDE_WRITE;
- }
-
- static int _event_socketpair_start( EventHook hook )
- {
- FH fh = hook->fh;
- SocketPair pair = fh->fh_pair;
- BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
- BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
- if (hook->wanted == FDE_READ)
- hook->h = rbip->evt_read;
-
- else if (hook->wanted == FDE_WRITE)
- hook->h = wbip->evt_write;
-
- else {
- D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" );
- return 0;
- }
- D( "_event_socketpair_start: hook %s for %x wanted=%x\n",
- hook->fh->name, _fh_to_int(fh), hook->wanted);
- return 1;
-}
-
-static int _event_socketpair_peek( EventHook hook )
-{
- _event_socketpair_prepare( hook );
- return hook->ready != 0;
-}
-
-static void _fh_socketpair_hook( FH fh, int events, EventHook hook )
-{
- hook->prepare = _event_socketpair_prepare;
- hook->start = _event_socketpair_start;
- hook->peek = _event_socketpair_peek;
-}
-
-
-void
-adb_sysdeps_init( void )
-{
-#define ADB_MUTEX(x) InitializeCriticalSection( & x );
-#include "mutex_list.h"
- InitializeCriticalSection( &_win32_lock );
+ return true;
}
/**************************************************************************/
@@ -2349,20 +1186,63 @@
//
// Code organization:
//
+// * _get_console_handle() and unix_isatty() provide console information.
// * stdin_raw_init() and stdin_raw_restore() reconfigure the console.
// * unix_read() detects console windows (as opposed to pipes, files, etc.).
// * _console_read() is the main code of the emulation.
+// Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
+// If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
+// with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
+static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+ // First check isatty(); this is very fast and eliminates most non-console
+ // FDs, but returns 1 for both consoles and character devices like NUL.
+#pragma push_macro("isatty")
+#undef isatty
+ if (!isatty(fd)) {
+ return nullptr;
+ }
+#pragma pop_macro("isatty")
-// Read an input record from the console; one that should be processed.
-static bool _get_interesting_input_record_uncached(const HANDLE console,
- INPUT_RECORD* const input_record) {
+ // To differentiate between character devices and consoles we need to get
+ // the underlying HANDLE and use GetConsoleMode(), which is what requires
+ // GENERIC_READ permissions.
+ const intptr_t intptr_handle = _get_osfhandle(fd);
+ if (intptr_handle == -1) {
+ return nullptr;
+ }
+ const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+ DWORD temp_mode = 0;
+ if (!GetConsoleMode(handle, mode ? mode : &temp_mode)) {
+ return nullptr;
+ }
+
+ return handle;
+}
+
+// Returns a console handle if |stream| is a console, otherwise returns nullptr.
+static HANDLE _get_console_handle(FILE* const stream) {
+ // Save and restore errno to make it easier for callers to prevent from overwriting errno.
+ android::base::ErrnoRestorer er;
+ const int fd = fileno(stream);
+ if (fd < 0) {
+ return nullptr;
+ }
+ return _get_console_handle(fd);
+}
+
+int unix_isatty(int fd) {
+ return _get_console_handle(fd) ? 1 : 0;
+}
+
+// Get the next KEY_EVENT_RECORD that should be processed.
+static bool _get_key_event_record(const HANDLE console, INPUT_RECORD* const input_record) {
for (;;) {
DWORD read_count = 0;
memset(input_record, 0, sizeof(*input_record));
if (!ReadConsoleInputA(console, input_record, 1, &read_count)) {
- D("_get_interesting_input_record_uncached: ReadConsoleInputA() "
- "failed: %s\n", SystemErrorCodeToString(GetLastError()).c_str());
+ D("_get_key_event_record: ReadConsoleInputA() failed: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
errno = EIO;
return false;
}
@@ -2375,6 +1255,18 @@
fatal("ReadConsoleInputA did not return one input record");
}
+ // If the console window is resized, emulate SIGWINCH by breaking out
+ // of read() with errno == EINTR. Note that there is no event on
+ // vertical resize because we don't give the console our own custom
+ // screen buffer (with CreateConsoleScreenBuffer() +
+ // SetConsoleActiveScreenBuffer()). Instead, we use the default which
+ // supports scrollback, but doesn't seem to raise an event for vertical
+ // window resize.
+ if (input_record->EventType == WINDOW_BUFFER_SIZE_EVENT) {
+ errno = EINTR;
+ return false;
+ }
+
if ((input_record->EventType == KEY_EVENT) &&
(input_record->Event.KeyEvent.bKeyDown)) {
if (input_record->Event.KeyEvent.wRepeatCount == 0) {
@@ -2388,28 +1280,6 @@
}
}
-// Cached input record (in case _console_read() is passed a buffer that doesn't
-// have enough space to fit wRepeatCount number of key sequences). A non-zero
-// wRepeatCount indicates that a record is cached.
-static INPUT_RECORD _win32_input_record;
-
-// Get the next KEY_EVENT_RECORD that should be processed.
-static KEY_EVENT_RECORD* _get_key_event_record(const HANDLE console) {
- // If nothing cached, read directly from the console until we get an
- // interesting record.
- if (_win32_input_record.Event.KeyEvent.wRepeatCount == 0) {
- if (!_get_interesting_input_record_uncached(console,
- &_win32_input_record)) {
- // There was an error, so make sure wRepeatCount is zero because
- // that signifies no cached input record.
- _win32_input_record.Event.KeyEvent.wRepeatCount = 0;
- return NULL;
- }
- }
-
- return &_win32_input_record.Event.KeyEvent;
-}
-
static __inline__ bool _is_shift_pressed(const DWORD control_key_state) {
return (control_key_state & SHIFT_PRESSED) != 0;
}
@@ -2754,16 +1624,34 @@
return len + 1;
}
-// Writes to buffer buf (of length len), returning number of bytes written or
-// -1 on error. Never returns zero because Win32 consoles are never 'closed'
-// (as far as I can tell).
+// Internal buffer to satisfy future _console_read() calls.
+static auto& g_console_input_buffer = *new std::vector<char>();
+
+// Writes to buffer buf (of length len), returning number of bytes written or -1 on error. Never
+// returns zero on console closure because Win32 consoles are never 'closed' (as far as I can tell).
static int _console_read(const HANDLE console, void* buf, size_t len) {
for (;;) {
- KEY_EVENT_RECORD* const key_event = _get_key_event_record(console);
- if (key_event == NULL) {
+ // Read of zero bytes should not block waiting for something from the console.
+ if (len == 0) {
+ return 0;
+ }
+
+ // Flush as much as possible from input buffer.
+ if (!g_console_input_buffer.empty()) {
+ const int bytes_read = std::min(len, g_console_input_buffer.size());
+ memcpy(buf, g_console_input_buffer.data(), bytes_read);
+ const auto begin = g_console_input_buffer.begin();
+ g_console_input_buffer.erase(begin, begin + bytes_read);
+ return bytes_read;
+ }
+
+ // Read from the actual console. This may block until input.
+ INPUT_RECORD input_record;
+ if (!_get_key_event_record(console, &input_record)) {
return -1;
}
+ KEY_EVENT_RECORD* const key_event = &input_record.Event.KeyEvent;
const WORD vk = key_event->wVirtualKeyCode;
const CHAR ch = key_event->uChar.AsciiChar;
const DWORD control_key_state = _normalize_altgr_control_key_state(
@@ -2941,7 +1829,12 @@
break;
case 0x32: // 2
+ case 0x33: // 3
+ case 0x34: // 4
+ case 0x35: // 5
case 0x36: // 6
+ case 0x37: // 7
+ case 0x38: // 8
case VK_OEM_MINUS: // -_
{
seqbuflen = _get_control_character(seqbuf, key_event,
@@ -2957,25 +1850,6 @@
}
break;
- case 0x33: // 3
- case 0x34: // 4
- case 0x35: // 5
- case 0x37: // 7
- case 0x38: // 8
- {
- seqbuflen = _get_control_character(seqbuf, key_event,
- control_key_state);
-
- // If Alt is pressed and it isn't Ctrl-Alt-ShiftUp, then
- // prefix with escape.
- if (_is_alt_pressed(control_key_state) &&
- !(_is_ctrl_pressed(control_key_state) &&
- !_is_shift_pressed(control_key_state))) {
- seqbuflen = _escape_prefix(seqbuf, seqbuflen);
- }
- }
- break;
-
case 0x41: // a
case 0x42: // b
case 0x43: // c
@@ -3102,111 +1976,71 @@
//
// Consume the input and 'continue' to cause us to get a new key
// event.
- D("_console_read: unknown virtual key code: %d, enhanced: %s\n",
+ D("_console_read: unknown virtual key code: %d, enhanced: %s",
vk, _is_enhanced_key(control_key_state) ? "true" : "false");
- key_event->wRepeatCount = 0;
continue;
}
- int bytesRead = 0;
-
- // put output wRepeatCount times into buf/len
- while (key_event->wRepeatCount > 0) {
- if (len >= outlen) {
- // Write to buf/len
- memcpy(buf, out, outlen);
- buf = (void*)((char*)buf + outlen);
- len -= outlen;
- bytesRead += outlen;
-
- // consume the input
- --key_event->wRepeatCount;
- } else {
- // Not enough space, so just leave it in _win32_input_record
- // for a subsequent retrieval.
- if (bytesRead == 0) {
- // We didn't write anything because there wasn't enough
- // space to even write one sequence. This should never
- // happen if the caller uses sensible buffer sizes
- // (i.e. >= maximum sequence length which is probably a
- // few bytes long).
- D("_console_read: no buffer space to write one sequence; "
- "buffer: %ld, sequence: %ld\n", (long)len,
- (long)outlen);
- errno = ENOMEM;
- return -1;
- } else {
- // Stop trying to write to buf/len, just return whatever
- // we wrote so far.
- break;
- }
- }
+ // put output wRepeatCount times into g_console_input_buffer
+ while (key_event->wRepeatCount-- > 0) {
+ g_console_input_buffer.insert(g_console_input_buffer.end(), out, out + outlen);
}
- return bytesRead;
+ // Loop around and try to flush g_console_input_buffer
}
}
static DWORD _old_console_mode; // previous GetConsoleMode() result
static HANDLE _console_handle; // when set, console mode should be restored
-void stdin_raw_init(const int fd) {
- if (STDIN_FILENO == fd) {
- const HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
- if ((in == INVALID_HANDLE_VALUE) || (in == NULL)) {
- return;
- }
+void stdin_raw_init() {
+ const HANDLE in = _get_console_handle(STDIN_FILENO, &_old_console_mode);
+ if (in == nullptr) {
+ return;
+ }
- if (GetFileType(in) != FILE_TYPE_CHAR) {
- // stdin might be a file or pipe.
- return;
- }
+ // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
+ // calling the process Ctrl-C routine (configured by
+ // SetConsoleCtrlHandler()).
+ // Disable ENABLE_LINE_INPUT so that input is immediately sent.
+ // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
+ // flag also seems necessary to have proper line-ending processing.
+ DWORD new_console_mode = _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
+ ENABLE_LINE_INPUT |
+ ENABLE_ECHO_INPUT);
+ // Enable ENABLE_WINDOW_INPUT to get window resizes.
+ new_console_mode |= ENABLE_WINDOW_INPUT;
- if (!GetConsoleMode(in, &_old_console_mode)) {
- // If GetConsoleMode() fails, stdin is probably is not a console.
- return;
- }
+ if (!SetConsoleMode(in, new_console_mode)) {
+ // This really should not fail.
+ D("stdin_raw_init: SetConsoleMode() failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
- // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
- // calling the process Ctrl-C routine (configured by
- // SetConsoleCtrlHandler()).
- // Disable ENABLE_LINE_INPUT so that input is immediately sent.
- // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
- // flag also seems necessary to have proper line-ending processing.
- if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
- ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
+ // Once this is set, it means that stdin has been configured for
+ // reading from and that the old console mode should be restored later.
+ _console_handle = in;
+
+ // Note that we don't need to configure C Runtime line-ending
+ // translation because _console_read() does not call the C Runtime to
+ // read from the console.
+}
+
+void stdin_raw_restore() {
+ if (_console_handle != NULL) {
+ const HANDLE in = _console_handle;
+ _console_handle = NULL; // clear state
+
+ if (!SetConsoleMode(in, _old_console_mode)) {
// This really should not fail.
- D("stdin_raw_init: SetConsoleMode() failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
-
- // Once this is set, it means that stdin has been configured for
- // reading from and that the old console mode should be restored later.
- _console_handle = in;
-
- // Note that we don't need to configure C Runtime line-ending
- // translation because _console_read() does not call the C Runtime to
- // read from the console.
- }
-}
-
-void stdin_raw_restore(const int fd) {
- if (STDIN_FILENO == fd) {
- if (_console_handle != NULL) {
- const HANDLE in = _console_handle;
- _console_handle = NULL; // clear state
-
- if (!SetConsoleMode(in, _old_console_mode)) {
- // This really should not fail.
- D("stdin_raw_restore: SetConsoleMode() failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
+ D("stdin_raw_restore: SetConsoleMode() failed: %s",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
}
}
}
-// Called by 'adb shell' and 'adb exec-in' to read from stdin.
-int unix_read(int fd, void* buf, size_t len) {
+// Called by 'adb shell' and 'adb exec-in' (via unix_read()) to read from stdin.
+int unix_read_interruptible(int fd, void* buf, size_t len) {
if ((fd == STDIN_FILENO) && (_console_handle != NULL)) {
// If it is a request to read from stdin, and stdin_raw_init() has been
// called, and it successfully configured the console, then read from
@@ -3214,6 +2048,12 @@
// terminal.
return _console_read(_console_handle, buf, len);
} else {
+ // On older versions of Windows (definitely 7, definitely not 10),
+ // ReadConsole() with a size >= 31367 fails, so if |fd| is a console
+ // we need to limit the read size.
+ if (len > 4096 && unix_isatty(fd)) {
+ len = 4096;
+ }
// Just call into C Runtime which can read from pipes/files and which
// can do LF/CR translation (which is overridable with _setmode()).
// Undefine the macro that is set in sysdeps.h which bans calls to
@@ -3272,11 +2112,11 @@
// The Choice
// ----------
//
-// The code below chooses option 3, the UTF-8 everywhere strategy. It
-// introduces narrow() which converts UTF-16 to UTF-8. This is used by the
+// The code below chooses option 3, the UTF-8 everywhere strategy. It uses
+// android::base::WideToUTF8() which converts UTF-16 to UTF-8. This is used by the
// NarrowArgs helper class that is used to convert wmain() args into UTF-8
-// args that are passed to main() at the beginning of program startup. We also
-// introduce widen() which converts from UTF-8 to UTF-16. This is used to
+// args that are passed to main() at the beginning of program startup. We also use
+// android::base::UTF8ToWide() which converts from UTF-8 to UTF-16. This is used to
// implement wrappers below that call UTF-16 OS and C Runtime APIs.
//
// Unicode console output
@@ -3306,140 +2146,17 @@
// to UTF-16 and then calls WriteConsoleW().
-// Function prototype because attributes cannot be placed on func definitions.
-static void _widen_fatal(const char *fmt, ...)
- __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
-
-// A version of fatal() that does not call adb_(v)fprintf(), so it can be
-// called from those functions.
-static void _widen_fatal(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- // If (v)fprintf are macros that point to adb_(v)fprintf, when random adb
- // code calls (v)fprintf, it may end up calling adb_(v)fprintf, which then
- // calls _widen_fatal(). So then how does _widen_fatal() output a error?
- // By directly calling real C Runtime APIs that don't properly output
- // Unicode, but will be able to get a comprehendible message out. To do
- // this, make sure we don't call (v)fprintf macros by undefining them.
-#pragma push_macro("fprintf")
-#pragma push_macro("vfprintf")
-#undef fprintf
-#undef vfprintf
- fprintf(stderr, "error: ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
-#pragma pop_macro("vfprintf")
-#pragma pop_macro("fprintf")
- va_end(ap);
- exit(-1);
-}
-
-// TODO: Consider implementing widen() and narrow() out of std::wstring_convert
-// once libcxx is supported on Windows. Or, consider libutils/Unicode.cpp.
-
-// Convert from UTF-8 to UTF-16. A size of -1 specifies a NULL terminated
-// string. Any other size specifies the number of chars to convert, excluding
-// any NULL terminator (if you're passing an explicit size, you probably don't
-// have a NULL terminated string in the first place).
-std::wstring widen(const char* utf8, const int size) {
- // Note: Do not call SystemErrorCodeToString() from widen() because
- // SystemErrorCodeToString() calls narrow() which may call fatal() which
- // calls adb_vfprintf() which calls widen(), potentially causing infinite
- // recursion.
- const int chars_to_convert = MultiByteToWideChar(CP_UTF8, 0, utf8, size,
- NULL, 0);
- if (chars_to_convert <= 0) {
- // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
- _widen_fatal("MultiByteToWideChar failed counting: %d, "
- "GetLastError: %lu", chars_to_convert, GetLastError());
- }
-
- std::wstring utf16;
- size_t chars_to_allocate = chars_to_convert;
- if (size == -1) {
- // chars_to_convert includes a NULL terminator, so subtract space
- // for that because resize() includes that itself.
- --chars_to_allocate;
- }
- utf16.resize(chars_to_allocate);
-
- // This uses &string[0] to get write-access to the entire string buffer
- // which may be assuming that the chars are all contiguous, but it seems
- // to work and saves us the hassle of using a temporary
- // std::vector<wchar_t>.
- const int result = MultiByteToWideChar(CP_UTF8, 0, utf8, size, &utf16[0],
- chars_to_convert);
- if (result != chars_to_convert) {
- // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
- _widen_fatal("MultiByteToWideChar failed conversion: %d, "
- "GetLastError: %lu", result, GetLastError());
- }
-
- // If a size was passed in (size != -1), then the string is NULL terminated
- // by a NULL char that was written by std::string::resize(). If size == -1,
- // then MultiByteToWideChar() read a NULL terminator from the original
- // string and converted it to a NULL UTF-16 char in the output.
-
- return utf16;
-}
-
-// Convert a NULL terminated string from UTF-8 to UTF-16.
-std::wstring widen(const char* utf8) {
- // Pass -1 to let widen() determine the string length.
- return widen(utf8, -1);
-}
-
-// Convert from UTF-8 to UTF-16.
-std::wstring widen(const std::string& utf8) {
- return widen(utf8.c_str(), utf8.length());
-}
-
-// Convert from UTF-16 to UTF-8.
-std::string narrow(const std::wstring& utf16) {
- return narrow(utf16.c_str());
-}
-
-// Convert from UTF-16 to UTF-8.
-std::string narrow(const wchar_t* utf16) {
- // Note: Do not call SystemErrorCodeToString() from narrow() because
- // SystemErrorCodeToString() calls narrow() and we don't want potential
- // infinite recursion.
- const int chars_required = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL,
- 0, NULL, NULL);
- if (chars_required <= 0) {
- // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
- fatal("WideCharToMultiByte failed counting: %d, GetLastError: %lu",
- chars_required, GetLastError());
- }
-
- std::string utf8;
- // Subtract space for the NULL terminator because resize() includes
- // that itself. Note that this could potentially throw a std::bad_alloc
- // exception.
- utf8.resize(chars_required - 1);
-
- // This uses &string[0] to get write-access to the entire string buffer
- // which may be assuming that the chars are all contiguous, but it seems
- // to work and saves us the hassle of using a temporary
- // std::vector<char>.
- const int result = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, &utf8[0],
- chars_required, NULL, NULL);
- if (result != chars_required) {
- // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
- fatal("WideCharToMultiByte failed conversion: %d, GetLastError: %lu",
- result, GetLastError());
- }
-
- return utf8;
-}
-
// Constructor for helper class to convert wmain() UTF-16 args to UTF-8 to
// be passed to main().
NarrowArgs::NarrowArgs(const int argc, wchar_t** const argv) {
narrow_args = new char*[argc + 1];
for (int i = 0; i < argc; ++i) {
- narrow_args[i] = strdup(narrow(argv[i]).c_str());
+ std::string arg_narrow;
+ if (!android::base::WideToUTF8(argv[i], &arg_narrow)) {
+ fatal_errno("cannot convert argument from UTF-16 to UTF-8");
+ }
+ narrow_args[i] = strdup(arg_narrow.c_str());
}
narrow_args[argc] = nullptr; // terminate
}
@@ -3455,43 +2172,33 @@
}
int unix_open(const char* path, int options, ...) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
if ((options & O_CREAT) == 0) {
- return _wopen(widen(path).c_str(), options);
+ return _wopen(path_wide.c_str(), options);
} else {
int mode;
va_list args;
va_start(args, options);
mode = va_arg(args, int);
va_end(args);
- return _wopen(widen(path).c_str(), options, mode);
+ return _wopen(path_wide.c_str(), options, mode);
}
}
-// Version of stat() that takes a UTF-8 path.
-int adb_stat(const char* f, struct adb_stat* s) {
-#pragma push_macro("wstat")
-// This definition of wstat seems to be missing from <sys/stat.h>.
-#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
-#ifdef _USE_32BIT_TIME_T
-#define wstat _wstat32i64
-#else
-#define wstat _wstat64
-#endif
-#else
-// <sys/stat.h> has a function prototype for wstat() that should be available.
-#endif
-
- return wstat(widen(f).c_str(), s);
-
-#pragma pop_macro("wstat")
-}
-
// Version of opendir() that takes a UTF-8 path.
-DIR* adb_opendir(const char* name) {
+DIR* adb_opendir(const char* path) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return nullptr;
+ }
+
// Just cast _WDIR* to DIR*. This doesn't work if the caller reads any of
// the fields, but right now all the callers treat the structure as
// opaque.
- return reinterpret_cast<DIR*>(_wopendir(widen(name).c_str()));
+ return reinterpret_cast<DIR*>(_wopendir(path_wide.c_str()));
}
// Version of readdir() that returns UTF-8 paths.
@@ -3501,8 +2208,12 @@
if (went == nullptr) {
return nullptr;
}
+
// Convert from UTF-16 to UTF-8.
- const std::string name_utf8(narrow(went->d_name));
+ std::string name_utf8;
+ if (!android::base::WideToUTF8(went->d_name, &name_utf8)) {
+ return nullptr;
+ }
// Cast the _wdirent* to dirent* and overwrite the d_name field (which has
// space for UTF-16 wchar_t's) with UTF-8 char's.
@@ -3534,7 +2245,10 @@
// Version of unlink() that takes a UTF-8 path.
int adb_unlink(const char* path) {
- const std::wstring wpath(widen(path));
+ std::wstring wpath;
+ if (!android::base::UTF8ToWide(path, &wpath)) {
+ return -1;
+ }
int rc = _wunlink(wpath.c_str());
@@ -3550,63 +2264,131 @@
// Version of mkdir() that takes a UTF-8 path.
int adb_mkdir(const std::string& path, int mode) {
- return _wmkdir(widen(path.c_str()).c_str());
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
+ return _wmkdir(path_wide.c_str());
}
// Version of utime() that takes a UTF-8 path.
int adb_utime(const char* path, struct utimbuf* u) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
static_assert(sizeof(struct utimbuf) == sizeof(struct _utimbuf),
"utimbuf and _utimbuf should be the same size because they both "
"contain the same types, namely time_t");
- return _wutime(widen(path).c_str(), reinterpret_cast<struct _utimbuf*>(u));
+ return _wutime(path_wide.c_str(), reinterpret_cast<struct _utimbuf*>(u));
}
// Version of chmod() that takes a UTF-8 path.
int adb_chmod(const char* path, int mode) {
- return _wchmod(widen(path).c_str(), mode);
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
+ return _wchmod(path_wide.c_str(), mode);
}
-// Internal function to get a Win32 console HANDLE from a C Runtime FILE*.
-static HANDLE _get_console_handle(FILE* const stream) {
- // Get a C Runtime file descriptor number from the FILE* structure.
- const int fd = fileno(stream);
- if (fd < 0) {
- return NULL;
- }
-
- // If it is not a "character device", it is probably a file and not a
- // console. Do this check early because it is probably cheap. Still do more
- // checks after this since there are devices that pass this test, but are
- // not a console, such as NUL, the Windows /dev/null equivalent (I think).
- if (!isatty(fd)) {
- return NULL;
- }
-
- // Given a C Runtime file descriptor number, get the underlying OS
- // file handle.
- const intptr_t osfh = _get_osfhandle(fd);
- if (osfh == -1) {
- return NULL;
- }
-
- const HANDLE h = reinterpret_cast<const HANDLE>(osfh);
-
- DWORD old_mode = 0;
- if (!GetConsoleMode(h, &old_mode)) {
- return NULL;
- }
-
- // If GetConsoleMode() was successful, assume this is a console.
- return h;
+// From libutils/Unicode.cpp, get the length of a UTF-8 sequence given the lead byte.
+static inline size_t utf8_codepoint_len(uint8_t ch) {
+ return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1;
}
-// Internal helper function to write UTF-8 bytes to a console. Returns -1
-// on error.
-static int _console_write_utf8(const char* buf, size_t size, FILE* stream,
+namespace internal {
+
+// Given a sequence of UTF-8 bytes (denoted by the range [first, last)), return the number of bytes
+// (from the beginning) that are complete UTF-8 sequences and append the remaining bytes to
+// remaining_bytes.
+size_t ParseCompleteUTF8(const char* const first, const char* const last,
+ std::vector<char>* const remaining_bytes) {
+ // Walk backwards from the end of the sequence looking for the beginning of a UTF-8 sequence.
+ // Current_after points one byte past the current byte to be examined.
+ for (const char* current_after = last; current_after != first; --current_after) {
+ const char* const current = current_after - 1;
+ const char ch = *current;
+ const char kHighBit = 0x80u;
+ const char kTwoHighestBits = 0xC0u;
+ if ((ch & kHighBit) == 0) { // high bit not set
+ // The buffer ends with a one-byte UTF-8 sequence, possibly followed by invalid trailing
+ // bytes with no leading byte, so return the entire buffer.
+ break;
+ } else if ((ch & kTwoHighestBits) == kTwoHighestBits) { // top two highest bits set
+ // Lead byte in UTF-8 sequence, so check if we have all the bytes in the sequence.
+ const size_t bytes_available = last - current;
+ if (bytes_available < utf8_codepoint_len(ch)) {
+ // We don't have all the bytes in the UTF-8 sequence, so return all the bytes
+ // preceding the current incomplete UTF-8 sequence and append the remaining bytes
+ // to remaining_bytes.
+ remaining_bytes->insert(remaining_bytes->end(), current, last);
+ return current - first;
+ } else {
+ // The buffer ends with a complete UTF-8 sequence, possibly followed by invalid
+ // trailing bytes with no lead byte, so return the entire buffer.
+ break;
+ }
+ } else {
+ // Trailing byte, so keep going backwards looking for the lead byte.
+ }
+ }
+
+ // Return the size of the entire buffer. It is possible that we walked backward past invalid
+ // trailing bytes with no lead byte, in which case we want to return all those invalid bytes
+ // so that they can be processed.
+ return last - first;
+}
+
+}
+
+// Bytes that have not yet been output to the console because they are incomplete UTF-8 sequences.
+// Note that we use only one buffer even though stderr and stdout are logically separate streams.
+// This matches the behavior of Linux.
+
+// Internal helper function to write UTF-8 bytes to a console. Returns -1 on error.
+static int _console_write_utf8(const char* const buf, const size_t buf_size, FILE* stream,
HANDLE console) {
- // Convert from UTF-8 to UTF-16.
+ static std::mutex& console_output_buffer_lock = *new std::mutex();
+ static auto& console_output_buffer = *new std::vector<char>();
+
+ const int saved_errno = errno;
+ std::vector<char> combined_buffer;
+
+ // Complete UTF-8 sequences that should be immediately written to the console.
+ const char* utf8;
+ size_t utf8_size;
+
+ {
+ std::lock_guard<std::mutex> lock(console_output_buffer_lock);
+ if (console_output_buffer.empty()) {
+ // If console_output_buffer doesn't have a buffered up incomplete UTF-8 sequence (the
+ // common case with plain ASCII), parse buf directly.
+ utf8 = buf;
+ utf8_size = internal::ParseCompleteUTF8(buf, buf + buf_size, &console_output_buffer);
+ } else {
+ // If console_output_buffer has a buffered up incomplete UTF-8 sequence, move it to
+ // combined_buffer (and effectively clear console_output_buffer) and append buf to
+ // combined_buffer, then parse it all together.
+ combined_buffer.swap(console_output_buffer);
+ combined_buffer.insert(combined_buffer.end(), buf, buf + buf_size);
+
+ utf8 = combined_buffer.data();
+ utf8_size = internal::ParseCompleteUTF8(utf8, utf8 + combined_buffer.size(),
+ &console_output_buffer);
+ }
+ }
+
+ std::wstring utf16;
+
+ // Try to convert from data that might be UTF-8 to UTF-16, ignoring errors (just like Linux
+ // which does not return an error on bad UTF-8). Data might not be UTF-8 if the user cat's
+ // random data, runs dmesg (which might have non-UTF-8), etc.
// This could throw std::bad_alloc.
- const std::wstring output(widen(buf, size));
+ (void)android::base::UTF8ToWide(utf8, utf8_size, &utf16);
// Note that this does not do \n => \r\n translation because that
// doesn't seem necessary for the Windows console. For the Windows
@@ -3619,16 +2401,16 @@
// Write UTF-16 to the console.
DWORD written = 0;
- if (!WriteConsoleW(console, output.c_str(), output.length(), &written,
- NULL)) {
+ if (!WriteConsoleW(console, utf16.c_str(), utf16.length(), &written, NULL)) {
errno = EIO;
return -1;
}
- // This is the number of UTF-16 chars written, which might be different
- // than the number of UTF-8 chars passed in. It doesn't seem practical to
- // get this count correct.
- return written;
+ // Return the size of the original buffer passed in, signifying that we consumed it all, even
+ // if nothing was displayed, in the case of being passed an incomplete UTF-8 sequence. This
+ // matches the Linux behavior.
+ errno = saved_errno;
+ return buf_size;
}
// Function prototype because attributes cannot be placed on func definitions.
@@ -3640,14 +2422,21 @@
// Returns -1 on error.
static int _console_vfprintf(const HANDLE console, FILE* stream,
const char *format, va_list ap) {
+ const int saved_errno = errno;
std::string output_utf8;
// Format the string.
// This could throw std::bad_alloc.
android::base::StringAppendV(&output_utf8, format, ap);
- return _console_write_utf8(output_utf8.c_str(), output_utf8.length(),
- stream, console);
+ const int result = _console_write_utf8(output_utf8.c_str(), output_utf8.length(), stream,
+ console);
+ if (result != -1) {
+ errno = saved_errno;
+ } else {
+ // If -1 was returned, errno has been set.
+ }
+ return result;
}
// Version of vfprintf() that takes UTF-8 and can write Unicode to a
@@ -3669,6 +2458,11 @@
}
}
+// Version of vprintf() that takes UTF-8 and can write Unicode to a Windows console.
+int adb_vprintf(const char *format, va_list ap) {
+ return adb_vfprintf(stdout, format, ap);
+}
+
// Version of fprintf() that takes UTF-8 and can write Unicode to a
// Windows console.
int adb_fprintf(FILE *stream, const char *format, ...) {
@@ -3696,6 +2490,7 @@
int adb_fputs(const char* buf, FILE* stream) {
// adb_fprintf returns -1 on error, which is conveniently the same as EOF
// which fputs (and hence adb_fputs) should return on error.
+ static_assert(EOF == -1, "EOF is not -1, so this code needs to be fixed");
return adb_fprintf(stream, "%s", buf);
}
@@ -3703,32 +2498,32 @@
// Windows console.
int adb_fputc(int ch, FILE* stream) {
const int result = adb_fprintf(stream, "%c", ch);
- if (result <= 0) {
- // If there was an error, or if nothing was printed (which should be an
- // error), return an error, which fprintf signifies with EOF.
+ if (result == -1) {
return EOF;
}
// For success, fputc returns the char, cast to unsigned char, then to int.
return static_cast<unsigned char>(ch);
}
+// Version of putchar() that takes UTF-8 and can write Unicode to a Windows console.
+int adb_putchar(int ch) {
+ return adb_fputc(ch, stdout);
+}
+
+// Version of puts() that takes UTF-8 and can write Unicode to a Windows console.
+int adb_puts(const char* buf) {
+ // adb_printf returns -1 on error, which is conveniently the same as EOF
+ // which puts (and hence adb_puts) should return on error.
+ static_assert(EOF == -1, "EOF is not -1, so this code needs to be fixed");
+ return adb_printf("%s\n", buf);
+}
+
// Internal function to write UTF-8 to a Win32 console. Returns the number of
// items (of length size) written. On error, returns a short item count or 0.
static size_t _console_fwrite(const void* ptr, size_t size, size_t nmemb,
FILE* stream, HANDLE console) {
- // TODO: Note that a Unicode character could be several UTF-8 bytes. But
- // if we're passed only some of the bytes of a character (for example, from
- // the network socket for adb shell), we won't be able to convert the char
- // to a complete UTF-16 char (or surrogate pair), so the output won't look
- // right.
- //
- // To fix this, see libutils/Unicode.cpp for hints on decoding UTF-8.
- //
- // For now we ignore this problem because the alternative is that we'd have
- // to parse UTF-8 and buffer things up (doable). At least this is better
- // than what we had before -- always incorrect multi-byte UTF-8 output.
- int result = _console_write_utf8(reinterpret_cast<const char*>(ptr),
- size * nmemb, stream, console);
+ const int result = _console_write_utf8(reinterpret_cast<const char*>(ptr), size * nmemb, stream,
+ console);
if (result == -1) {
return 0;
}
@@ -3756,8 +2551,41 @@
// Version of fopen() that takes a UTF-8 filename and can access a file with
// a Unicode filename.
-FILE* adb_fopen(const char* f, const char* m) {
- return _wfopen(widen(f).c_str(), widen(m).c_str());
+FILE* adb_fopen(const char* path, const char* mode) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return nullptr;
+ }
+
+ std::wstring mode_wide;
+ if (!android::base::UTF8ToWide(mode, &mode_wide)) {
+ return nullptr;
+ }
+
+ return _wfopen(path_wide.c_str(), mode_wide.c_str());
+}
+
+// Return a lowercase version of the argument. Uses C Runtime tolower() on
+// each byte which is not UTF-8 aware, and theoretically uses the current C
+// Runtime locale (which in practice is not changed, so this becomes a ASCII
+// conversion).
+static std::string ToLower(const std::string& anycase) {
+ // copy string
+ std::string str(anycase);
+ // transform the copy
+ std::transform(str.begin(), str.end(), str.begin(), tolower);
+ return str;
+}
+
+extern "C" int main(int argc, char** argv);
+
+// Link with -municode to cause this wmain() to be used as the program
+// entrypoint. It will convert the args from UTF-16 to UTF-8 and call the
+// regular main() with UTF-8 args.
+extern "C" int wmain(int argc, wchar_t **argv) {
+ // Convert args from UTF-16 to UTF-8 and pass that to main().
+ NarrowArgs narrow_args(argc, argv);
+ return main(argc, narrow_args.data());
}
// Shadow UTF-8 environment variable name/value pairs that are created from
@@ -3765,7 +2593,7 @@
// currently updated if putenv, setenv, unsetenv are called. Note that no
// thread synchronization is done, but we're called early enough in
// single-threaded startup that things work ok.
-static std::unordered_map<std::string, char*> g_environ_utf8;
+static auto& g_environ_utf8 = *new std::unordered_map<std::string, char*>();
// Make sure that shadow UTF-8 environment variables are setup.
static void _ensure_env_setup() {
@@ -3774,6 +2602,13 @@
return;
}
+ if (_wenviron == nullptr) {
+ // If _wenviron is null, then -municode probably wasn't used. That
+ // linker flag will cause the entry point to setup _wenviron. It will
+ // also require an implementation of wmain() (which we provide above).
+ fatal("_wenviron is not set, did you link with -municode?");
+ }
+
// Read name/value pairs from UTF-16 _wenviron and write new name/value
// pairs to UTF-8 g_environ_utf8. Note that it probably does not make sense
// to use the D() macro here because that tracing only works if the
@@ -3787,21 +2622,38 @@
continue;
}
- const std::string name_utf8(narrow(std::wstring(*env, equal - *env)));
- char* const value_utf8 = strdup(narrow(equal + 1).c_str());
+ // If we encounter an error converting UTF-16, don't error-out on account of a single env
+ // var because the program might never even read this particular variable.
+ std::string name_utf8;
+ if (!android::base::WideToUTF8(*env, equal - *env, &name_utf8)) {
+ continue;
+ }
- // Overwrite any duplicate name, but there shouldn't be a dup in the
- // first place.
- g_environ_utf8[name_utf8] = value_utf8;
+ // Store lowercase name so that we can do case-insensitive searches.
+ name_utf8 = ToLower(name_utf8);
+
+ std::string value_utf8;
+ if (!android::base::WideToUTF8(equal + 1, &value_utf8)) {
+ continue;
+ }
+
+ char* const value_dup = strdup(value_utf8.c_str());
+
+ // Don't overwrite a previus env var with the same name. In reality,
+ // the system probably won't let two env vars with the same name exist
+ // in _wenviron.
+ g_environ_utf8.insert({name_utf8, value_dup});
}
}
// Version of getenv() that takes a UTF-8 environment variable name and
-// retrieves a UTF-8 value.
+// retrieves a UTF-8 value. Case-insensitive to match getenv() on Windows.
char* adb_getenv(const char* name) {
_ensure_env_setup();
- const auto it = g_environ_utf8.find(std::string(name));
+ // Case-insensitive search by searching for lowercase name in a map of
+ // lowercase names.
+ const auto it = g_environ_utf8.find(ToLower(std::string(name)));
if (it == g_environ_utf8.end()) {
return nullptr;
}
@@ -3816,10 +2668,15 @@
return nullptr;
}
- const std::string buf_utf8(narrow(wbuf));
+ std::string buf_utf8;
+ const bool narrow_result = android::base::WideToUTF8(wbuf, &buf_utf8);
free(wbuf);
wbuf = nullptr;
+ if (!narrow_result) {
+ return nullptr;
+ }
+
// If size was specified, make sure all the chars will fit.
if (size != 0) {
if (size < static_cast<int>(buf_utf8.length() + 1)) {
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
new file mode 100644
index 0000000..529b212
--- /dev/null
+++ b/adb/sysdeps_win32_test.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "sysdeps.h"
+
+#include <android-base/test_utils.h>
+
+TEST(sysdeps_win32, adb_getenv) {
+ // Insert all test env vars before first call to adb_getenv() which will
+ // read the env var block only once.
+ ASSERT_EQ(0, _putenv("SYSDEPS_WIN32_TEST_UPPERCASE=1"));
+ ASSERT_EQ(0, _putenv("sysdeps_win32_test_lowercase=2"));
+ ASSERT_EQ(0, _putenv("Sysdeps_Win32_Test_MixedCase=3"));
+
+ // UTF-16 value
+ ASSERT_EQ(0, _wputenv(L"SYSDEPS_WIN32_TEST_UNICODE=\u00a1\u0048\u006f\u006c"
+ L"\u0061\u0021\u03b1\u03b2\u03b3\u0061\u006d\u0062"
+ L"\u0075\u006c\u014d\u043f\u0440\u0438\u0432\u0435"
+ L"\u0442"));
+
+ // Search for non-existant env vars.
+ EXPECT_STREQ(nullptr, adb_getenv("SYSDEPS_WIN32_TEST_NONEXISTANT"));
+
+ // Search for existing env vars.
+
+ // There is no test for an env var with a value of a zero-length string
+ // because _putenv() does not support inserting such an env var.
+
+ // Search for env var that is uppercase.
+ EXPECT_STREQ("1", adb_getenv("SYSDEPS_WIN32_TEST_UPPERCASE"));
+ EXPECT_STREQ("1", adb_getenv("sysdeps_win32_test_uppercase"));
+ EXPECT_STREQ("1", adb_getenv("Sysdeps_Win32_Test_Uppercase"));
+
+ // Search for env var that is lowercase.
+ EXPECT_STREQ("2", adb_getenv("SYSDEPS_WIN32_TEST_LOWERCASE"));
+ EXPECT_STREQ("2", adb_getenv("sysdeps_win32_test_lowercase"));
+ EXPECT_STREQ("2", adb_getenv("Sysdeps_Win32_Test_Lowercase"));
+
+ // Search for env var that is mixed-case.
+ EXPECT_STREQ("3", adb_getenv("SYSDEPS_WIN32_TEST_MIXEDCASE"));
+ EXPECT_STREQ("3", adb_getenv("sysdeps_win32_test_mixedcase"));
+ EXPECT_STREQ("3", adb_getenv("Sysdeps_Win32_Test_MixedCase"));
+
+ // Check that UTF-16 was converted to UTF-8.
+ EXPECT_STREQ("\xc2\xa1\x48\x6f\x6c\x61\x21\xce\xb1\xce\xb2\xce\xb3\x61\x6d"
+ "\x62\x75\x6c\xc5\x8d\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5"
+ "\xd1\x82",
+ adb_getenv("SYSDEPS_WIN32_TEST_UNICODE"));
+
+ // Check an env var that should always be set.
+ const char* path_val = adb_getenv("PATH");
+ EXPECT_NE(nullptr, path_val);
+ if (path_val != nullptr) {
+ EXPECT_GT(strlen(path_val), 0U);
+ }
+}
+
+TEST(sysdeps_win32, unix_isatty) {
+ // stdin and stdout should be consoles. Use CONIN$ and CONOUT$ special files
+ // so that we can test this even if stdin/stdout have been redirected. Read
+ // permissions are required for unix_isatty().
+ int conin_fd = unix_open("CONIN$", O_RDONLY);
+ int conout_fd = unix_open("CONOUT$", O_RDWR);
+ for (const int fd : {conin_fd, conout_fd}) {
+ EXPECT_TRUE(fd >= 0);
+ EXPECT_EQ(1, unix_isatty(fd));
+ EXPECT_EQ(0, unix_close(fd));
+ }
+
+ // nul returns 1 from isatty(), make sure unix_isatty() corrects that.
+ for (auto flags : {O_RDONLY, O_RDWR}) {
+ int nul_fd = unix_open("nul", flags);
+ EXPECT_TRUE(nul_fd >= 0);
+ EXPECT_EQ(0, unix_isatty(nul_fd));
+ EXPECT_EQ(0, unix_close(nul_fd));
+ }
+
+ // Check a real file, both read-write and read-only.
+ TemporaryFile temp_file;
+ EXPECT_TRUE(temp_file.fd >= 0);
+ EXPECT_EQ(0, unix_isatty(temp_file.fd));
+
+ int temp_file_ro_fd = unix_open(temp_file.path, O_RDONLY);
+ EXPECT_TRUE(temp_file_ro_fd >= 0);
+ EXPECT_EQ(0, unix_isatty(temp_file_ro_fd));
+ EXPECT_EQ(0, unix_close(temp_file_ro_fd));
+
+ // Check a real OS pipe.
+ int pipe_fds[2];
+ EXPECT_EQ(0, _pipe(pipe_fds, 64, _O_BINARY));
+ EXPECT_EQ(0, unix_isatty(pipe_fds[0]));
+ EXPECT_EQ(0, unix_isatty(pipe_fds[1]));
+ EXPECT_EQ(0, _close(pipe_fds[0]));
+ EXPECT_EQ(0, _close(pipe_fds[1]));
+
+ // Make sure an invalid FD is handled correctly.
+ EXPECT_EQ(0, unix_isatty(-1));
+}
+
+void TestParseCompleteUTF8(const char* buf, const size_t buf_size,
+ const size_t expected_complete_bytes,
+ const std::vector<char>& expected_remaining_bytes) {
+ std::vector<char> remaining_bytes;
+ const size_t complete_bytes = internal::ParseCompleteUTF8(buf, buf + buf_size,
+ &remaining_bytes);
+ EXPECT_EQ(expected_complete_bytes, complete_bytes);
+ EXPECT_EQ(expected_remaining_bytes, remaining_bytes);
+}
+
+TEST(sysdeps_win32, ParseCompleteUTF8) {
+ const std::vector<std::vector<char>> multi_byte_sequences = {
+ { '\xc2', '\xa9' }, // 2 byte UTF-8 sequence
+ { '\xe1', '\xb4', '\xa8' }, // 3 byte UTF-8 sequence
+ { '\xf0', '\x9f', '\x98', '\x80' }, // 4 byte UTF-8 sequence
+ };
+ std::vector<std::vector<char>> all_sequences = {
+ {}, // 0 bytes
+ { '\0' }, // NULL byte
+ { 'a' }, // 1 byte UTF-8 sequence
+ };
+ all_sequences.insert(all_sequences.end(), multi_byte_sequences.begin(),
+ multi_byte_sequences.end());
+
+ // Vary a prefix of bytes in front of the sequence that we're actually interested in parsing.
+ for (const auto& prefix : all_sequences) {
+ // Parse (prefix + one byte of the sequence at a time)
+ for (const auto& seq : multi_byte_sequences) {
+ std::vector<char> buffer(prefix);
+
+ // For every byte of the sequence except the last
+ for (size_t i = 0; i < seq.size() - 1; ++i) {
+ buffer.push_back(seq[i]);
+
+ // When parsing an incomplete UTF-8 sequence, the amount of the buffer preceding
+ // the start of the incomplete UTF-8 sequence is valid. The remaining bytes are the
+ // bytes of the incomplete UTF-8 sequence.
+ TestParseCompleteUTF8(buffer.data(), buffer.size(), prefix.size(),
+ std::vector<char>(seq.begin(), seq.begin() + i + 1));
+ }
+
+ // For the last byte of the sequence
+ buffer.push_back(seq.back());
+ TestParseCompleteUTF8(buffer.data(), buffer.size(), buffer.size(), std::vector<char>());
+ }
+
+ // Parse (prefix (aka sequence) + invalid trailing bytes) to verify that the invalid
+ // trailing bytes are immediately "returned" to prevent them from being stuck in some
+ // buffer.
+ std::vector<char> buffer(prefix);
+ for (size_t i = 0; i < 8; ++i) {
+ buffer.push_back(0x80); // trailing byte
+ TestParseCompleteUTF8(buffer.data(), buffer.size(), buffer.size(), std::vector<char>());
+ }
+ }
+}
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 59aa14d..cb3e0d8 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -21,8 +21,13 @@
"""
from __future__ import print_function
+import contextlib
+import os
import random
+import socket
+import struct
import subprocess
+import threading
import unittest
import adb
@@ -63,6 +68,167 @@
self.assertEqual(1, p.returncode)
self.assertIn('error', 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()
+ 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):
+ # 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;
+ # adb start-server does the same TCP connection which takes another
+ # second, and it waits 3 seconds after starting the server.
+
+ # Start adb client with redirected stdin/stdout/stderr to check if it
+ # passes those redirections to the adb server that it starts. To do
+ # this, run an instance of the adb server on a non-default port so we
+ # don't conflict with a pre-existing adb server that may already be
+ # setup with adb TCP/emulator connections. If there is a pre-existing
+ # adb server, this also tests whether multiple instances of the adb
+ # server conflict on adb.log.
+
+ port = 5038
+ # Kill any existing server on this non-default port.
+ subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+ stderr=subprocess.STDOUT)
+
+ 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)
+
+ # 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))
+ 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))
+ 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()
+
+ # 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,
+ # stdin must still be open in the adb server. The adb client is
+ # probably letting the adb server inherit stdin which would be
+ # wrong.
+ with self.assertRaises(IOError):
+ p.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
+ # stdout/stderr were not closed and and they must be open in the adb
+ # server, suggesting that the adb client is letting the adb server
+ # inherit stdout/stderr which would be wrong.
+ self.assertTrue(stdout_event.wait(5), "adb stdout not closed")
+ self.assertTrue(stderr_event.wait(5), "adb stderr not closed")
+ finally:
+ # If we started a server, kill it.
+ 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.
+ def _reset_socket_on_close(self, sock):
+ # 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
+ l_linger = 0
+
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+ struct.pack(linger_format, l_onoff, l_linger))
+ # Verify that we set the linger structure properly by retrieving it.
+ linger = sock.getsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 16)
+ self.assertEqual((l_onoff, l_linger),
+ struct.unpack_from(linger_format, linger))
+
+ def test_emu_kill(self):
+ """Ensure that adb emu kill works.
+
+ Bug: https://code.google.com/p/android/issues/detail?id=21021
+ """
+ port = 12345
+
+ with contextlib.closing(
+ 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)
+ listener.bind(('127.0.0.1', port))
+ listener.listen(4)
+
+ # Now that listening has started, start adb emu kill, telling it to
+ # connect to our mock emulator.
+ p = subprocess.Popen(
+ ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
+ stderr=subprocess.STDOUT)
+
+ accepted_connection, addr = listener.accept()
+ with contextlib.closing(accepted_connection) as conn:
+ # 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')
+ conn.sendall('OK\r\n')
+
+ with contextlib.closing(conn.makefile()) as f:
+ self.assertEqual('kill\n', f.readline())
+ self.assertEqual('quit\n', f.readline())
+
+ conn.sendall('OK: killing emulator, bye bye\r\n')
+
+ # Use SO_LINGER to send TCP RST segment to test whether adb
+ # ignores WSAECONNRESET on Windows. This happens with the
+ # real emulator because it just calls exit() without closing
+ # the socket or calling shutdown(SD_SEND). At process
+ # termination, Windows sends a TCP RST segment for every
+ # open socket that shutdown(SD_SEND) wasn't used on.
+ self._reset_socket_on_close(conn)
+
+ # Wait for adb to finish, so we can check return code.
+ p.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)
+
+ def test_connect_ipv4_ipv6(self):
+ """Ensure that `adb connect localhost:1234` will try both IPv4 and IPv6.
+
+ Bug: http://b/30313466
+ """
+ ipv4 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ ipv4.bind(('127.0.0.1', 0))
+ ipv4.listen(1)
+
+ ipv6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+ ipv6.bind(('::1', ipv4.getsockname()[1] + 1))
+ ipv6.listen(1)
+
+ for s in (ipv4, ipv6):
+ port = s.getsockname()[1]
+ output = subprocess.check_output(
+ ['adb', 'connect', 'localhost:{}'.format(port)])
+
+ self.assertEqual(
+ output.strip(), 'connected to localhost:{}'.format(port))
+ s.close()
+
def main():
random.seed(0)
diff --git a/adb/test_device.py b/adb/test_device.py
index c893ad4..e76aaed 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -17,14 +17,21 @@
#
from __future__ import print_function
+import contextlib
import hashlib
import os
import posixpath
import random
+import re
import shlex
import shutil
+import signal
+import socket
+import string
import subprocess
+import sys
import tempfile
+import time
import unittest
import mock
@@ -37,7 +44,7 @@
if self.device.get_prop('ro.debuggable') != '1':
raise unittest.SkipTest('requires rootable build')
- was_root = self.device.shell(['id', '-un']).strip() == 'root'
+ was_root = self.device.shell(['id', '-un'])[0].strip() == 'root'
if not was_root:
self.device.root()
self.device.wait()
@@ -52,6 +59,23 @@
return wrapper
+def requires_non_root(func):
+ def wrapper(self, *args):
+ was_root = self.device.shell(['id', '-un'])[0].strip() == 'root'
+ if was_root:
+ self.device.unroot()
+ self.device.wait()
+
+ try:
+ func(self, *args)
+ finally:
+ if was_root:
+ self.device.root()
+ self.device.wait()
+
+ return wrapper
+
+
class GetDeviceTest(unittest.TestCase):
def setUp(self):
self.android_serial = os.getenv('ANDROID_SERIAL')
@@ -110,10 +134,200 @@
self.device = adb.get_device()
+class ForwardReverseTest(DeviceTest):
+ def _test_no_rebind(self, description, direction_list, direction,
+ direction_no_rebind, direction_remove_all):
+ msg = direction_list()
+ self.assertEqual('', msg.strip(),
+ description + ' list must be empty to run this test.')
+
+ # Use --no-rebind with no existing binding
+ direction_no_rebind('tcp:5566', 'tcp:6655')
+ msg = direction_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+
+ # Use --no-rebind with existing binding
+ with self.assertRaises(subprocess.CalledProcessError):
+ direction_no_rebind('tcp:5566', 'tcp:6677')
+ msg = direction_list()
+ self.assertFalse(re.search(r'tcp:5566.+tcp:6677', msg))
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+
+ # Use the absence of --no-rebind with existing binding
+ direction('tcp:5566', 'tcp:6677')
+ msg = direction_list()
+ self.assertFalse(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6677', msg))
+
+ direction_remove_all()
+ msg = direction_list()
+ self.assertEqual('', msg.strip())
+
+ def test_forward_no_rebind(self):
+ self._test_no_rebind('forward', self.device.forward_list,
+ self.device.forward, self.device.forward_no_rebind,
+ self.device.forward_remove_all)
+
+ def test_reverse_no_rebind(self):
+ self._test_no_rebind('reverse', self.device.reverse_list,
+ self.device.reverse, self.device.reverse_no_rebind,
+ self.device.reverse_remove_all)
+
+ def test_forward(self):
+ msg = self.device.forward_list()
+ self.assertEqual('', msg.strip(),
+ 'Forwarding list must be empty to run this test.')
+ self.device.forward('tcp:5566', 'tcp:6655')
+ msg = self.device.forward_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.device.forward('tcp:7788', 'tcp:8877')
+ msg = self.device.forward_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
+ self.device.forward_remove('tcp:5566')
+ msg = self.device.forward_list()
+ self.assertFalse(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
+ self.device.forward_remove_all()
+ msg = self.device.forward_list()
+ self.assertEqual('', msg.strip())
+
+ def test_forward_tcp_port_0(self):
+ self.assertEqual('', self.device.forward_list().strip(),
+ 'Forwarding list must be empty to run this test.')
+
+ try:
+ # If resolving TCP port 0 is supported, `adb forward` will print
+ # the actual port number.
+ port = self.device.forward('tcp:0', 'tcp:8888').strip()
+ if not port:
+ raise unittest.SkipTest('Forwarding tcp:0 is not available.')
+
+ self.assertTrue(re.search(r'tcp:{}.+tcp:8888'.format(port),
+ self.device.forward_list()))
+ finally:
+ self.device.forward_remove_all()
+
+ def test_reverse(self):
+ msg = self.device.reverse_list()
+ self.assertEqual('', msg.strip(),
+ 'Reverse forwarding list must be empty to run this test.')
+ self.device.reverse('tcp:5566', 'tcp:6655')
+ msg = self.device.reverse_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.device.reverse('tcp:7788', 'tcp:8877')
+ msg = self.device.reverse_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
+ self.device.reverse_remove('tcp:5566')
+ msg = self.device.reverse_list()
+ self.assertFalse(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
+ self.device.reverse_remove_all()
+ msg = self.device.reverse_list()
+ self.assertEqual('', msg.strip())
+
+ def test_reverse_tcp_port_0(self):
+ self.assertEqual('', self.device.reverse_list().strip(),
+ 'Reverse list must be empty to run this test.')
+
+ try:
+ # If resolving TCP port 0 is supported, `adb reverse` will print
+ # the actual port number.
+ port = self.device.reverse('tcp:0', 'tcp:8888').strip()
+ if not port:
+ raise unittest.SkipTest('Reversing tcp:0 is not available.')
+
+ self.assertTrue(re.search(r'tcp:{}.+tcp:8888'.format(port),
+ self.device.reverse_list()))
+ finally:
+ self.device.reverse_remove_all()
+
+ # Note: If you run this test when adb connect'd to a physical device over
+ # TCP, it will fail in adb reverse due to https://code.google.com/p/android/issues/detail?id=189821
+ def test_forward_reverse_echo(self):
+ """Send data through adb forward and read it back via adb reverse"""
+ forward_port = 12345
+ reverse_port = forward_port + 1
+ forward_spec = 'tcp:' + str(forward_port)
+ reverse_spec = 'tcp:' + str(reverse_port)
+ forward_setup = False
+ reverse_setup = False
+
+ try:
+ # listen on localhost:forward_port, connect to remote:forward_port
+ self.device.forward(forward_spec, forward_spec)
+ forward_setup = True
+ # listen on remote:forward_port, connect to localhost:reverse_port
+ self.device.reverse(forward_spec, reverse_spec)
+ reverse_setup = True
+
+ listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ with contextlib.closing(listener):
+ # Use SO_REUSEADDR so that 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)
+
+ # Listen on localhost:reverse_port before connecting to
+ # localhost:forward_port because that will cause adb to connect
+ # back to localhost:reverse_port.
+ listener.bind(('127.0.0.1', reverse_port))
+ listener.listen(4)
+
+ client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ with contextlib.closing(client):
+ # Connect to the listener.
+ client.connect(('127.0.0.1', forward_port))
+
+ # Accept the client connection.
+ accepted_connection, addr = listener.accept()
+ with contextlib.closing(accepted_connection) as server:
+ data = 'hello'
+
+ # Send data into the port setup by adb forward.
+ client.sendall(data)
+ # Explicitly close() so that server gets EOF.
+ client.close()
+
+ # Verify that the data came back via adb reverse.
+ self.assertEqual(data, server.makefile().read())
+ finally:
+ if reverse_setup:
+ self.device.reverse_remove(forward_spec)
+ if forward_setup:
+ self.device.forward_remove(forward_spec)
+
+
class ShellTest(DeviceTest):
+ def _interactive_shell(self, shell_args, input):
+ """Runs an interactive adb shell.
+
+ Args:
+ shell_args: List of string arguments to `adb shell`.
+ input: String input to send to the interactive shell.
+
+ Returns:
+ The remote exit code.
+
+ Raises:
+ unittest.SkipTest: The device doesn't support exit codes.
+ """
+ if not self.device.has_shell_protocol():
+ raise unittest.SkipTest('exit codes are unavailable on this device')
+
+ proc = subprocess.Popen(
+ self.device.adb_cmd + ['shell'] + shell_args,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ # Closing host-side stdin doesn't trigger a PTY shell to exit so we need
+ # to explicitly add an exit command to close the session from the device
+ # side, plus the necessary newline to complete the interactive command.
+ proc.communicate(input + '; exit\n')
+ return proc.returncode
+
def test_cat(self):
"""Check that we can at least cat a file."""
- out = self.device.shell(['cat', '/proc/uptime']).strip()
+ out = self.device.shell(['cat', '/proc/uptime'])[0].strip()
elements = out.split()
self.assertEqual(len(elements), 2)
@@ -122,20 +336,19 @@
self.assertGreater(float(idle), 0.0)
def test_throws_on_failure(self):
- self.assertRaises(subprocess.CalledProcessError,
- self.device.shell, ['false'])
+ self.assertRaises(adb.ShellError, self.device.shell, ['false'])
def test_output_not_stripped(self):
- out = self.device.shell(['echo', 'foo'])
+ out = self.device.shell(['echo', 'foo'])[0]
self.assertEqual(out, 'foo' + self.device.linesep)
def test_shell_nocheck_failure(self):
- rc, out = self.device.shell_nocheck(['false'])
+ rc, out, _ = self.device.shell_nocheck(['false'])
self.assertNotEqual(rc, 0)
self.assertEqual(out, '')
def test_shell_nocheck_output_not_stripped(self):
- rc, out = self.device.shell_nocheck(['echo', 'foo'])
+ rc, out, _ = self.device.shell_nocheck(['echo', 'foo'])
self.assertEqual(rc, 0)
self.assertEqual(out, 'foo' + self.device.linesep)
@@ -143,7 +356,7 @@
# If result checking on ADB shell is naively implemented as
# `adb shell <cmd>; echo $?`, we would be unable to distinguish the
# output from the result for a cmd of `echo -n 1`.
- rc, out = self.device.shell_nocheck(['echo', '-n', '1'])
+ rc, out, _ = self.device.shell_nocheck(['echo', '-n', '1'])
self.assertEqual(rc, 0)
self.assertEqual(out, '1')
@@ -152,33 +365,182 @@
Bug: http://b/19735063
"""
- output = self.device.shell(['uname'])
+ output = self.device.shell(['uname'])[0]
self.assertEqual(output, 'Linux' + self.device.linesep)
def test_pty_logic(self):
- """Verify PTY logic for shells.
+ """Tests that a PTY is allocated when it should be.
- Interactive shells should use a PTY, non-interactive should not.
-
- Bug: http://b/21215503
+ PTY allocation behavior should match ssh.
"""
- proc = subprocess.Popen(
- self.device.adb_cmd + ['shell'], stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- # [ -t 0 ] is used (rather than `tty`) to provide portability. This
- # gives an exit code of 0 iff stdin is connected to a terminal.
- #
- # Closing host-side stdin doesn't currently trigger the interactive
- # shell to exit so we need to explicitly add an exit command to
- # close the session from the device side, and append \n to complete
- # the interactive command.
- result = proc.communicate('[ -t 0 ]; echo x$?; exit 0\n')[0]
- partition = result.rpartition('x')
- self.assertEqual(partition[1], 'x')
- self.assertEqual(int(partition[2]), 0)
+ def check_pty(args):
+ """Checks adb shell PTY allocation.
- exit_code = self.device.shell_nocheck(['[ -t 0 ]'])[0]
- self.assertEqual(exit_code, 1)
+ Tests |args| for terminal and non-terminal stdin.
+
+ Args:
+ args: -Tt args in a list (e.g. ['-t', '-t']).
+
+ Returns:
+ A tuple (<terminal>, <non-terminal>). True indicates
+ the corresponding shell allocated a remote PTY.
+ """
+ test_cmd = self.device.adb_cmd + ['shell'] + args + ['[ -t 0 ]']
+
+ terminal = subprocess.Popen(
+ test_cmd, stdin=None,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ terminal.communicate()
+
+ non_terminal = subprocess.Popen(
+ test_cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ non_terminal.communicate()
+
+ return (terminal.returncode == 0, non_terminal.returncode == 0)
+
+ # -T: never allocate PTY.
+ self.assertEqual((False, False), check_pty(['-T']))
+
+ # These tests require a new device.
+ if self.device.has_shell_protocol() and os.isatty(sys.stdin.fileno()):
+ # No args: PTY only if stdin is a terminal and shell is interactive,
+ # which is difficult to reliably test from a script.
+ self.assertEqual((False, False), check_pty([]))
+
+ # -t: PTY if stdin is a terminal.
+ self.assertEqual((True, False), check_pty(['-t']))
+
+ # -t -t: always allocate PTY.
+ self.assertEqual((True, True), check_pty(['-t', '-t']))
+
+ # -tt: always allocate PTY, POSIX style (http://b/32216152).
+ self.assertEqual((True, True), check_pty(['-tt']))
+
+ # -ttt: ssh has weird even/odd behavior with multiple -t flags, but
+ # we follow the man page instead.
+ self.assertEqual((True, True), check_pty(['-ttt']))
+
+ # -ttx: -x and -tt aren't incompatible (though -Tx would be an error).
+ self.assertEqual((True, True), check_pty(['-ttx']))
+
+ # -Ttt: -tt cancels out -T.
+ self.assertEqual((True, True), check_pty(['-Ttt']))
+
+ # -ttT: -T cancels out -tt.
+ self.assertEqual((False, False), check_pty(['-ttT']))
+
+ def test_shell_protocol(self):
+ """Tests the shell protocol on the device.
+
+ If the device supports shell protocol, this gives us the ability
+ to separate stdout/stderr and return the exit code directly.
+
+ Bug: http://b/19734861
+ """
+ if not self.device.has_shell_protocol():
+ raise unittest.SkipTest('shell protocol unsupported on this device')
+
+ # Shell protocol should be used by default.
+ result = self.device.shell_nocheck(
+ shlex.split('echo foo; echo bar >&2; exit 17'))
+ self.assertEqual(17, result[0])
+ self.assertEqual('foo' + self.device.linesep, result[1])
+ self.assertEqual('bar' + self.device.linesep, result[2])
+
+ self.assertEqual(17, self._interactive_shell([], 'exit 17'))
+
+ # -x flag should disable shell protocol.
+ result = self.device.shell_nocheck(
+ shlex.split('-x echo foo; echo bar >&2; exit 17'))
+ self.assertEqual(0, result[0])
+ self.assertEqual('foo{0}bar{0}'.format(self.device.linesep), result[1])
+ self.assertEqual('', result[2])
+
+ self.assertEqual(0, self._interactive_shell(['-x'], 'exit 17'))
+
+ def test_non_interactive_sigint(self):
+ """Tests that SIGINT in a non-interactive shell kills the process.
+
+ This requires the shell protocol in order to detect the broken
+ pipe; raw data transfer mode will only see the break once the
+ subprocess tries to read or write.
+
+ Bug: http://b/23825725
+ """
+ if not self.device.has_shell_protocol():
+ raise unittest.SkipTest('shell protocol unsupported on this device')
+
+ # Start a long-running process.
+ sleep_proc = subprocess.Popen(
+ self.device.adb_cmd + shlex.split('shell echo $$; sleep 60'),
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ remote_pid = sleep_proc.stdout.readline().strip()
+ self.assertIsNone(sleep_proc.returncode, 'subprocess terminated early')
+ proc_query = shlex.split('ps {0} | grep {0}'.format(remote_pid))
+
+ # Verify that the process is running, send signal, verify it stopped.
+ self.device.shell(proc_query)
+ os.kill(sleep_proc.pid, signal.SIGINT)
+ sleep_proc.communicate()
+
+ # It can take some time for the process to receive the signal and die.
+ end_time = time.time() + 3
+ while self.device.shell_nocheck(proc_query)[0] != 1:
+ self.assertFalse(time.time() > end_time,
+ 'subprocess failed to terminate in time')
+
+ def test_non_interactive_stdin(self):
+ """Tests that non-interactive shells send stdin."""
+ if not self.device.has_shell_protocol():
+ raise unittest.SkipTest('non-interactive stdin unsupported '
+ 'on this device')
+
+ # Test both small and large inputs.
+ small_input = 'foo'
+ large_input = '\n'.join(c * 100 for c in (string.ascii_letters +
+ string.digits))
+
+ for input in (small_input, large_input):
+ proc = subprocess.Popen(self.device.adb_cmd + ['shell', 'cat'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = proc.communicate(input)
+ self.assertEqual(input.splitlines(), stdout.splitlines())
+ self.assertEqual('', stderr)
+
+ def test_sighup(self):
+ """Ensure that SIGHUP gets sent upon non-interactive ctrl-c"""
+ log_path = "/data/local/tmp/adb_signal_test.log"
+
+ # Clear the output file.
+ self.device.shell_nocheck(["echo", ">", log_path])
+
+ script = """
+ trap "echo SIGINT > {path}; exit 0" SIGINT
+ trap "echo SIGHUP > {path}; exit 0" SIGHUP
+ echo Waiting
+ read
+ """.format(path=log_path)
+
+ script = ";".join([x.strip() for x in script.strip().splitlines()])
+
+ process = self.device.shell_popen([script], kill_atexit=False,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+
+ self.assertEqual("Waiting\n", process.stdout.readline())
+ process.send_signal(signal.SIGINT)
+ process.wait()
+
+ # Waiting for the local adb to finish is insufficient, since it hangs
+ # up immediately.
+ time.sleep(1)
+
+ stdout, _ = self.device.shell(["cat", log_path])
+ self.assertEqual(stdout.strip(), "SIGHUP")
class ArgumentEscapingTest(DeviceTest):
@@ -191,36 +553,44 @@
# as `sh -c echo` (with an argument to that shell of "hello"),
# and then `echo world` back in the first shell.
result = self.device.shell(
- shlex.split("sh -c 'echo hello; echo world'"))
+ shlex.split("sh -c 'echo hello; echo world'"))[0]
result = result.splitlines()
self.assertEqual(['', 'world'], result)
# If you really wanted "hello" and "world", here's what you'd do:
result = self.device.shell(
- shlex.split(r'echo hello\;echo world')).splitlines()
+ shlex.split(r'echo hello\;echo world'))[0].splitlines()
self.assertEqual(['hello', 'world'], result)
# http://b/15479704
- result = self.device.shell(shlex.split("'true && echo t'")).strip()
+ result = self.device.shell(shlex.split("'true && echo t'"))[0].strip()
self.assertEqual('t', result)
result = self.device.shell(
- shlex.split("sh -c 'true && echo t'")).strip()
+ shlex.split("sh -c 'true && echo t'"))[0].strip()
self.assertEqual('t', result)
# http://b/20564385
- result = self.device.shell(shlex.split('FOO=a BAR=b echo t')).strip()
+ result = self.device.shell(shlex.split('FOO=a BAR=b echo t'))[0].strip()
self.assertEqual('t', result)
- result = self.device.shell(shlex.split(r'echo -n 123\;uname')).strip()
+ result = self.device.shell(
+ shlex.split(r'echo -n 123\;uname'))[0].strip()
self.assertEqual('123Linux', result)
def test_install_argument_escaping(self):
"""Make sure that install argument escaping works."""
- # http://b/20323053
- tf = tempfile.NamedTemporaryFile('wb', suffix='-text;ls;1.apk')
- self.assertIn("-text;ls;1.apk", self.device.install(tf.name))
+ # http://b/20323053, http://b/3090932.
+ for file_suffix in ('-text;ls;1.apk', "-Live Hold'em.apk"):
+ tf = tempfile.NamedTemporaryFile('wb', suffix=file_suffix,
+ delete=False)
+ tf.close()
- # http://b/3090932
- tf = tempfile.NamedTemporaryFile('wb', suffix="-Live Hold'em.apk")
- self.assertIn("-Live Hold'em.apk", self.device.install(tf.name))
+ # Installing bogus .apks fails if the device supports exit codes.
+ try:
+ output = self.device.install(tf.name)
+ except subprocess.CalledProcessError as e:
+ output = e.output
+
+ self.assertIn(file_suffix, output)
+ os.remove(tf.name)
class RootUnrootTest(DeviceTest):
@@ -229,19 +599,19 @@
if 'adbd cannot run as root in production builds' in message:
return
self.device.wait()
- self.assertEqual('root', self.device.shell(['id', '-un']).strip())
+ self.assertEqual('root', self.device.shell(['id', '-un'])[0].strip())
def _test_unroot(self):
self.device.unroot()
self.device.wait()
- self.assertEqual('shell', self.device.shell(['id', '-un']).strip())
+ self.assertEqual('shell', self.device.shell(['id', '-un'])[0].strip())
def test_root_unroot(self):
"""Make sure that adb root and adb unroot work, using id(1)."""
if self.device.get_prop('ro.debuggable') != '1':
raise unittest.SkipTest('requires rootable build')
- original_user = self.device.shell(['id', '-un']).strip()
+ original_user = self.device.shell(['id', '-un'])[0].strip()
try:
if original_user == 'root':
self._test_unroot()
@@ -280,7 +650,7 @@
self.device.set_prop(prop_name, 'qux')
self.assertEqual(
- self.device.shell(['getprop', prop_name]).strip(), 'qux')
+ self.device.shell(['getprop', prop_name])[0].strip(), 'qux')
def compute_md5(string):
@@ -294,7 +664,7 @@
try:
device.shell(['md5sum', '/proc/uptime'])
return 'md5sum'
- except subprocess.CalledProcessError:
+ except adb.ShellError:
return 'md5'
@@ -332,7 +702,7 @@
return files
-def make_random_device_files(device, in_dir, num_files):
+def make_random_device_files(device, in_dir, num_files, prefix='device_tmpfile'):
min_size = 1 * (1 << 10)
max_size = 16 * (1 << 10)
@@ -340,12 +710,12 @@
for file_num in xrange(num_files):
size = random.randrange(min_size, max_size, 1024)
- base_name = 'device_tmpfile' + str(file_num)
+ base_name = prefix + str(file_num)
full_path = posixpath.join(in_dir, base_name)
device.shell(['dd', 'if=/dev/urandom', 'of={}'.format(full_path),
'bs={}'.format(size), 'count=1'])
- dev_md5, _ = device.shell([get_md5_prog(device), full_path]).split()
+ dev_md5, _ = device.shell([get_md5_prog(device), full_path])[0].split()
files.append(DeviceFile(dev_md5, full_path))
return files
@@ -356,13 +726,15 @@
DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
- def _test_push(self, local_file, checksum):
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
- self.device.push(local=local_file, remote=self.DEVICE_TEMP_FILE)
+ def _verify_remote(self, checksum, remote_path):
dev_md5, _ = self.device.shell([get_md5_prog(self.device),
- self.DEVICE_TEMP_FILE]).split()
+ remote_path])[0].split()
self.assertEqual(checksum, dev_md5)
- self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+
+ def _verify_local(self, checksum, local_path):
+ with open(local_path, 'rb') as host_file:
+ host_md5 = compute_md5(host_file.read())
+ self.assertEqual(host_md5, checksum)
def test_push(self):
"""Push a randomly generated file to specified device."""
@@ -371,10 +743,155 @@
rand_str = os.urandom(1024 * kbytes)
tmp.write(rand_str)
tmp.close()
- self._test_push(tmp.name, compute_md5(rand_str))
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+ self.device.push(local=tmp.name, remote=self.DEVICE_TEMP_FILE)
+
+ self._verify_remote(compute_md5(rand_str), self.DEVICE_TEMP_FILE)
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+
os.remove(tmp.name)
- # TODO: write push directory test.
+ def test_push_dir(self):
+ """Push a randomly generated directory of files to the device."""
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ # Make sure the temp directory isn't setuid, or else adb will complain.
+ os.chmod(host_dir, 0o700)
+
+ # Create 32 random files.
+ temp_files = make_random_host_files(in_dir=host_dir, num_files=32)
+ self.device.push(host_dir, self.DEVICE_TEMP_DIR)
+
+ for temp_file in temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ os.path.basename(host_dir),
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ @unittest.expectedFailure # b/25566053
+ def test_push_empty(self):
+ """Push a directory containing an empty directory to the device."""
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ # Make sure the temp directory isn't setuid, or else adb will complain.
+ os.chmod(host_dir, 0o700)
+
+ # Create an empty directory.
+ os.mkdir(os.path.join(host_dir, 'empty'))
+
+ self.device.push(host_dir, self.DEVICE_TEMP_DIR)
+
+ test_empty_cmd = ['[', '-d',
+ os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
+ rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
+ self.assertEqual(rc, 0)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ @unittest.skipIf(sys.platform == "win32", "symlinks require elevated privileges on windows")
+ def test_push_symlink(self):
+ """Push a symlink.
+
+ Bug: http://b/31491920
+ """
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ # Make sure the temp directory isn't setuid, or else adb will
+ # complain.
+ os.chmod(host_dir, 0o700)
+
+ with open(os.path.join(host_dir, 'foo'), 'w') as f:
+ f.write('foo')
+
+ symlink_path = os.path.join(host_dir, 'symlink')
+ os.symlink('foo', symlink_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+ self.device.push(symlink_path, self.DEVICE_TEMP_DIR)
+ rc, out, _ = self.device.shell_nocheck(
+ ['cat', posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')])
+ self.assertEqual(0, rc)
+ self.assertEqual(out.strip(), 'foo')
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_multiple_push(self):
+ """Push multiple files to the device in one adb push command.
+
+ Bug: http://b/25324823
+ """
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ # Create some random files and a subdirectory containing more files.
+ temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
+
+ subdir = os.path.join(host_dir, 'subdir')
+ os.mkdir(subdir)
+ subdir_temp_files = make_random_host_files(in_dir=subdir,
+ num_files=4)
+
+ paths = map(lambda temp_file: temp_file.full_path, temp_files)
+ paths.append(subdir)
+ self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
+
+ for temp_file in temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+
+ for subdir_temp_file in subdir_temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ # BROKEN: http://b/25394682
+ # 'subdir';
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ @requires_non_root
+ def test_push_error_reporting(self):
+ """Make sure that errors that occur while pushing a file get reported
+
+ Bug: http://b/26816782
+ """
+ with tempfile.NamedTemporaryFile() as tmp_file:
+ tmp_file.write('\0' * 1024 * 1024)
+ tmp_file.flush()
+ try:
+ self.device.push(local=tmp_file.name, remote='/system/')
+ self.fail('push should not have succeeded')
+ except subprocess.CalledProcessError as e:
+ output = e.output
+
+ self.assertTrue('Permission denied' in output or
+ 'Read-only file system' in output)
def _test_pull(self, remote_file, checksum):
tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
@@ -386,6 +903,20 @@
self.assertEqual(checksum, host_md5)
os.remove(tmp_write.name)
+ @requires_non_root
+ def test_pull_error_reporting(self):
+ self.device.shell(['touch', self.DEVICE_TEMP_FILE])
+ self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
+
+ try:
+ output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
+ except subprocess.CalledProcessError as e:
+ output = e.output
+
+ self.assertIn('Permission denied', output)
+
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+
def test_pull(self):
"""Pull a randomly generated file from specified device."""
kbytes = 512
@@ -395,75 +926,266 @@
'count={}'.format(kbytes)]
self.device.shell(cmd)
dev_md5, _ = self.device.shell(
- [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
+ [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
def test_pull_dir(self):
"""Pull a randomly generated directory of files from the device."""
- host_dir = tempfile.mkdtemp()
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+ try:
+ host_dir = tempfile.mkdtemp()
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
- for temp_file in temp_files:
- host_path = os.path.join(host_dir, temp_file.base_name)
- with open(host_path, 'rb') as host_file:
- host_md5 = compute_md5(host_file.read())
- self.assertEqual(host_md5, temp_file.checksum)
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
+ temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_dir_symlink(self):
+ """Pull a directory into a symlink to a directory.
+
+ Bug: http://b/27362811
+ """
+ if os.name != 'posix':
+ raise unittest.SkipTest('requires POSIX')
+
+ try:
+ host_dir = tempfile.mkdtemp()
+ real_dir = os.path.join(host_dir, 'dir')
+ symlink = os.path.join(host_dir, 'symlink')
+ os.mkdir(real_dir)
+ os.symlink(real_dir, symlink)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
+ temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_dir_symlink_collision(self):
+ """Pull a directory into a colliding symlink to directory."""
+ if os.name != 'posix':
+ raise unittest.SkipTest('requires POSIX')
+
+ try:
+ host_dir = tempfile.mkdtemp()
+ real_dir = os.path.join(host_dir, 'real')
+ tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
+ symlink = os.path.join(host_dir, tmp_dirname)
+ os.mkdir(real_dir)
+ os.symlink(real_dir, symlink)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(real_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_dir_nonexistent(self):
+ """Pull a directory of files from the device to a nonexistent path."""
+ try:
+ host_dir = tempfile.mkdtemp()
+ dest_dir = os.path.join(host_dir, 'dest')
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(dest_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_symlink_dir(self):
+ """Pull a symlink to a directory of symlinks to files."""
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
+ remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
+ remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', remote_dir, remote_links])
+ self.device.shell(['ln', '-s', remote_links, remote_symlink])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=remote_dir, num_files=32)
+
+ for temp_file in temp_files:
+ self.device.shell(
+ ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
+ posixpath.join(remote_links, temp_file.base_name)])
+
+ self.device.pull(remote=remote_symlink, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ host_dir, 'symlink', temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_empty(self):
+ """Pull a directory containing an empty directory from the device."""
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', remote_empty_path])
+
+ self.device.pull(remote=remote_empty_path, local=host_dir)
+ self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_multiple_pull(self):
+ """Pull a randomly generated directory of files from the device."""
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', subdir])
+
+ # Create some random files and a subdirectory containing more files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
+
+ subdir_temp_files = make_random_device_files(
+ self.device, in_dir=subdir, num_files=4, prefix='subdir_')
+
+ paths = map(lambda temp_file: temp_file.full_path, temp_files)
+ paths.append(subdir)
+ self.device._simple_call(['pull'] + paths + [host_dir])
+
+ for temp_file in temp_files:
+ local_path = os.path.join(host_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, local_path)
+
+ for subdir_temp_file in subdir_temp_files:
+ local_path = os.path.join(host_dir,
+ 'subdir',
+ subdir_temp_file.base_name)
+ self._verify_local(subdir_temp_file.checksum, local_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
def test_sync(self):
"""Sync a randomly generated directory of files to specified device."""
- base_dir = tempfile.mkdtemp()
- # Create mirror device directory hierarchy within base_dir.
- full_dir_path = base_dir + self.DEVICE_TEMP_DIR
- os.makedirs(full_dir_path)
+ try:
+ base_dir = tempfile.mkdtemp()
- # Create 32 random files within the host mirror.
- temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
+ # Create mirror device directory hierarchy within base_dir.
+ full_dir_path = base_dir + self.DEVICE_TEMP_DIR
+ os.makedirs(full_dir_path)
- # Clean up any trash on the device.
- device = adb.get_device(product=base_dir)
- device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ # Create 32 random files within the host mirror.
+ temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
- device.sync('data')
+ # Clean up any trash on the device.
+ device = adb.get_device(product=base_dir)
+ device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- # Confirm that every file on the device mirrors that on the host.
- for temp_file in temp_files:
- device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
- temp_file.base_name)
- dev_md5, _ = device.shell(
- [get_md5_prog(self.device), device_full_path]).split()
- self.assertEqual(temp_file.checksum, dev_md5)
+ device.sync('data')
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- shutil.rmtree(base_dir + self.DEVICE_TEMP_DIR)
+ # Confirm that every file on the device mirrors that on the host.
+ for temp_file in temp_files:
+ device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ temp_file.base_name)
+ dev_md5, _ = device.shell(
+ [get_md5_prog(self.device), device_full_path])[0].split()
+ self.assertEqual(temp_file.checksum, dev_md5)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if base_dir is not None:
+ shutil.rmtree(base_dir)
def test_unicode_paths(self):
"""Ensure that we can support non-ASCII paths, even on Windows."""
- name = u'로보카 폴리'.encode('utf-8')
+ name = u'로보카 폴리'
+
+ self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+ remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
## push.
- tf = tempfile.NamedTemporaryFile('wb', suffix=name)
- self.device.push(tf.name, '/data/local/tmp/adb-test-{}'.format(name))
- self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+ tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
+ tf.close()
+ self.device.push(tf.name, remote_path)
+ os.remove(tf.name)
+ self.assertFalse(os.path.exists(tf.name))
+
+ # Verify that the device ended up with the expected UTF-8 path
+ output = self.device.shell(
+ ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
+ self.assertEqual(remote_path.encode('utf-8'), output)
# pull.
- cmd = ['touch', '"/data/local/tmp/adb-test-{}"'.format(name)]
- self.device.shell(cmd)
-
- tf = tempfile.NamedTemporaryFile('wb', suffix=name)
- self.device.pull('/data/local/tmp/adb-test-{}'.format(name), tf.name)
+ self.device.pull(remote_path, tf.name)
+ self.assertTrue(os.path.exists(tf.name))
+ os.remove(tf.name)
+ self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
def main():
diff --git a/adb/test_track_devices.cpp b/adb/test_track_devices.cpp
deleted file mode 100644
index 6f658f6..0000000
--- a/adb/test_track_devices.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-// TODO: replace this with a shell/python script.
-
-/* a simple test program, connects to ADB server, and opens a track-devices session */
-#include <errno.h>
-#include <memory.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include <base/file.h>
-
-static void
-panic( const char* msg )
-{
- fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
- exit(1);
-}
-
-int main(int argc, char* argv[]) {
- const char* request = "host:track-devices";
-
- if (argv[1] && strcmp(argv[1], "--jdwp") == 0) {
- request = "track-jdwp";
- }
-
- int ret;
- struct sockaddr_in server;
- char buffer[1024];
-
- memset( &server, 0, sizeof(server) );
- server.sin_family = AF_INET;
- server.sin_port = htons(5037);
- server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- int s = socket( PF_INET, SOCK_STREAM, 0 );
- ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
- if (ret < 0) panic( "could not connect to server" );
-
- /* send the request */
- int len = snprintf(buffer, sizeof(buffer), "%04zx%s", strlen(request), request);
- if (!android::base::WriteFully(s, buffer, len))
- panic( "could not send request" );
-
- /* read the OKAY answer */
- if (!android::base::ReadFully(s, buffer, 4))
- panic( "could not read request" );
-
- printf( "server answer: %.*s\n", 4, buffer );
-
- /* now loop */
- while (true) {
- char head[5] = "0000";
-
- if (!android::base::ReadFully(s, head, 4))
- panic("could not read length");
-
- int len;
- if (sscanf(head, "%04x", &len) != 1 )
- panic("could not decode length");
-
- if (!android::base::ReadFully(s, buffer, len))
- panic("could not read data");
-
- printf( "received header %.*s (%d bytes):\n%.*s----\n", 4, head, len, len, buffer );
- }
- close(s);
-}
diff --git a/adb/trace.sh b/adb/trace.sh
new file mode 100755
index 0000000..49e5026
--- /dev/null
+++ b/adb/trace.sh
@@ -0,0 +1,17 @@
+set -e
+
+if ! [ -e $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py ]; then
+ echo "error: can't find systrace.py at \$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py"
+ exit 1
+fi
+
+adb shell "sleep 1; atrace -b 65536 --async_start adb sched power freq idle disk mmc load"
+adb shell killall adbd
+adb wait-for-device
+echo "press enter to finish..."
+read
+TRACE_TEMP=`mktemp /tmp/trace.XXXXXX`
+echo Saving trace to ${TRACE_TEMP}, html file to ${TRACE_TEMP}.html
+adb shell atrace --async_stop -z > ${TRACE_TEMP}
+$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=${TRACE_TEMP} -o ${TRACE_TEMP}.html
+chrome ${TRACE_TEMP}.html
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 2ea4d44..60f3b5c 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_TRANSPORT
+#define TRACE_TAG TRANSPORT
#include "sysdeps.h"
#include "transport.h"
@@ -26,79 +26,42 @@
#include <string.h>
#include <unistd.h>
+#include <algorithm>
#include <list>
+#include <mutex>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "adb.h"
+#include "adb_auth.h"
+#include "adb_trace.h"
#include "adb_utils.h"
+#include "diagnose_usb.h"
static void transport_unref(atransport *t);
-static std::list<atransport*> transport_list;
-static std::list<atransport*> pending_list;
+static auto& transport_list = *new std::list<atransport*>();
+static auto& pending_list = *new std::list<atransport*>();
-ADB_MUTEX_DEFINE( transport_lock );
+static std::mutex& transport_lock = *new std::mutex();
-void kick_transport(atransport* t)
-{
- if (t && !t->kicked)
- {
- int kicked;
+const char* const kFeatureShell2 = "shell_v2";
+const char* const kFeatureCmd = "cmd";
+const char* const kFeatureStat2 = "stat_v2";
- adb_mutex_lock(&transport_lock);
- kicked = t->kicked;
- if (!kicked)
- t->kicked = 1;
- adb_mutex_unlock(&transport_lock);
-
- if (!kicked)
- t->kick(t);
- }
-}
-
-// Each atransport contains a list of adisconnects (t->disconnects).
-// An adisconnect contains a link to the next/prev adisconnect, a function
-// pointer to a disconnect callback which takes a void* piece of user data and
-// the atransport, and some user data for the callback (helpfully named
-// "opaque").
-//
-// The list is circular. New items are added to the entry member of the list
-// (t->disconnects) by add_transport_disconnect.
-//
-// run_transport_disconnects invokes each function in the list.
-//
-// Gotchas:
-// * run_transport_disconnects assumes that t->disconnects is non-null, so
-// this can't be run on a zeroed atransport.
-// * The callbacks in this list are not removed when called, and this function
-// is not guarded against running more than once. As such, ensure that this
-// function is not called multiple times on the same atransport.
-// TODO(danalbert): Just fix this so that it is guarded once you have tests.
-void run_transport_disconnects(atransport* t)
-{
- adisconnect* dis = t->disconnects.next;
-
- D("%s: run_transport_disconnects\n", t->serial);
- while (dis != &t->disconnects) {
- adisconnect* next = dis->next;
- dis->func( dis->opaque, t );
- dis = next;
- }
-}
-
-static void dump_packet(const char* name, const char* func, apacket* p) {
- unsigned command = p->msg.command;
- int len = p->msg.data_length;
- char cmd[9];
- char arg0[12], arg1[12];
- int n;
+static std::string dump_packet(const char* name, const char* func, apacket* p) {
+ unsigned command = p->msg.command;
+ int len = p->msg.data_length;
+ char cmd[9];
+ char arg0[12], arg1[12];
+ int n;
for (n = 0; n < 4; n++) {
- int b = (command >> (n*8)) & 255;
- if (b < 32 || b >= 127)
- break;
+ int b = (command >> (n * 8)) & 255;
+ if (b < 32 || b >= 127) break;
cmd[n] = (char)b;
}
if (n == 4) {
@@ -119,235 +82,220 @@
else
snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
- D("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ",
- name, func, cmd, arg0, arg1, len);
- dump_hex(p->data, len);
+ std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name,
+ func, cmd, arg0, arg1, len);
+ result += dump_hex(p->data, len);
+ return result;
}
-static int
-read_packet(int fd, const char* name, apacket** ppacket)
-{
+static int read_packet(int fd, const char* name, apacket** ppacket) {
+ ATRACE_NAME("read_packet");
char buff[8];
if (!name) {
snprintf(buff, sizeof buff, "fd=%d", fd);
name = buff;
}
- char* p = reinterpret_cast<char*>(ppacket); /* really read a packet address */
+ char* p = reinterpret_cast<char*>(ppacket); /* really read a packet address */
int len = sizeof(apacket*);
- while(len > 0) {
+ while (len > 0) {
int r = adb_read(fd, p, len);
- if(r > 0) {
+ if (r > 0) {
len -= r;
p += r;
} else {
- D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
- if((r < 0) && (errno == EINTR)) continue;
+ D("%s: read_packet (fd=%d), error ret=%d: %s", name, fd, r, strerror(errno));
return -1;
}
}
- if (ADB_TRACING) {
- dump_packet(name, "from remote", *ppacket);
- }
+ VLOG(TRANSPORT) << dump_packet(name, "from remote", *ppacket);
return 0;
}
-static int
-write_packet(int fd, const char* name, apacket** ppacket)
-{
+static int write_packet(int fd, const char* name, apacket** ppacket) {
+ ATRACE_NAME("write_packet");
char buff[8];
if (!name) {
snprintf(buff, sizeof buff, "fd=%d", fd);
name = buff;
}
- if (ADB_TRACING) {
- dump_packet(name, "to remote", *ppacket);
- }
- char* p = reinterpret_cast<char*>(ppacket); /* we really write the packet address */
+ VLOG(TRANSPORT) << dump_packet(name, "to remote", *ppacket);
+ char* p = reinterpret_cast<char*>(ppacket); /* we really write the packet address */
int len = sizeof(apacket*);
- while(len > 0) {
+ while (len > 0) {
int r = adb_write(fd, p, len);
- if(r > 0) {
+ if (r > 0) {
len -= r;
p += r;
} else {
- D("%s: write_packet (fd=%d) error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
- if((r < 0) && (errno == EINTR)) continue;
+ D("%s: write_packet (fd=%d) error ret=%d: %s", name, fd, r, strerror(errno));
return -1;
}
}
return 0;
}
-static void transport_socket_events(int fd, unsigned events, void *_t)
-{
- atransport *t = reinterpret_cast<atransport*>(_t);
- D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events);
- if(events & FDE_READ){
- apacket *p = 0;
- if(read_packet(fd, t->serial, &p)){
- D("%s: failed to read packet from transport socket on fd %d\n", t->serial, fd);
+static void transport_socket_events(int fd, unsigned events, void* _t) {
+ atransport* t = reinterpret_cast<atransport*>(_t);
+ D("transport_socket_events(fd=%d, events=%04x,...)", fd, events);
+ if (events & FDE_READ) {
+ apacket* p = 0;
+ if (read_packet(fd, t->serial, &p)) {
+ D("%s: failed to read packet from transport socket on fd %d", t->serial, fd);
} else {
- handle_packet(p, (atransport *) _t);
+ handle_packet(p, (atransport*)_t);
}
}
}
-void send_packet(apacket *p, atransport *t)
-{
- unsigned char *x;
- unsigned sum;
- unsigned count;
-
+void send_packet(apacket* p, atransport* t) {
p->msg.magic = p->msg.command ^ 0xffffffff;
-
- count = p->msg.data_length;
- x = (unsigned char *) p->data;
- sum = 0;
- while(count-- > 0){
- sum += *x++;
- }
- p->msg.data_check = sum;
+ p->msg.data_check = calculate_apacket_checksum(p);
print_packet("send", p);
if (t == NULL) {
- D("Transport is null \n");
- // Zap errno because print_packet() and other stuff have errno effect.
- errno = 0;
- fatal_errno("Transport is null");
+ fatal("Transport is null");
}
- if(write_packet(t->transport_socket, t->serial, &p)){
+ if (write_packet(t->transport_socket, t->serial, &p)) {
fatal_errno("cannot enqueue packet on transport socket");
}
}
-/* The transport is opened by transport_register_func before
-** the input and output threads are started.
-**
-** The output thread issues a SYNC(1, token) message to let
-** the input thread know to start things up. In the event
-** of transport IO failure, the output thread will post a
-** SYNC(0,0) message to ensure shutdown.
-**
-** The transport will not actually be closed until both
-** threads exit, but the input thread will kick the transport
-** on its way out to disconnect the underlying device.
-*/
+// The transport is opened by transport_register_func before
+// the read_transport and write_transport threads are started.
+//
+// The read_transport thread issues a SYNC(1, token) message to let
+// the write_transport thread know to start things up. In the event
+// of transport IO failure, the read_transport thread will post a
+// SYNC(0,0) message to ensure shutdown.
+//
+// The transport will not actually be closed until both threads exit, but the threads
+// will kick the transport on their way out to disconnect the underlying device.
+//
+// read_transport thread reads data from a transport (representing a usb/tcp connection),
+// and makes the main thread call handle_packet().
+static void read_transport_thread(void* _t) {
+ atransport* t = reinterpret_cast<atransport*>(_t);
+ apacket* p;
-static void *output_thread(void *_t)
-{
- atransport *t = reinterpret_cast<atransport*>(_t);
- apacket *p;
-
- D("%s: starting transport output thread on fd %d, SYNC online (%d)\n",
- t->serial, t->fd, t->sync_token + 1);
+ adb_thread_setname(
+ android::base::StringPrintf("<-%s", (t->serial != nullptr ? t->serial : "transport")));
+ D("%s: starting read_transport thread on fd %d, SYNC online (%d)", t->serial, t->fd,
+ t->sync_token + 1);
p = get_apacket();
p->msg.command = A_SYNC;
p->msg.arg0 = 1;
p->msg.arg1 = ++(t->sync_token);
p->msg.magic = A_SYNC ^ 0xffffffff;
- if(write_packet(t->fd, t->serial, &p)) {
+ if (write_packet(t->fd, t->serial, &p)) {
put_apacket(p);
- D("%s: failed to write SYNC packet\n", t->serial);
+ D("%s: failed to write SYNC packet", t->serial);
goto oops;
}
- D("%s: data pump started\n", t->serial);
- for(;;) {
+ D("%s: data pump started", t->serial);
+ for (;;) {
+ ATRACE_NAME("read_transport loop");
p = get_apacket();
- if(t->read_from_remote(p, t) == 0){
- D("%s: received remote packet, sending to transport\n",
- t->serial);
- if(write_packet(t->fd, t->serial, &p)){
+ {
+ ATRACE_NAME("read_transport read_remote");
+ if (t->read_from_remote(p, t) != 0) {
+ D("%s: remote read failed for transport", t->serial);
put_apacket(p);
- D("%s: failed to write apacket to transport\n", t->serial);
- goto oops;
+ break;
}
- } else {
- D("%s: remote read failed for transport\n", t->serial);
+ }
+
+ D("%s: received remote packet, sending to transport", t->serial);
+ if (write_packet(t->fd, t->serial, &p)) {
put_apacket(p);
- break;
+ D("%s: failed to write apacket to transport", t->serial);
+ goto oops;
}
}
- D("%s: SYNC offline for transport\n", t->serial);
+ D("%s: SYNC offline for transport", t->serial);
p = get_apacket();
p->msg.command = A_SYNC;
p->msg.arg0 = 0;
p->msg.arg1 = 0;
p->msg.magic = A_SYNC ^ 0xffffffff;
- if(write_packet(t->fd, t->serial, &p)) {
+ if (write_packet(t->fd, t->serial, &p)) {
put_apacket(p);
- D("%s: failed to write SYNC apacket to transport\n", t->serial);
+ D("%s: failed to write SYNC apacket to transport", t->serial);
}
oops:
- D("%s: transport output thread is exiting\n", t->serial);
+ D("%s: read_transport thread is exiting", t->serial);
kick_transport(t);
transport_unref(t);
- return 0;
}
-static void *input_thread(void *_t)
-{
- atransport *t = reinterpret_cast<atransport*>(_t);
- apacket *p;
+// write_transport thread gets packets sent by the main thread (through send_packet()),
+// and writes to a transport (representing a usb/tcp connection).
+static void write_transport_thread(void* _t) {
+ atransport* t = reinterpret_cast<atransport*>(_t);
+ apacket* p;
int active = 0;
- D("%s: starting transport input thread, reading from fd %d\n",
- t->serial, t->fd);
+ adb_thread_setname(
+ android::base::StringPrintf("->%s", (t->serial != nullptr ? t->serial : "transport")));
+ D("%s: starting write_transport thread, reading from fd %d", t->serial, t->fd);
- for(;;){
- if(read_packet(t->fd, t->serial, &p)) {
- D("%s: failed to read apacket from transport on fd %d\n",
- t->serial, t->fd );
+ for (;;) {
+ ATRACE_NAME("write_transport loop");
+ if (read_packet(t->fd, t->serial, &p)) {
+ D("%s: failed to read apacket from transport on fd %d", t->serial, t->fd);
break;
}
- if(p->msg.command == A_SYNC){
- if(p->msg.arg0 == 0) {
- D("%s: transport SYNC offline\n", t->serial);
+
+ if (p->msg.command == A_SYNC) {
+ if (p->msg.arg0 == 0) {
+ D("%s: transport SYNC offline", t->serial);
put_apacket(p);
break;
} else {
- if(p->msg.arg1 == t->sync_token) {
- D("%s: transport SYNC online\n", t->serial);
+ if (p->msg.arg1 == t->sync_token) {
+ D("%s: transport SYNC online", t->serial);
active = 1;
} else {
- D("%s: transport ignoring SYNC %d != %d\n",
- t->serial, p->msg.arg1, t->sync_token);
+ D("%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token);
}
}
} else {
- if(active) {
- D("%s: transport got packet, sending to remote\n", t->serial);
+ if (active) {
+ D("%s: transport got packet, sending to remote", t->serial);
+ ATRACE_NAME("write_transport write_remote");
t->write_to_remote(p, t);
} else {
- D("%s: transport ignoring packet while offline\n", t->serial);
+ D("%s: transport ignoring packet while offline", t->serial);
}
}
put_apacket(p);
}
- // this is necessary to avoid a race condition that occured when a transport closes
- // while a client socket is still active.
- close_all_sockets(t);
-
- D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd);
+ D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd);
kick_transport(t);
transport_unref(t);
- return 0;
}
+void kick_transport(atransport* t) {
+ std::lock_guard<std::mutex> lock(transport_lock);
+ // As kick_transport() can be called from threads without guarantee that t is valid,
+ // check if the transport is in transport_list first.
+ if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
+ t->Kick();
+ }
+}
static int transport_registration_send = -1;
static int transport_registration_recv = -1;
static fdevent transport_registration_fde;
-
#if ADB_HOST
/* this adds support required by the 'track-devices' service.
@@ -356,39 +304,34 @@
* live TCP connection
*/
struct device_tracker {
- asocket socket;
- int update_needed;
- device_tracker* next;
+ asocket socket;
+ int update_needed;
+ device_tracker* next;
};
/* linked list of all device trackers */
-static device_tracker* device_tracker_list;
+static device_tracker* device_tracker_list;
-static void
-device_tracker_remove( device_tracker* tracker )
-{
- device_tracker** pnode = &device_tracker_list;
- device_tracker* node = *pnode;
+static void device_tracker_remove(device_tracker* tracker) {
+ device_tracker** pnode = &device_tracker_list;
+ device_tracker* node = *pnode;
- adb_mutex_lock( &transport_lock );
+ std::lock_guard<std::mutex> lock(transport_lock);
while (node) {
if (node == tracker) {
*pnode = node->next;
break;
}
pnode = &node->next;
- node = *pnode;
+ node = *pnode;
}
- adb_mutex_unlock( &transport_lock );
}
-static void
-device_tracker_close( asocket* socket )
-{
- device_tracker* tracker = (device_tracker*) socket;
- asocket* peer = socket->peer;
+static void device_tracker_close(asocket* socket) {
+ device_tracker* tracker = (device_tracker*)socket;
+ asocket* peer = socket->peer;
- D( "device tracker %p removed\n", tracker);
+ D("device tracker %p removed", tracker);
if (peer) {
peer->peer = NULL;
peer->close(peer);
@@ -397,9 +340,7 @@
free(tracker);
}
-static int
-device_tracker_enqueue( asocket* socket, apacket* p )
-{
+static int device_tracker_enqueue(asocket* socket, apacket* p) {
/* you can't read from a device tracker, close immediately */
put_apacket(p);
device_tracker_close(socket);
@@ -429,26 +370,23 @@
}
}
-asocket*
-create_device_tracker(void)
-{
+asocket* create_device_tracker(void) {
device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
if (tracker == nullptr) fatal("cannot allocate device tracker");
- D( "device tracker %p created\n", tracker);
+ D("device tracker %p created", tracker);
tracker->socket.enqueue = device_tracker_enqueue;
- tracker->socket.ready = device_tracker_ready;
- tracker->socket.close = device_tracker_close;
- tracker->update_needed = 1;
+ tracker->socket.ready = device_tracker_ready;
+ tracker->socket.close = device_tracker_close;
+ tracker->update_needed = 1;
- tracker->next = device_tracker_list;
+ tracker->next = device_tracker_list;
device_tracker_list = tracker;
return &tracker->socket;
}
-
// Call this function each time the transport list has changed.
void update_transports() {
std::string transports = list_transports(false);
@@ -468,99 +406,83 @@
// Nothing to do on the device side.
}
-#endif // ADB_HOST
+#endif // ADB_HOST
-struct tmsg
-{
- atransport *transport;
- int action;
+struct tmsg {
+ atransport* transport;
+ int action;
};
-static int
-transport_read_action(int fd, struct tmsg* m)
-{
- char *p = (char*)m;
- int len = sizeof(*m);
- int r;
+static int transport_read_action(int fd, struct tmsg* m) {
+ char* p = (char*)m;
+ int len = sizeof(*m);
+ int r;
- while(len > 0) {
+ while (len > 0) {
r = adb_read(fd, p, len);
- if(r > 0) {
+ if (r > 0) {
len -= r;
- p += r;
+ p += r;
} else {
- if((r < 0) && (errno == EINTR)) continue;
- D("transport_read_action: on fd %d, error %d: %s\n",
- fd, errno, strerror(errno));
+ D("transport_read_action: on fd %d: %s", fd, strerror(errno));
return -1;
}
}
return 0;
}
-static int
-transport_write_action(int fd, struct tmsg* m)
-{
- char *p = (char*)m;
- int len = sizeof(*m);
- int r;
+static int transport_write_action(int fd, struct tmsg* m) {
+ char* p = (char*)m;
+ int len = sizeof(*m);
+ int r;
- while(len > 0) {
+ while (len > 0) {
r = adb_write(fd, p, len);
- if(r > 0) {
+ if (r > 0) {
len -= r;
- p += r;
+ p += r;
} else {
- if((r < 0) && (errno == EINTR)) continue;
- D("transport_write_action: on fd %d, error %d: %s\n",
- fd, errno, strerror(errno));
+ D("transport_write_action: on fd %d: %s", fd, strerror(errno));
return -1;
}
}
return 0;
}
-static void transport_registration_func(int _fd, unsigned ev, void *data)
-{
+static void transport_registration_func(int _fd, unsigned ev, void* data) {
tmsg m;
int s[2];
- atransport *t;
+ atransport* t;
- if(!(ev & FDE_READ)) {
+ if (!(ev & FDE_READ)) {
return;
}
- if(transport_read_action(_fd, &m)) {
+ if (transport_read_action(_fd, &m)) {
fatal_errno("cannot read transport registration socket");
}
t = m.transport;
if (m.action == 0) {
- D("transport: %s removing and free'ing %d\n", t->serial, t->transport_socket);
+ D("transport: %s removing and free'ing %d", t->serial, t->transport_socket);
- /* IMPORTANT: the remove closes one half of the
- ** socket pair. The close closes the other half.
- */
+ /* IMPORTANT: the remove closes one half of the
+ ** socket pair. The close closes the other half.
+ */
fdevent_remove(&(t->transport_fde));
adb_close(t->fd);
- adb_mutex_lock(&transport_lock);
- transport_list.remove(t);
- adb_mutex_unlock(&transport_lock);
+ {
+ std::lock_guard<std::mutex> lock(transport_lock);
+ transport_list.remove(t);
+ }
- run_transport_disconnects(t);
-
- if (t->product)
- free(t->product);
- if (t->serial)
- free(t->serial);
- if (t->model)
- free(t->model);
- if (t->device)
- free(t->device);
- if (t->devpath)
- free(t->devpath);
+ if (t->product) free(t->product);
+ if (t->serial) free(t->serial);
+ if (t->model) free(t->model);
+ if (t->device) free(t->device);
+ if (t->devpath) free(t->devpath);
delete t;
@@ -577,178 +499,139 @@
fatal_errno("cannot open transport socketpair");
}
- D("transport: %s socketpair: (%d,%d) starting\n", t->serial, s[0], s[1]);
+ D("transport: %s socketpair: (%d,%d) starting", t->serial, s[0], s[1]);
t->transport_socket = s[0];
t->fd = s[1];
- fdevent_install(&(t->transport_fde),
- t->transport_socket,
- transport_socket_events,
- t);
+ fdevent_install(&(t->transport_fde), t->transport_socket, transport_socket_events, t);
fdevent_set(&(t->transport_fde), FDE_READ);
- if (!adb_thread_create(input_thread, t)) {
- fatal_errno("cannot create input thread");
+ if (!adb_thread_create(write_transport_thread, t)) {
+ fatal_errno("cannot create write_transport thread");
}
- if (!adb_thread_create(output_thread, t)) {
- fatal_errno("cannot create output thread");
+ if (!adb_thread_create(read_transport_thread, t)) {
+ fatal_errno("cannot create read_transport thread");
}
}
- adb_mutex_lock(&transport_lock);
- pending_list.remove(t);
- transport_list.push_front(t);
- adb_mutex_unlock(&transport_lock);
-
- t->disconnects.next = t->disconnects.prev = &t->disconnects;
+ {
+ std::lock_guard<std::mutex> lock(transport_lock);
+ pending_list.remove(t);
+ transport_list.push_front(t);
+ }
update_transports();
}
-void init_transport_registration(void)
-{
+void init_transport_registration(void) {
int s[2];
- if(adb_socketpair(s)){
+ if (adb_socketpair(s)) {
fatal_errno("cannot open transport registration socketpair");
}
- D("socketpair: (%d,%d)\n", s[0], s[1]);
+ D("socketpair: (%d,%d)", s[0], s[1]);
transport_registration_send = s[0];
transport_registration_recv = s[1];
- fdevent_install(&transport_registration_fde,
- transport_registration_recv,
- transport_registration_func,
- 0);
+ fdevent_install(&transport_registration_fde, transport_registration_recv,
+ transport_registration_func, 0);
fdevent_set(&transport_registration_fde, FDE_READ);
}
/* the fdevent select pump is single threaded */
-static void register_transport(atransport *transport)
-{
+static void register_transport(atransport* transport) {
tmsg m;
m.transport = transport;
m.action = 1;
- D("transport: %s registered\n", transport->serial);
- if(transport_write_action(transport_registration_send, &m)) {
+ D("transport: %s registered", transport->serial);
+ if (transport_write_action(transport_registration_send, &m)) {
fatal_errno("cannot write transport registration socket\n");
}
}
-static void remove_transport(atransport *transport)
-{
+static void remove_transport(atransport* transport) {
tmsg m;
m.transport = transport;
m.action = 0;
- D("transport: %s removed\n", transport->serial);
- if(transport_write_action(transport_registration_send, &m)) {
+ D("transport: %s removed", transport->serial);
+ if (transport_write_action(transport_registration_send, &m)) {
fatal_errno("cannot write transport registration socket\n");
}
}
+static void transport_unref(atransport* t) {
+ CHECK(t != nullptr);
-static void transport_unref_locked(atransport *t)
-{
+ std::lock_guard<std::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)\n", t->serial);
- if (!t->kicked) {
- t->kicked = 1;
- t->kick(t);
- }
+ D("transport: %s unref (kicking and closing)", t->serial);
t->close(t);
remove_transport(t);
} else {
- D("transport: %s unref (count=%d)\n", t->serial, t->ref_count);
+ D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
}
}
-static void transport_unref(atransport *t)
-{
- if (t) {
- adb_mutex_lock(&transport_lock);
- transport_unref_locked(t);
- adb_mutex_unlock(&transport_lock);
- }
-}
-
-void add_transport_disconnect(atransport* t, adisconnect* dis)
-{
- adb_mutex_lock(&transport_lock);
- dis->next = &t->disconnects;
- dis->prev = dis->next->prev;
- dis->prev->next = dis;
- dis->next->prev = dis;
- adb_mutex_unlock(&transport_lock);
-}
-
-void remove_transport_disconnect(atransport* t, adisconnect* dis)
-{
- dis->prev->next = dis->next;
- dis->next->prev = dis->prev;
- dis->next = dis->prev = dis;
-}
-
-static int qual_match(const char *to_test,
- const char *prefix, const char *qual, bool sanitize_qual)
-{
- if (!to_test || !*to_test)
- /* Return true if both the qual and to_test are null strings. */
+static int qual_match(const char* to_test, const char* prefix, const char* qual,
+ bool sanitize_qual) {
+ if (!to_test || !*to_test) /* Return true if both the qual and to_test are null strings. */
return !qual || !*qual;
- if (!qual)
- return 0;
+ if (!qual) return 0;
if (prefix) {
while (*prefix) {
- if (*prefix++ != *to_test++)
- return 0;
+ if (*prefix++ != *to_test++) return 0;
}
}
while (*qual) {
char ch = *qual++;
- if (sanitize_qual && !isalnum(ch))
- ch = '_';
- if (ch != *to_test++)
- return 0;
+ if (sanitize_qual && !isalnum(ch)) ch = '_';
+ if (ch != *to_test++) return 0;
}
/* Everything matched so far. Return true if *to_test is a NUL. */
return !*to_test;
}
-atransport* acquire_one_transport(ConnectionState state, TransportType type,
- const char* serial, std::string* error_out) {
- atransport *result = NULL;
- int ambiguous = 0;
+atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
+ std::string* error_out) {
+ atransport* result = nullptr;
-retry:
- *error_out = serial ? android::base::StringPrintf("device '%s' not found", serial) : "no devices found";
+ if (serial) {
+ *error_out = android::base::StringPrintf("device '%s' not found", serial);
+ } else if (type == kTransportLocal) {
+ *error_out = "no emulators found";
+ } else if (type == kTransportAny) {
+ *error_out = "no devices/emulators found";
+ } else {
+ *error_out = "no devices found";
+ }
- adb_mutex_lock(&transport_lock);
- for (auto t : transport_list) {
+ std::unique_lock<std::mutex> lock(transport_lock);
+ for (const auto& t : transport_list) {
if (t->connection_state == kCsNoPerm) {
- *error_out = "insufficient permissions for device";
+#if ADB_HOST
+ *error_out = UsbNoPermissionsLongHelpText();
+#endif
continue;
}
- /* check for matching serial number */
+ // Check for matching serial number.
if (serial) {
- if ((t->serial && !strcmp(serial, t->serial)) ||
- (t->devpath && !strcmp(serial, t->devpath)) ||
- qual_match(serial, "product:", t->product, false) ||
- qual_match(serial, "model:", t->model, true) ||
- qual_match(serial, "device:", t->device, false)) {
+ if (t->MatchesTarget(serial)) {
if (result) {
*error_out = "more than one device";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
@@ -757,79 +640,85 @@
if (type == kTransportUsb && t->type == kTransportUsb) {
if (result) {
*error_out = "more than one device";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
} else if (type == kTransportLocal && t->type == kTransportLocal) {
if (result) {
*error_out = "more than one emulator";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
} else if (type == kTransportAny) {
if (result) {
*error_out = "more than one device/emulator";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
}
}
}
- adb_mutex_unlock(&transport_lock);
+ lock.unlock();
- if (result) {
- if (result->connection_state == 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 = NULL;
- }
+ // Don't return unauthorized devices; the caller can't do anything with them.
+ if (result && result->connection_state == 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;
+ }
- /* offline devices are ignored -- they are either being born or dying */
- if (result && result->connection_state == kCsOffline) {
- *error_out = "device offline";
- result = NULL;
- }
-
- /* check for required connection state */
- if (result && state != kCsAny && result->connection_state != state) {
- *error_out = "invalid device state";
- result = NULL;
- }
+ // Don't return offline devices; the caller can't do anything with them.
+ if (result && result->connection_state == kCsOffline) {
+ *error_out = "device offline";
+ result = nullptr;
}
if (result) {
- /* found one that we can take */
*error_out = "success";
- } else if (state != kCsAny && (serial || !ambiguous)) {
- adb_sleep_ms(1000);
- goto retry;
}
return result;
}
-const char* atransport::connection_state_name() const {
+void atransport::Kick() {
+ if (!kicked_) {
+ kicked_ = true;
+ CHECK(kick_func_ != nullptr);
+ kick_func_(this);
+ }
+}
+
+const std::string atransport::connection_state_name() const {
switch (connection_state) {
- case kCsOffline: return "offline";
- case kCsBootloader: return "bootloader";
- case kCsDevice: return "device";
- case kCsHost: return "host";
- case kCsRecovery: return "recovery";
- case kCsNoPerm: return "no permissions";
- case kCsSideload: return "sideload";
- case kCsUnauthorized: return "unauthorized";
- default: return "unknown";
+ case kCsOffline:
+ return "offline";
+ case kCsBootloader:
+ return "bootloader";
+ case kCsDevice:
+ return "device";
+ case kCsHost:
+ return "host";
+ case kCsRecovery:
+ return "recovery";
+ case kCsNoPerm:
+ return UsbNoPermissionsShortHelpText();
+ case kCsSideload:
+ return "sideload";
+ case kCsUnauthorized:
+ return "unauthorized";
+ default:
+ return "unknown";
}
}
@@ -846,33 +735,104 @@
return max_payload;
}
-// The list of features supported by the current system. Will be sent to the
-// other side of the connection in the banner.
-static const FeatureSet gSupportedFeatures = {
- // None yet.
-};
+namespace {
+
+constexpr char kFeatureStringDelimiter = ',';
+
+} // namespace
const FeatureSet& supported_features() {
- return gSupportedFeatures;
+ // Local static allocation to avoid global non-POD variables.
+ static const FeatureSet* features = new FeatureSet{
+ kFeatureShell2, kFeatureCmd, kFeatureStat2,
+ // Increment ADB_SERVER_VERSION whenever the feature list changes to
+ // make sure that the adb client and server features stay in sync
+ // (http://b/24370690).
+ };
+
+ return *features;
+}
+
+std::string FeatureSetToString(const FeatureSet& features) {
+ return android::base::Join(features, kFeatureStringDelimiter);
+}
+
+FeatureSet StringToFeatureSet(const std::string& features_string) {
+ if (features_string.empty()) {
+ return FeatureSet();
+ }
+
+ auto names = android::base::Split(features_string, {kFeatureStringDelimiter});
+ return FeatureSet(names.begin(), names.end());
+}
+
+bool CanUseFeature(const FeatureSet& feature_set, const std::string& feature) {
+ return feature_set.count(feature) > 0 && supported_features().count(feature) > 0;
}
bool atransport::has_feature(const std::string& feature) const {
return features_.count(feature) > 0;
}
-void atransport::add_feature(const std::string& feature) {
- features_.insert(feature);
+void atransport::SetFeatures(const std::string& features_string) {
+ features_ = StringToFeatureSet(features_string);
}
-bool atransport::CanUseFeature(const std::string& feature) const {
- return has_feature(feature) &&
- supported_features().count(feature) > 0;
+void atransport::AddDisconnect(adisconnect* disconnect) {
+ disconnects_.push_back(disconnect);
+}
+
+void atransport::RemoveDisconnect(adisconnect* disconnect) {
+ disconnects_.remove(disconnect);
+}
+
+void atransport::RunDisconnects() {
+ for (const auto& disconnect : disconnects_) {
+ disconnect->func(disconnect->opaque, this);
+ }
+ disconnects_.clear();
+}
+
+bool atransport::MatchesTarget(const std::string& target) const {
+ if (serial) {
+ if (target == serial) {
+ return true;
+ } else if (type == kTransportLocal) {
+ // Local transports can match [tcp:|udp:]<hostname>[:port].
+ const char* local_target_ptr = target.c_str();
+
+ // For fastboot compatibility, ignore protocol prefixes.
+ if (android::base::StartsWith(target, "tcp:") ||
+ android::base::StartsWith(target, "udp:")) {
+ local_target_ptr += 4;
+ }
+
+ // Parse our |serial| and the given |target| to check if the hostnames and ports match.
+ std::string serial_host, error;
+ int serial_port = -1;
+ if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr, &error)) {
+ // |target| may omit the port to default to ours.
+ std::string target_host;
+ int target_port = serial_port;
+ if (android::base::ParseNetAddress(local_target_ptr, &target_host, &target_port,
+ nullptr, &error) &&
+ serial_host == target_host && serial_port == target_port) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return (devpath && target == devpath) ||
+ qual_match(target.c_str(), "product:", product, false) ||
+ qual_match(target.c_str(), "model:", model, true) ||
+ qual_match(target.c_str(), "device:", device, false);
}
#if ADB_HOST
-static void append_transport_info(std::string* result, const char* key,
- const char* value, bool sanitize) {
+static void append_transport_info(std::string* result, const char* key, const char* value,
+ bool sanitize) {
if (value == nullptr || *value == '\0') {
return;
}
@@ -885,8 +845,7 @@
}
}
-static void append_transport(const atransport* t, std::string* result,
- bool long_listing) {
+static void append_transport(const atransport* t, std::string* result, bool long_listing) {
const char* serial = t->serial;
if (!serial || !serial[0]) {
serial = "(no serial number)";
@@ -897,43 +856,42 @@
*result += '\t';
*result += t->connection_state_name();
} else {
- android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name());
+ android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name().c_str());
append_transport_info(result, "", t->devpath, false);
append_transport_info(result, "product:", t->product, false);
append_transport_info(result, "model:", t->model, true);
append_transport_info(result, "device:", t->device, false);
- append_transport_info(result, "features:",
- android::base::Join(t->features(), ',').c_str(),
- false);
}
*result += '\n';
}
std::string list_transports(bool long_listing) {
std::string result;
- adb_mutex_lock(&transport_lock);
- for (const auto t : transport_list) {
+
+ std::lock_guard<std::mutex> lock(transport_lock);
+ for (const auto& t : transport_list) {
append_transport(t, &result, long_listing);
}
- adb_mutex_unlock(&transport_lock);
return result;
}
+void close_usb_devices(std::function<bool(const atransport*)> predicate) {
+ std::lock_guard<std::mutex> lock(transport_lock);
+ for (auto& t : transport_list) {
+ if (predicate(t)) {
+ t->Kick();
+ }
+ }
+}
+
/* hack for osx */
void close_usb_devices() {
- adb_mutex_lock(&transport_lock);
- for (auto t : transport_list) {
- if (!t->kicked) {
- t->kicked = 1;
- t->kick(t);
- }
- }
- adb_mutex_unlock(&transport_lock);
+ close_usb_devices([](const atransport*) { return true; });
}
-#endif // ADB_HOST
+#endif // ADB_HOST
-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* t = new atransport();
if (!serial) {
@@ -942,24 +900,26 @@
serial = buf;
}
- D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port);
+ D("transport: %s init'ing for socket %d, on port %d", serial, s, port);
if (init_socket_transport(t, s, port, local) < 0) {
delete t;
return -1;
}
- adb_mutex_lock(&transport_lock);
- for (auto transport : pending_list) {
+ std::unique_lock<std::mutex> lock(transport_lock);
+ for (const auto& transport : pending_list) {
if (transport->serial && strcmp(serial, transport->serial) == 0) {
- adb_mutex_unlock(&transport_lock);
+ VLOG(TRANSPORT) << "socket transport " << transport->serial
+ << " is already in pending_list and fails to register";
delete t;
return -1;
}
}
- for (auto transport : transport_list) {
+ for (const auto& transport : transport_list) {
if (transport->serial && strcmp(serial, transport->serial) == 0) {
- adb_mutex_unlock(&transport_lock);
+ VLOG(TRANSPORT) << "socket transport " << transport->serial
+ << " is already in transport_list and fails to register";
delete t;
return -1;
}
@@ -967,70 +927,50 @@
pending_list.push_front(t);
t->serial = strdup(serial);
- adb_mutex_unlock(&transport_lock);
+
+ lock.unlock();
register_transport(t);
return 0;
}
#if ADB_HOST
-atransport *find_transport(const char *serial) {
+atransport* find_transport(const char* serial) {
atransport* result = nullptr;
- adb_mutex_lock(&transport_lock);
- for (auto t : transport_list) {
+ std::lock_guard<std::mutex> lock(transport_lock);
+ for (auto& t : transport_list) {
if (t->serial && strcmp(serial, t->serial) == 0) {
result = t;
break;
}
}
- adb_mutex_unlock(&transport_lock);
return result;
}
-void unregister_transport(atransport *t)
-{
- adb_mutex_lock(&transport_lock);
- transport_list.remove(t);
- adb_mutex_unlock(&transport_lock);
-
- kick_transport(t);
- transport_unref(t);
-}
-
-// Unregisters all non-emulator TCP transports.
-void unregister_all_tcp_transports() {
- adb_mutex_lock(&transport_lock);
- for (auto it = transport_list.begin(); it != transport_list.end(); ) {
- atransport* t = *it;
- if (t->type == kTransportLocal && t->adb_port == 0) {
- // We cannot call kick_transport when holding transport_lock.
- if (!t->kicked) {
- t->kicked = 1;
- t->kick(t);
- }
- transport_unref_locked(t);
-
- it = transport_list.erase(it);
- } else {
- ++it;
+void kick_all_tcp_devices() {
+ std::lock_guard<std::mutex> lock(transport_lock);
+ for (auto& t : transport_list) {
+ if (t->IsTcpDevice()) {
+ // Kicking breaks the read_transport thread of this transport out of any read, then
+ // the read_transport thread will notify the main thread to make this transport
+ // offline. Then the main thread will notify the write_transport thread to exit.
+ // Finally, this transport will be closed and freed in the main thread.
+ t->Kick();
}
}
-
- adb_mutex_unlock(&transport_lock);
}
#endif
-void register_usb_transport(usb_handle* usb, const char* serial,
- const char* devpath, unsigned writeable) {
+void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
+ unsigned writeable) {
atransport* t = new atransport();
- D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
- serial ? serial : "");
+ D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
- if(serial) {
+ if (serial) {
t->serial = strdup(serial);
}
@@ -1038,56 +978,49 @@
t->devpath = strdup(devpath);
}
- adb_mutex_lock(&transport_lock);
- pending_list.push_front(t);
- adb_mutex_unlock(&transport_lock);
+ {
+ std::lock_guard<std::mutex> lock(transport_lock);
+ pending_list.push_front(t);
+ }
register_transport(t);
}
// This should only be used for transports with connection_state == kCsNoPerm.
-void unregister_usb_transport(usb_handle *usb) {
- adb_mutex_lock(&transport_lock);
- transport_list.remove_if([usb](atransport* t) {
- return t->usb == usb && t->connection_state == kCsNoPerm;
- });
- adb_mutex_unlock(&transport_lock);
+void unregister_usb_transport(usb_handle* usb) {
+ std::lock_guard<std::mutex> lock(transport_lock);
+ transport_list.remove_if(
+ [usb](atransport* t) { return t->usb == usb && t->connection_state == kCsNoPerm; });
}
-#undef TRACE_TAG
-#define TRACE_TAG TRACE_RWX
-
-int check_header(apacket *p, atransport *t)
-{
- if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
- D("check_header(): invalid magic\n");
+int check_header(apacket* p, atransport* t) {
+ if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
+ VLOG(RWX) << "check_header(): invalid magic";
return -1;
}
- if(p->msg.data_length > t->get_max_payload()) {
- D("check_header(): %u > atransport::max_payload = %zu\n",
- p->msg.data_length, t->get_max_payload());
+ if (p->msg.data_length > t->get_max_payload()) {
+ VLOG(RWX) << "check_header(): " << p->msg.data_length
+ << " atransport::max_payload = " << t->get_max_payload();
return -1;
}
return 0;
}
-int check_data(apacket *p)
-{
- unsigned count, sum;
- unsigned char *x;
-
- count = p->msg.data_length;
- x = p->data;
- sum = 0;
- while(count-- > 0) {
- sum += *x++;
- }
-
- if(sum != p->msg.data_check) {
+int check_data(apacket* p) {
+ if (calculate_apacket_checksum(p) != p->msg.data_check) {
return -1;
- } else {
- return 0;
}
+ return 0;
}
+
+#if ADB_HOST
+std::shared_ptr<RSA> atransport::NextKey() {
+ if (keys_.empty()) keys_ = adb_auth_get_private_keys();
+
+ std::shared_ptr<RSA> result = keys_[0];
+ keys_.pop_front();
+ return result;
+}
+#endif
diff --git a/adb/transport.h b/adb/transport.h
index e809407..3306388 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -19,15 +19,35 @@
#include <sys/types.h>
+#include <deque>
+#include <functional>
+#include <list>
+#include <memory>
#include <string>
#include <unordered_set>
#include "adb.h"
+#include <openssl/rsa.h>
+
typedef std::unordered_set<std::string> FeatureSet;
const FeatureSet& supported_features();
+// Encodes and decodes FeatureSet objects into human-readable strings.
+std::string FeatureSetToString(const FeatureSet& features);
+FeatureSet StringToFeatureSet(const std::string& features_string);
+
+// Returns true if both local features and |feature_set| support |feature|.
+bool CanUseFeature(const FeatureSet& feature_set, const std::string& feature);
+
+// Do not use any of [:;=,] in feature strings, they have special meaning
+// in the connection banner.
+extern const char* const kFeatureShell2;
+// The 'cmd' command is available
+extern const char* const kFeatureCmd;
+extern const char* const kFeatureStat2;
+
class atransport {
public:
// TODO(danalbert): We expose waaaaaaay too much stuff because this was
@@ -36,7 +56,6 @@
// it's better to do this piece by piece.
atransport() {
- auth_fde = {};
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
@@ -47,12 +66,18 @@
int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
int (*write_to_remote)(apacket* p, atransport* t) = nullptr;
void (*close)(atransport* t) = nullptr;
- void (*kick)(atransport* t) = nullptr;
+ void SetKickFunction(void (*kick_func)(atransport*)) {
+ kick_func_ = kick_func;
+ }
+ bool IsKicked() {
+ return kicked_;
+ }
+ void Kick();
int fd = -1;
int transport_socket = -1;
fdevent transport_fde;
- int ref_count = 0;
+ size_t ref_count = 0;
uint32_t sync_token = 0;
ConnectionState connection_state = kCsOffline;
bool online = false;
@@ -68,64 +93,101 @@
char* model = nullptr;
char* device = nullptr;
char* devpath = nullptr;
- int adb_port = -1; // Use for emulators (local transport)
- bool kicked = false;
+ void SetLocalPortForEmulator(int port) {
+ CHECK_EQ(local_port_for_emulator_, -1);
+ local_port_for_emulator_ = port;
+ }
- // A list of adisconnect callbacks called when the transport is kicked.
- adisconnect disconnects = {};
+ bool GetLocalPortForEmulator(int* port) const {
+ if (type == kTransportLocal && local_port_for_emulator_ != -1) {
+ *port = local_port_for_emulator_;
+ return true;
+ }
+ return false;
+ }
- void* key = nullptr;
- unsigned char token[TOKEN_SIZE] = {};
- fdevent auth_fde;
+ bool IsTcpDevice() const {
+ return type == kTransportLocal && local_port_for_emulator_ == -1;
+ }
+
+#if ADB_HOST
+ std::shared_ptr<RSA> NextKey();
+#endif
+
+ char token[TOKEN_SIZE] = {};
size_t failed_auth_attempts = 0;
- const char* connection_state_name() const;
+ const std::string connection_state_name() const;
void update_version(int version, size_t payload);
int get_protocol_version() const;
size_t get_max_payload() const;
- inline const FeatureSet features() const {
+ const FeatureSet& features() const {
return features_;
}
bool has_feature(const std::string& feature) const;
- void add_feature(const std::string& feature);
- // Returns true if both we and the other end of the transport support the
- // feature.
- bool CanUseFeature(const std::string& feature) const;
+ // Loads the transport's feature set from the given string.
+ void SetFeatures(const std::string& features_string);
+
+ void AddDisconnect(adisconnect* disconnect);
+ void RemoveDisconnect(adisconnect* disconnect);
+ void RunDisconnects();
+
+ // Returns true if |target| matches this transport. A matching |target| can be any of:
+ // * <serial>
+ // * <devpath>
+ // * product:<product>
+ // * model:<model>
+ // * device:<device>
+ //
+ // If this is a local transport, serial will also match [tcp:|udp:]<hostname>[:port] targets.
+ // For example, serial "100.100.100.100:5555" would match any of:
+ // * 100.100.100.100
+ // * tcp:100.100.100.100
+ // * udp:100.100.100.100:5555
+ // This is to make it easier to use the same network target for both fastboot and adb.
+ bool MatchesTarget(const std::string& target) const;
private:
+ int local_port_for_emulator_ = -1;
+ bool kicked_ = false;
+ void (*kick_func_)(atransport*) = nullptr;
+
// A set of features transmitted in the banner with the initial connection.
// This is stored in the banner as 'features=feature0,feature1,etc'.
FeatureSet features_;
int protocol_version;
size_t max_payload;
+ // A list of adisconnect callbacks called when the transport is kicked.
+ std::list<adisconnect*> disconnects_;
+
+#if ADB_HOST
+ std::deque<std::shared_ptr<RSA>> keys_;
+#endif
+
DISALLOW_COPY_AND_ASSIGN(atransport);
};
/*
* Obtain a transport from the available transports.
- * If state is != kCsAny, only transports in that state are considered.
- * If serial is non-NULL then only the device with that serial will be chosen.
- * If no suitable transport is found, error is set.
+ * If serial is non-null then only the device with that serial will be chosen.
+ * If multiple devices/emulators would match, *is_ambiguous (if non-null)
+ * is set to true and nullptr returned.
+ * If no suitable transport is found, error is set and nullptr returned.
*/
-atransport* acquire_one_transport(ConnectionState state, TransportType type,
- const char* serial, std::string* error_out);
-void add_transport_disconnect(atransport* t, adisconnect* dis);
-void remove_transport_disconnect(atransport* t, adisconnect* dis);
+atransport* acquire_one_transport(TransportType type, const char* serial,
+ bool* is_ambiguous, std::string* error_out);
void kick_transport(atransport* t);
-void run_transport_disconnects(atransport* t);
void update_transports(void);
-/* transports are ref-counted
-** get_device_transport does an acquire on your behalf before returning
-*/
void init_transport_registration(void);
std::string list_transports(bool long_listing);
atransport* find_transport(const char* serial);
+void kick_all_tcp_devices();
void register_usb_transport(usb_handle* h, const char* serial,
const char* devpath, unsigned writeable);
@@ -136,15 +198,11 @@
// This should only be used for transports with connection_state == kCsNoPerm.
void unregister_usb_transport(usb_handle* usb);
-/* these should only be used for the "adb disconnect" command */
-void unregister_transport(atransport* t);
-void unregister_all_tcp_transports();
-
int check_header(apacket* p, atransport* t);
int check_data(apacket* p);
-/* for MacOS X cleanup */
void close_usb_devices();
+void close_usb_devices(std::function<bool(const atransport*)> predicate);
void send_packet(apacket* p, atransport* t);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 6a17497..c17f869 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_TRANSPORT
+#define TRACE_TAG TRANSPORT
#include "sysdeps.h"
#include "transport.h"
@@ -25,48 +25,59 @@
#include <string.h>
#include <sys/types.h>
-#include <base/stringprintf.h>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#if !ADB_HOST
-#include "cutils/properties.h"
+#include <android-base/properties.h>
#endif
#include "adb.h"
#include "adb_io.h"
#include "adb_utils.h"
+#include "sysdeps/chrono.h"
#if ADB_HOST
+
+// Android Wear has been using port 5601 in all of its documentation/tooling,
+// but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
+// Avoid stomping on their port by limiting the number of emulators that can be
+// connected.
+#define ADB_LOCAL_TRANSPORT_MAX 16
+
+static std::mutex& local_transports_lock = *new std::mutex();
+
/* we keep a list of opened transports. The atransport struct knows to which
* local transport it is connected. The list is used to detect when we're
* trying to connect twice to a given local transport.
*/
-#define ADB_LOCAL_TRANSPORT_MAX 64
-
-ADB_MUTEX_DEFINE( local_transports_lock );
-
static atransport* local_transports[ ADB_LOCAL_TRANSPORT_MAX ];
#endif /* ADB_HOST */
static int remote_read(apacket *p, atransport *t)
{
if(!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))){
- D("remote local: read terminated (message)\n");
+ D("remote local: read terminated (message)");
return -1;
}
if(check_header(p, t)) {
- D("bad header: terminated (data)\n");
+ D("bad header: terminated (data)");
return -1;
}
if(!ReadFdExactly(t->sfd, p->data, p->msg.data_length)){
- D("remote local: terminated (data)\n");
+ D("remote local: terminated (data)");
return -1;
}
if(check_data(p)) {
- D("bad data: terminated (data)\n");
+ D("bad data: terminated (data)");
return -1;
}
@@ -78,23 +89,24 @@
int length = p->msg.data_length;
if(!WriteFdExactly(t->sfd, &p->msg, sizeof(amessage) + length)) {
- D("remote local: write terminated\n");
+ D("remote local: write terminated");
return -1;
}
return 0;
}
-void local_connect(int port) {
+bool local_connect(int port) {
std::string dummy;
- local_connect_arbitrary_ports(port-1, port, &dummy);
+ return local_connect_arbitrary_ports(port-1, port, &dummy) == 0;
}
int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) {
int fd = -1;
#if ADB_HOST
- if (find_emulator_transport_by_adb_port(adb_port) != nullptr) {
+ if (find_emulator_transport_by_adb_port(adb_port) != nullptr ||
+ find_emulator_transport_by_console_port(console_port) != nullptr) {
return -1;
}
@@ -108,10 +120,10 @@
}
if (fd >= 0) {
- D("client: connected on remote on fd %d\n", fd);
+ D("client: connected on remote on fd %d", fd);
close_on_exec(fd);
disable_tcp_nagle(fd);
- std::string serial = android::base::StringPrintf("emulator-%d", console_port);
+ std::string serial = getEmulatorSerialString(console_port);
if (register_socket_transport(fd, serial.c_str(), adb_port, 1) == 0) {
return 0;
}
@@ -121,72 +133,127 @@
}
#if ADB_HOST
-static void *client_socket_thread(void *x)
-{
- D("transport: client_socket_thread() starting\n");
- while (true) {
- int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
- int count = ADB_LOCAL_TRANSPORT_MAX;
- // Try to connect to any number of running emulator instances.
- for ( ; count > 0; count--, port += 2 ) {
- local_connect(port);
- }
- sleep(1);
+static void PollAllLocalPortsForEmulator() {
+ int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+ int count = ADB_LOCAL_TRANSPORT_MAX;
+
+ // Try to connect to any number of running emulator instances.
+ for ( ; count > 0; count--, port += 2 ) {
+ local_connect(port);
}
- return 0;
+}
+
+// Retry the disconnected local port for 60 times, and sleep 1 second between two retries.
+constexpr uint32_t LOCAL_PORT_RETRY_COUNT = 60;
+constexpr auto LOCAL_PORT_RETRY_INTERVAL = 1s;
+
+struct RetryPort {
+ int port;
+ uint32_t retry_count;
+};
+
+// Retry emulators just kicked.
+static std::vector<RetryPort>& retry_ports = *new std::vector<RetryPort>;
+std::mutex &retry_ports_lock = *new std::mutex;
+std::condition_variable &retry_ports_cond = *new std::condition_variable;
+
+static void client_socket_thread(void* x) {
+ adb_thread_setname("client_socket_thread");
+ D("transport: client_socket_thread() starting");
+ PollAllLocalPortsForEmulator();
+ while (true) {
+ std::vector<RetryPort> ports;
+ // Collect retry ports.
+ {
+ std::unique_lock<std::mutex> lock(retry_ports_lock);
+ while (retry_ports.empty()) {
+ retry_ports_cond.wait(lock);
+ }
+ retry_ports.swap(ports);
+ }
+ // Sleep here instead of the end of loop, because if we immediately try to reconnect
+ // the emulator just kicked, the adbd on the emulator may not have time to remove the
+ // just kicked transport.
+ std::this_thread::sleep_for(LOCAL_PORT_RETRY_INTERVAL);
+
+ // Try connecting retry ports.
+ std::vector<RetryPort> next_ports;
+ for (auto& port : ports) {
+ VLOG(TRANSPORT) << "retry port " << port.port << ", last retry_count "
+ << port.retry_count;
+ if (local_connect(port.port)) {
+ VLOG(TRANSPORT) << "retry port " << port.port << " successfully";
+ continue;
+ }
+ if (--port.retry_count > 0) {
+ next_ports.push_back(port);
+ } else {
+ VLOG(TRANSPORT) << "stop retrying port " << port.port;
+ }
+ }
+
+ // Copy back left retry ports.
+ {
+ std::unique_lock<std::mutex> lock(retry_ports_lock);
+ retry_ports.insert(retry_ports.end(), next_ports.begin(), next_ports.end());
+ }
+ }
}
#else // ADB_HOST
-static void *server_socket_thread(void * arg)
-{
+static void server_socket_thread(void* arg) {
int serverfd, fd;
- struct sockaddr addr;
- socklen_t alen;
int port = (int) (uintptr_t) arg;
- D("transport: server_socket_thread() starting\n");
+ adb_thread_setname("server socket");
+ D("transport: server_socket_thread() starting");
serverfd = -1;
for(;;) {
if(serverfd == -1) {
std::string error;
serverfd = network_inaddr_any_server(port, SOCK_STREAM, &error);
if(serverfd < 0) {
- D("server: cannot bind socket yet: %s\n", error.c_str());
- adb_sleep_ms(1000);
+ D("server: cannot bind socket yet: %s", error.c_str());
+ std::this_thread::sleep_for(1s);
continue;
}
close_on_exec(serverfd);
}
- alen = sizeof(addr);
- D("server: trying to get new connection from %d\n", port);
- fd = adb_socket_accept(serverfd, &addr, &alen);
+ D("server: trying to get new connection from %d", port);
+ fd = adb_socket_accept(serverfd, nullptr, nullptr);
if(fd >= 0) {
- D("server: new connection on fd %d\n", fd);
+ D("server: new connection on fd %d", fd);
close_on_exec(fd);
disable_tcp_nagle(fd);
- register_socket_transport(fd, "host", port, 1);
+ std::string serial = android::base::StringPrintf("host-%d", fd);
+ if (register_socket_transport(fd, serial.c_str(), port, 1) != 0) {
+ adb_close(fd);
+ }
}
}
- D("transport: server_socket_thread() exiting\n");
- return 0;
+ D("transport: server_socket_thread() exiting");
}
/* This is relevant only for ADB daemon running inside the emulator. */
/*
* Redefine open and write for qemu_pipe.h that contains inlined references
- * to those routines. We will redifine them back after qemu_pipe.h inclusion.
+ * to those routines. We will redefine them back after qemu_pipe.h inclusion.
*/
#undef open
+#undef read
#undef write
#define open adb_open
+#define read adb_read
#define write adb_write
-#include <hardware/qemu_pipe.h>
+#include <system/qemu_pipe.h>
#undef open
+#undef read
#undef write
#define open ___xxx_open
+#define read ___xxx_read
#define write ___xxx_write
/* A worker thread that monitors host connections, and registers a transport for
@@ -217,33 +284,33 @@
* the transport registration is completed. That's why we need to send the
* 'start' request after the transport is registered.
*/
-static void *qemu_socket_thread(void * arg)
-{
-/* 'accept' request to the adb QEMUD service. */
-static const char _accept_req[] = "accept";
-/* 'start' request to the adb QEMUD service. */
-static const char _start_req[] = "start";
-/* 'ok' reply from the adb QEMUD service. */
-static const char _ok_resp[] = "ok";
+static void qemu_socket_thread(void* arg) {
+ /* 'accept' request to the adb QEMUD service. */
+ static const char _accept_req[] = "accept";
+ /* 'start' request to the adb QEMUD service. */
+ static const char _start_req[] = "start";
+ /* 'ok' reply from the adb QEMUD service. */
+ static const char _ok_resp[] = "ok";
const int port = (int) (uintptr_t) arg;
- int res, fd;
+ int fd;
char tmp[256];
char con_name[32];
- D("transport: qemu_socket_thread() starting\n");
+ adb_thread_setname("qemu socket");
+ D("transport: qemu_socket_thread() starting");
/* adb QEMUD service connection request. */
- snprintf(con_name, sizeof(con_name), "qemud:adb:%d", port);
+ snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
/* Connect to the adb QEMUD service. */
fd = qemu_pipe_open(con_name);
if (fd < 0) {
/* This could be an older version of the emulator, that doesn't
* implement adb QEMUD service. Fall back to the old TCP way. */
- D("adb service is not available. Falling back to TCP socket.\n");
+ D("adb service is not available. Falling back to TCP socket.");
adb_thread_create(server_socket_thread, arg);
- return 0;
+ return;
}
for(;;) {
@@ -252,61 +319,60 @@
*/
/* Send the 'accept' request. */
- res = adb_write(fd, _accept_req, strlen(_accept_req));
- if ((size_t)res == strlen(_accept_req)) {
+ if (WriteFdExactly(fd, _accept_req, strlen(_accept_req))) {
/* Wait for the response. In the response we expect 'ok' on success,
* or 'ko' on failure. */
- res = adb_read(fd, tmp, sizeof(tmp));
- if (res != 2 || memcmp(tmp, _ok_resp, 2)) {
- D("Accepting ADB host connection has failed.\n");
+ if (!ReadFdExactly(fd, tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
+ D("Accepting ADB host connection has failed.");
adb_close(fd);
} else {
/* Host is connected. Register the transport, and start the
* exchange. */
- register_socket_transport(fd, "host", port, 1);
- adb_write(fd, _start_req, strlen(_start_req));
+ std::string serial = android::base::StringPrintf("host-%d", fd);
+ if (register_socket_transport(fd, serial.c_str(), port, 1) != 0 ||
+ !WriteFdExactly(fd, _start_req, strlen(_start_req))) {
+ adb_close(fd);
+ }
}
/* Prepare for accepting of the next ADB host connection. */
fd = qemu_pipe_open(con_name);
if (fd < 0) {
- D("adb service become unavailable.\n");
- return 0;
+ D("adb service become unavailable.");
+ return;
}
} else {
- D("Unable to send the '%s' request to ADB service.\n", _accept_req);
- return 0;
+ D("Unable to send the '%s' request to ADB service.", _accept_req);
+ return;
}
}
- D("transport: qemu_socket_thread() exiting\n");
- return 0;
+ D("transport: qemu_socket_thread() exiting");
+ return;
}
#endif // !ADB_HOST
void local_init(int port)
{
- void* (*func)(void *);
+ adb_thread_func_t func;
const char* debug_name = "";
#if ADB_HOST
func = client_socket_thread;
debug_name = "client";
#else
- /* For the adbd daemon in the system image we need to distinguish
- * between the device, and the emulator. */
- char is_qemu[PROPERTY_VALUE_MAX];
- property_get("ro.kernel.qemu", is_qemu, "");
- if (!strcmp(is_qemu, "1")) {
- /* Running inside the emulator: use QEMUD pipe as the transport. */
+ // For the adbd daemon in the system image we need to distinguish
+ // between the device, and the emulator.
+ if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+ // Running inside the emulator: use QEMUD pipe as the transport.
func = qemu_socket_thread;
} else {
- /* Running inside the device: use TCP socket as the transport. */
+ // Running inside the device: use TCP socket as the transport.
func = server_socket_thread;
}
debug_name = "server";
#endif // !ADB_HOST
- D("transport: local %s init\n", debug_name);
+ D("transport: local %s init", debug_name);
if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
fatal_errno("cannot create local socket %s thread", debug_name);
}
@@ -321,14 +387,13 @@
#if ADB_HOST
int nn;
- adb_mutex_lock( &local_transports_lock );
+ std::lock_guard<std::mutex> lock(local_transports_lock);
for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
if (local_transports[nn] == t) {
local_transports[nn] = NULL;
break;
}
}
- adb_mutex_unlock( &local_transports_lock );
#endif
}
@@ -339,30 +404,55 @@
t->sfd = -1;
adb_close(fd);
}
+#if ADB_HOST
+ int local_port;
+ if (t->GetLocalPortForEmulator(&local_port)) {
+ VLOG(TRANSPORT) << "remote_close, local_port = " << local_port;
+ std::unique_lock<std::mutex> lock(retry_ports_lock);
+ RetryPort port;
+ port.port = local_port;
+ port.retry_count = LOCAL_PORT_RETRY_COUNT;
+ retry_ports.push_back(port);
+ retry_ports_cond.notify_one();
+ }
+#endif
}
#if ADB_HOST
/* Only call this function if you already hold local_transports_lock. */
-atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
+static atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
{
int i;
for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
- if (local_transports[i] && local_transports[i]->adb_port == adb_port) {
- return local_transports[i];
+ int local_port;
+ if (local_transports[i] && local_transports[i]->GetLocalPortForEmulator(&local_port)) {
+ if (local_port == adb_port) {
+ return local_transports[i];
+ }
}
}
return NULL;
}
+std::string getEmulatorSerialString(int console_port)
+{
+ return android::base::StringPrintf("emulator-%d", console_port);
+}
+
atransport* find_emulator_transport_by_adb_port(int adb_port)
{
- adb_mutex_lock( &local_transports_lock );
+ std::lock_guard<std::mutex> lock(local_transports_lock);
atransport* result = find_emulator_transport_by_adb_port_locked(adb_port);
- adb_mutex_unlock( &local_transports_lock );
return result;
}
+atransport* find_emulator_transport_by_console_port(int console_port)
+{
+ return find_transport(getEmulatorSerialString(console_port).c_str());
+}
+
+
/* Only call this function if you already hold local_transports_lock. */
int get_available_local_transport_index_locked()
{
@@ -377,9 +467,8 @@
int get_available_local_transport_index()
{
- adb_mutex_lock( &local_transports_lock );
+ std::lock_guard<std::mutex> lock(local_transports_lock);
int result = get_available_local_transport_index_locked();
- adb_mutex_unlock( &local_transports_lock );
return result;
}
#endif
@@ -388,7 +477,7 @@
{
int fail = 0;
- t->kick = remote_kick;
+ t->SetKickFunction(remote_kick);
t->close = remote_close;
t->read_from_remote = remote_read;
t->write_to_remote = remote_write;
@@ -396,30 +485,23 @@
t->sync_token = 1;
t->connection_state = kCsOffline;
t->type = kTransportLocal;
- t->adb_port = 0;
#if ADB_HOST
if (local) {
- adb_mutex_lock( &local_transports_lock );
- {
- t->adb_port = adb_port;
- atransport* existing_transport =
- find_emulator_transport_by_adb_port_locked(adb_port);
- int index = get_available_local_transport_index_locked();
- if (existing_transport != NULL) {
- D("local transport for port %d already registered (%p)?\n",
- adb_port, existing_transport);
- fail = -1;
- } else if (index < 0) {
- // Too many emulators.
- D("cannot register more emulators. Maximum is %d\n",
- ADB_LOCAL_TRANSPORT_MAX);
- fail = -1;
- } else {
- local_transports[index] = t;
- }
- }
- adb_mutex_unlock( &local_transports_lock );
+ std::lock_guard<std::mutex> lock(local_transports_lock);
+ t->SetLocalPortForEmulator(adb_port);
+ atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
+ int index = get_available_local_transport_index_locked();
+ if (existing_transport != NULL) {
+ D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
+ fail = -1;
+ } else if (index < 0) {
+ // Too many emulators.
+ D("cannot register more emulators. Maximum is %d", ADB_LOCAL_TRANSPORT_MAX);
+ fail = -1;
+ } else {
+ local_transports[index] = t;
+ }
}
#endif
return fail;
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 743d97d..8b38e03 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -20,129 +20,77 @@
#include "adb.h"
-class TestTransport : public atransport {
-public:
- bool operator==(const atransport& rhs) const {
- EXPECT_EQ(read_from_remote, rhs.read_from_remote);
- EXPECT_EQ(write_to_remote, rhs.write_to_remote);
- EXPECT_EQ(close, rhs.close);
- EXPECT_EQ(kick, rhs.kick);
-
- EXPECT_EQ(fd, rhs.fd);
- EXPECT_EQ(transport_socket, rhs.transport_socket);
-
- EXPECT_EQ(
- 0, memcmp(&transport_fde, &rhs.transport_fde, sizeof(fdevent)));
-
- EXPECT_EQ(ref_count, rhs.ref_count);
- EXPECT_EQ(sync_token, rhs.sync_token);
- EXPECT_EQ(connection_state, rhs.connection_state);
- EXPECT_EQ(online, rhs.online);
- EXPECT_EQ(type, rhs.type);
-
- EXPECT_EQ(usb, rhs.usb);
- EXPECT_EQ(sfd, rhs.sfd);
-
- EXPECT_EQ(serial, rhs.serial);
- EXPECT_EQ(product, rhs.product);
- EXPECT_EQ(model, rhs.model);
- EXPECT_EQ(device, rhs.device);
- EXPECT_EQ(devpath, rhs.devpath);
- EXPECT_EQ(adb_port, rhs.adb_port);
- EXPECT_EQ(kicked, rhs.kicked);
-
- EXPECT_EQ(
- 0, memcmp(&disconnects, &rhs.disconnects, sizeof(adisconnect)));
-
- EXPECT_EQ(key, rhs.key);
- EXPECT_EQ(0, memcmp(token, rhs.token, TOKEN_SIZE));
- EXPECT_EQ(0, memcmp(&auth_fde, &rhs.auth_fde, sizeof(fdevent)));
- EXPECT_EQ(failed_auth_attempts, rhs.failed_auth_attempts);
-
- EXPECT_EQ(features(), rhs.features());
-
- return true;
- }
-};
-
-class TransportSetup {
-public:
- TransportSetup() {
-#ifdef _WIN32
- // Use extern instead of including sysdeps.h which brings in various macros
- // that conflict with APIs used in this file.
- extern void adb_sysdeps_init(void);
- adb_sysdeps_init();
-#else
- // adb_sysdeps_init() is an inline function that we cannot link against.
-#endif
- }
-};
-
-// Static initializer will call adb_sysdeps_init() before main() to initialize
-// the transport mutex before it is used in the tests. Alternatives would be to
-// use __attribute__((constructor)) here or to use that or a static initializer
-// for adb_sysdeps_init() itself in sysdeps_win32.cpp (caveats of unclear
-// init order), or to use a test fixture whose SetUp() could do the init once.
-static TransportSetup g_TransportSetup;
-
TEST(transport, kick_transport) {
- TestTransport t;
-
+ atransport t;
+ static size_t kick_count;
+ kick_count = 0;
// Mutate some member so we can test that the function is run.
- t.kick = [](atransport* trans) { trans->fd = 42; };
-
- TestTransport expected;
- expected.kick = t.kick;
- expected.fd = 42;
- expected.kicked = 1;
-
- kick_transport(&t);
- ASSERT_EQ(42, t.fd);
- ASSERT_EQ(1, t.kicked);
- ASSERT_EQ(expected, t);
+ t.SetKickFunction([](atransport* trans) { kick_count++; });
+ ASSERT_FALSE(t.IsKicked());
+ t.Kick();
+ ASSERT_TRUE(t.IsKicked());
+ ASSERT_EQ(1u, kick_count);
+ // A transport can only be kicked once.
+ t.Kick();
+ ASSERT_TRUE(t.IsKicked());
+ ASSERT_EQ(1u, kick_count);
}
-TEST(transport, kick_transport_already_kicked) {
- // Ensure that the transport is not modified if the transport has already been
- // kicked.
- TestTransport t;
- t.kicked = 1;
- t.kick = [](atransport*) { FAIL() << "Kick should not have been called"; };
-
- TestTransport expected;
- expected.kicked = 1;
- expected.kick = t.kick;
-
- kick_transport(&t);
- ASSERT_EQ(expected, t);
+static void DisconnectFunc(void* arg, atransport*) {
+ int* count = reinterpret_cast<int*>(arg);
+ ++*count;
}
-// Disabled because the function currently segfaults for a zeroed atransport. I
-// want to make sure I understand how this is working at all before I try fixing
-// that.
-TEST(transport, DISABLED_run_transport_disconnects_zeroed_atransport) {
+TEST(transport, RunDisconnects) {
atransport t;
- run_transport_disconnects(&t);
+ // RunDisconnects() can be called with an empty atransport.
+ t.RunDisconnects();
+
+ int count = 0;
+ adisconnect disconnect;
+ disconnect.func = DisconnectFunc;
+ disconnect.opaque = &count;
+ t.AddDisconnect(&disconnect);
+ t.RunDisconnects();
+ ASSERT_EQ(1, count);
+
+ // disconnect should have been removed automatically.
+ t.RunDisconnects();
+ ASSERT_EQ(1, count);
+
+ count = 0;
+ t.AddDisconnect(&disconnect);
+ t.RemoveDisconnect(&disconnect);
+ t.RunDisconnects();
+ ASSERT_EQ(0, count);
}
-TEST(transport, add_feature) {
+TEST(transport, SetFeatures) {
atransport t;
ASSERT_EQ(0U, t.features().size());
- t.add_feature("foo");
+ t.SetFeatures(FeatureSetToString(FeatureSet{"foo"}));
ASSERT_EQ(1U, t.features().size());
ASSERT_TRUE(t.has_feature("foo"));
- t.add_feature("bar");
+ t.SetFeatures(FeatureSetToString(FeatureSet{"foo", "bar"}));
ASSERT_EQ(2U, t.features().size());
ASSERT_TRUE(t.has_feature("foo"));
ASSERT_TRUE(t.has_feature("bar"));
- t.add_feature("foo");
+ t.SetFeatures(FeatureSetToString(FeatureSet{"foo", "bar", "foo"}));
ASSERT_EQ(2U, t.features().size());
ASSERT_TRUE(t.has_feature("foo"));
ASSERT_TRUE(t.has_feature("bar"));
+
+ t.SetFeatures(FeatureSetToString(FeatureSet{"bar", "baz"}));
+ ASSERT_EQ(2U, t.features().size());
+ ASSERT_FALSE(t.has_feature("foo"));
+ ASSERT_TRUE(t.has_feature("bar"));
+ ASSERT_TRUE(t.has_feature("baz"));
+
+ t.SetFeatures("");
+ ASSERT_EQ(0U, t.features().size());
}
TEST(transport, parse_banner_no_features) {
@@ -192,3 +140,60 @@
ASSERT_EQ(std::string("bar"), t.model);
ASSERT_EQ(std::string("baz"), t.device);
}
+
+TEST(transport, test_matches_target) {
+ std::string serial = "foo";
+ std::string devpath = "/path/to/bar";
+ std::string product = "test_product";
+ std::string model = "test_model";
+ std::string device = "test_device";
+
+ atransport t;
+ t.serial = &serial[0];
+ t.devpath = &devpath[0];
+ t.product = &product[0];
+ t.model = &model[0];
+ t.device = &device[0];
+
+ // These tests should not be affected by the transport type.
+ for (TransportType type : {kTransportAny, kTransportLocal}) {
+ t.type = type;
+
+ EXPECT_TRUE(t.MatchesTarget(serial));
+ EXPECT_TRUE(t.MatchesTarget(devpath));
+ EXPECT_TRUE(t.MatchesTarget("product:" + product));
+ EXPECT_TRUE(t.MatchesTarget("model:" + model));
+ EXPECT_TRUE(t.MatchesTarget("device:" + device));
+
+ // Product, model, and device don't match without the prefix.
+ EXPECT_FALSE(t.MatchesTarget(product));
+ EXPECT_FALSE(t.MatchesTarget(model));
+ EXPECT_FALSE(t.MatchesTarget(device));
+ }
+}
+
+TEST(transport, test_matches_target_local) {
+ std::string serial = "100.100.100.100:5555";
+
+ atransport t;
+ t.serial = &serial[0];
+
+ // Network address matching should only be used for local transports.
+ for (TransportType type : {kTransportAny, kTransportLocal}) {
+ t.type = type;
+ bool should_match = (type == kTransportLocal);
+
+ EXPECT_EQ(should_match, t.MatchesTarget("100.100.100.100"));
+ EXPECT_EQ(should_match, t.MatchesTarget("tcp:100.100.100.100"));
+ EXPECT_EQ(should_match, t.MatchesTarget("tcp:100.100.100.100:5555"));
+ EXPECT_EQ(should_match, t.MatchesTarget("udp:100.100.100.100"));
+ EXPECT_EQ(should_match, t.MatchesTarget("udp:100.100.100.100:5555"));
+
+ // Wrong protocol, hostname, or port should never match.
+ EXPECT_FALSE(t.MatchesTarget("100.100.100"));
+ EXPECT_FALSE(t.MatchesTarget("100.100.100.100:"));
+ EXPECT_FALSE(t.MatchesTarget("100.100.100.100:-1"));
+ EXPECT_FALSE(t.MatchesTarget("100.100.100.100:5554"));
+ EXPECT_FALSE(t.MatchesTarget("abc:100.100.100.100"));
+ }
+}
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 96ccdad..3d6cc99 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG TRACE_TRANSPORT
+#define TRACE_TAG TRANSPORT
#include "sysdeps.h"
#include "transport.h"
@@ -28,24 +28,24 @@
static int remote_read(apacket *p, atransport *t)
{
if(usb_read(t->usb, &p->msg, sizeof(amessage))){
- D("remote usb: read terminated (message)\n");
+ D("remote usb: read terminated (message)");
return -1;
}
if(check_header(p, t)) {
- D("remote usb: check_header failed\n");
+ D("remote usb: check_header failed");
return -1;
}
if(p->msg.data_length) {
if(usb_read(t->usb, p->data, p->msg.data_length)){
- D("remote usb: terminated (data)\n");
+ D("remote usb: terminated (data)");
return -1;
}
}
if(check_data(p)) {
- D("remote usb: check_data failed\n");
+ D("remote usb: check_data failed");
return -1;
}
@@ -57,12 +57,12 @@
unsigned size = p->msg.data_length;
if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
- D("remote usb: 1 - write terminated\n");
+ D("remote usb: 1 - write terminated");
return -1;
}
if(p->msg.data_length == 0) return 0;
if(usb_write(t->usb, &p->data, size)) {
- D("remote usb: 2 - write terminated\n");
+ D("remote usb: 2 - write terminated");
return -1;
}
@@ -82,9 +82,9 @@
void init_usb_transport(atransport *t, usb_handle *h, ConnectionState state)
{
- D("transport: usb\n");
+ D("transport: usb");
t->close = remote_close;
- t->kick = remote_kick;
+ t->SetKickFunction(remote_kick);
t->read_from_remote = remote_read;
t->write_to_remote = remote_write;
t->sync_token = 1;
@@ -93,9 +93,7 @@
t->usb = h;
}
-#if ADB_HOST
-int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
+int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol)
{
return (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL);
}
-#endif
diff --git a/adb/usb.h b/adb/usb.h
new file mode 100644
index 0000000..879bacc
--- /dev/null
+++ b/adb/usb.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// USB host/client interface.
+
+#define ADB_USB_INTERFACE(handle_ref_type) \
+ void usb_init(); \
+ int usb_write(handle_ref_type h, const void* data, int len); \
+ int usb_read(handle_ref_type h, void* data, int len); \
+ int usb_close(handle_ref_type h); \
+ void usb_kick(handle_ref_type h)
+
+#if defined(_WIN32) || !ADB_HOST
+// Windows and the daemon have a single implementation.
+
+struct usb_handle;
+ADB_USB_INTERFACE(usb_handle*);
+
+#else // linux host || darwin
+// Linux and Darwin clients have native and libusb implementations.
+
+namespace libusb {
+ struct usb_handle;
+ ADB_USB_INTERFACE(libusb::usb_handle*);
+}
+
+namespace native {
+ struct usb_handle;
+ ADB_USB_INTERFACE(native::usb_handle*);
+}
+
+// Empty base that both implementations' opaque handles inherit from.
+struct usb_handle {
+};
+
+ADB_USB_INTERFACE(::usb_handle*);
+
+#endif // linux host || darwin
+
+
+// USB device detection.
+int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol);
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
deleted file mode 100644
index dd22712..0000000
--- a/adb/usb_linux.cpp
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_USB
-
-#include "sysdeps.h"
-
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/usb/ch9.h>
-#include <linux/usbdevice_fs.h>
-#include <linux/version.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <condition_variable>
-#include <list>
-#include <mutex>
-#include <string>
-
-#include <base/file.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
-
-#include "adb.h"
-#include "transport.h"
-
-using namespace std::literals;
-
-/* usb scan debugging is waaaay too verbose */
-#define DBGX(x...)
-
-struct usb_handle {
- ~usb_handle() {
- if (fd != -1) unix_close(fd);
- }
-
- std::string path;
- int fd = -1;
- unsigned char ep_in;
- unsigned char ep_out;
-
- unsigned zero_mask;
- unsigned writeable = 1;
-
- usbdevfs_urb urb_in;
- usbdevfs_urb urb_out;
-
- bool urb_in_busy = false;
- bool urb_out_busy = false;
- bool dead = false;
-
- std::condition_variable cv;
- std::mutex mutex;
-
- // for garbage collecting disconnected devices
- bool mark;
-
- // ID of thread currently in REAPURB
- pthread_t reaper_thread = 0;
-};
-
-static std::mutex g_usb_handles_mutex;
-static std::list<usb_handle*> g_usb_handles;
-
-static int is_known_device(const char* dev_name) {
- std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
- for (usb_handle* usb : g_usb_handles) {
- if (usb->path == dev_name) {
- // set mark flag to indicate this device is still alive
- usb->mark = true;
- return 1;
- }
- }
- return 0;
-}
-
-static void kick_disconnected_devices() {
- std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
- // kick any devices in the device list that were not found in the device scan
- for (usb_handle* usb : g_usb_handles) {
- if (!usb->mark) {
- usb_kick(usb);
- } else {
- usb->mark = false;
- }
- }
-}
-
-static inline bool contains_non_digit(const char* name) {
- while (*name) {
- if (!isdigit(*name++)) return true;
- }
- return false;
-}
-
-static void find_usb_device(const std::string& base,
- void (*register_device_callback)
- (const char*, const char*, unsigned char, unsigned char, int, int, unsigned))
-{
- std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
- if (!bus_dir) return;
-
- dirent* de;
- while ((de = readdir(bus_dir.get())) != 0) {
- if (contains_non_digit(de->d_name)) continue;
-
- std::string bus_name = base + "/" + de->d_name;
-
- std::unique_ptr<DIR, int(*)(DIR*)> dev_dir(opendir(bus_name.c_str()), closedir);
- if (!dev_dir) continue;
-
- while ((de = readdir(dev_dir.get()))) {
- unsigned char devdesc[4096];
- unsigned char* bufptr = devdesc;
- unsigned char* bufend;
- struct usb_device_descriptor* device;
- struct usb_config_descriptor* config;
- struct usb_interface_descriptor* interface;
- struct usb_endpoint_descriptor *ep1, *ep2;
- unsigned zero_mask = 0;
- unsigned vid, pid;
-
- if (contains_non_digit(de->d_name)) continue;
-
- std::string dev_name = bus_name + "/" + de->d_name;
- if (is_known_device(dev_name.c_str())) {
- continue;
- }
-
- int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
- continue;
- }
-
- size_t desclength = unix_read(fd, devdesc, sizeof(devdesc));
- bufend = bufptr + desclength;
-
- // should have device and configuration descriptors, and atleast two endpoints
- if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
- D("desclength %zu is too small\n", desclength);
- unix_close(fd);
- continue;
- }
-
- device = (struct usb_device_descriptor*)bufptr;
- bufptr += USB_DT_DEVICE_SIZE;
-
- if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
- unix_close(fd);
- continue;
- }
-
- vid = device->idVendor;
- pid = device->idProduct;
- DBGX("[ %s is V:%04x P:%04x ]\n", dev_name.c_str(), vid, pid);
-
- // should have config descriptor next
- config = (struct usb_config_descriptor *)bufptr;
- bufptr += USB_DT_CONFIG_SIZE;
- if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
- D("usb_config_descriptor not found\n");
- unix_close(fd);
- continue;
- }
-
- // loop through all the descriptors and look for the ADB interface
- while (bufptr < bufend) {
- unsigned char length = bufptr[0];
- unsigned char type = bufptr[1];
-
- if (type == USB_DT_INTERFACE) {
- interface = (struct usb_interface_descriptor *)bufptr;
- bufptr += length;
-
- if (length != USB_DT_INTERFACE_SIZE) {
- D("interface descriptor has wrong size\n");
- break;
- }
-
- DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
- "bInterfaceProtocol: %d, bNumEndpoints: %d\n",
- interface->bInterfaceClass, interface->bInterfaceSubClass,
- interface->bInterfaceProtocol, interface->bNumEndpoints);
-
- if (interface->bNumEndpoints == 2 &&
- is_adb_interface(vid, pid, interface->bInterfaceClass,
- interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
-
- struct stat st;
- char pathbuf[128];
- char link[256];
- char *devpath = nullptr;
-
- DBGX("looking for bulk endpoints\n");
- // looks like ADB...
- ep1 = (struct usb_endpoint_descriptor *)bufptr;
- bufptr += USB_DT_ENDPOINT_SIZE;
- // For USB 3.0 SuperSpeed devices, skip potential
- // USB 3.0 SuperSpeed Endpoint Companion descriptor
- if (bufptr+2 <= devdesc + desclength &&
- bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
- bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
- bufptr += USB_DT_SS_EP_COMP_SIZE;
- }
- ep2 = (struct usb_endpoint_descriptor *)bufptr;
- bufptr += USB_DT_ENDPOINT_SIZE;
- if (bufptr+2 <= devdesc + desclength &&
- bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
- bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
- bufptr += USB_DT_SS_EP_COMP_SIZE;
- }
-
- if (bufptr > devdesc + desclength ||
- ep1->bLength != USB_DT_ENDPOINT_SIZE ||
- ep1->bDescriptorType != USB_DT_ENDPOINT ||
- ep2->bLength != USB_DT_ENDPOINT_SIZE ||
- ep2->bDescriptorType != USB_DT_ENDPOINT) {
- D("endpoints not found\n");
- break;
- }
-
- // both endpoints should be bulk
- if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
- ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
- D("bulk endpoints not found\n");
- continue;
- }
- /* aproto 01 needs 0 termination */
- if(interface->bInterfaceProtocol == 0x01) {
- zero_mask = ep1->wMaxPacketSize - 1;
- }
-
- // we have a match. now we just need to figure out which is in and which is out.
- unsigned char local_ep_in, local_ep_out;
- if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
- local_ep_in = ep1->bEndpointAddress;
- local_ep_out = ep2->bEndpointAddress;
- } else {
- local_ep_in = ep2->bEndpointAddress;
- local_ep_out = ep1->bEndpointAddress;
- }
-
- // Determine the device path
- if (!fstat(fd, &st) && S_ISCHR(st.st_mode)) {
- snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d",
- major(st.st_rdev), minor(st.st_rdev));
- ssize_t link_len = readlink(pathbuf, link, sizeof(link) - 1);
- if (link_len > 0) {
- link[link_len] = '\0';
- const char* slash = strrchr(link, '/');
- if (slash) {
- snprintf(pathbuf, sizeof(pathbuf),
- "usb:%s", slash + 1);
- devpath = pathbuf;
- }
- }
- }
-
- register_device_callback(dev_name.c_str(), devpath,
- local_ep_in, local_ep_out,
- interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
- break;
- }
- } else {
- bufptr += length;
- }
- } // end of while
-
- unix_close(fd);
- }
- }
-}
-
-static int usb_bulk_write(usb_handle* h, const void* data, int len) {
- std::unique_lock<std::mutex> lock(h->mutex);
- D("++ usb_bulk_write ++\n");
-
- usbdevfs_urb* urb = &h->urb_out;
- memset(urb, 0, sizeof(*urb));
- urb->type = USBDEVFS_URB_TYPE_BULK;
- urb->endpoint = h->ep_out;
- urb->status = -1;
- urb->buffer = const_cast<void*>(data);
- urb->buffer_length = len;
-
- if (h->dead) {
- errno = EINVAL;
- return -1;
- }
-
- if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
- return -1;
- }
-
- h->urb_out_busy = true;
- while (true) {
- auto now = std::chrono::system_clock::now();
- if (h->cv.wait_until(lock, now + 5s) == std::cv_status::timeout || h->dead) {
- // TODO: call USBDEVFS_DISCARDURB?
- errno = ETIMEDOUT;
- return -1;
- }
- if (!h->urb_out_busy) {
- if (urb->status != 0) {
- errno = -urb->status;
- return -1;
- }
- return urb->actual_length;
- }
- }
-}
-
-static int usb_bulk_read(usb_handle* h, void* data, int len) {
- std::unique_lock<std::mutex> lock(h->mutex);
- D("++ usb_bulk_read ++\n");
-
- usbdevfs_urb* urb = &h->urb_in;
- memset(urb, 0, sizeof(*urb));
- urb->type = USBDEVFS_URB_TYPE_BULK;
- urb->endpoint = h->ep_in;
- urb->status = -1;
- urb->buffer = data;
- urb->buffer_length = len;
-
- if (h->dead) {
- errno = EINVAL;
- return -1;
- }
-
- if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
- return -1;
- }
-
- h->urb_in_busy = true;
- while (true) {
- D("[ reap urb - wait ]\n");
- h->reaper_thread = pthread_self();
- int fd = h->fd;
- lock.unlock();
-
- // This ioctl must not have TEMP_FAILURE_RETRY because we send SIGALRM to break out.
- usbdevfs_urb* out = nullptr;
- int res = ioctl(fd, USBDEVFS_REAPURB, &out);
- int saved_errno = errno;
-
- lock.lock();
- h->reaper_thread = 0;
- if (h->dead) {
- errno = EINVAL;
- return -1;
- }
- if (res < 0) {
- if (saved_errno == EINTR) {
- continue;
- }
- D("[ reap urb - error ]\n");
- errno = saved_errno;
- return -1;
- }
- D("[ urb @%p status = %d, actual = %d ]\n", out, out->status, out->actual_length);
-
- if (out == &h->urb_in) {
- D("[ reap urb - IN complete ]\n");
- h->urb_in_busy = false;
- if (urb->status != 0) {
- errno = -urb->status;
- return -1;
- }
- return urb->actual_length;
- }
- if (out == &h->urb_out) {
- D("[ reap urb - OUT compelete ]\n");
- h->urb_out_busy = false;
- h->cv.notify_all();
- }
- }
-}
-
-
-int usb_write(usb_handle *h, const void *_data, int len)
-{
- D("++ usb_write ++\n");
-
- unsigned char *data = (unsigned char*) _data;
- int n = usb_bulk_write(h, data, len);
- if (n != len) {
- D("ERROR: n = %d, errno = %d (%s)\n", n, errno, strerror(errno));
- return -1;
- }
-
- if (h->zero_mask && !(len & h->zero_mask)) {
- // If we need 0-markers and our transfer is an even multiple of the packet size,
- // then send a zero marker.
- return usb_bulk_write(h, _data, 0);
- }
-
- D("-- usb_write --\n");
- return 0;
-}
-
-int usb_read(usb_handle *h, void *_data, int len)
-{
- unsigned char *data = (unsigned char*) _data;
- int n;
-
- D("++ usb_read ++\n");
- while(len > 0) {
- int xfer = len;
-
- D("[ usb read %d fd = %d], path=%s\n", xfer, h->fd, h->path.c_str());
- n = usb_bulk_read(h, data, xfer);
- D("[ usb read %d ] = %d, path=%s\n", xfer, n, h->path.c_str());
- if(n != xfer) {
- if((errno == ETIMEDOUT) && (h->fd != -1)) {
- D("[ timeout ]\n");
- if(n > 0){
- data += n;
- len -= n;
- }
- continue;
- }
- D("ERROR: n = %d, errno = %d (%s)\n",
- n, errno, strerror(errno));
- return -1;
- }
-
- len -= xfer;
- data += xfer;
- }
-
- D("-- usb_read --\n");
- return 0;
-}
-
-void usb_kick(usb_handle* h) {
- std::lock_guard<std::mutex> lock(h->mutex);
- D("[ kicking %p (fd = %d) ]\n", h, h->fd);
- if (!h->dead) {
- h->dead = true;
-
- if (h->writeable) {
- /* HACK ALERT!
- ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
- ** This is a workaround for that problem.
- */
- if (h->reaper_thread) {
- pthread_kill(h->reaper_thread, SIGALRM);
- }
-
- /* cancel any pending transactions
- ** these will quietly fail if the txns are not active,
- ** but this ensures that a reader blocked on REAPURB
- ** will get unblocked
- */
- ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_in);
- ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_out);
- h->urb_in.status = -ENODEV;
- h->urb_out.status = -ENODEV;
- h->urb_in_busy = false;
- h->urb_out_busy = false;
- h->cv.notify_all();
- } else {
- unregister_usb_transport(h);
- }
- }
-}
-
-int usb_close(usb_handle* h) {
- std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
- g_usb_handles.remove(h);
-
- D("-- usb close %p (fd = %d) --\n", h, h->fd);
-
- delete h;
-
- return 0;
-}
-
-static void register_device(const char* dev_name, const char* dev_path,
- unsigned char ep_in, unsigned char ep_out,
- int interface, int serial_index,
- unsigned zero_mask) {
- // Since Linux will not reassign the device ID (and dev_name) as long as the
- // device is open, we can add to the list here once we open it and remove
- // from the list when we're finally closed and everything will work out
- // fine.
- //
- // If we have a usb_handle on the list of handles with a matching name, we
- // have no further work to do.
- {
- std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
- for (usb_handle* usb: g_usb_handles) {
- if (usb->path == dev_name) {
- return;
- }
- }
- }
-
- D("[ usb located new device %s (%d/%d/%d) ]\n", dev_name, ep_in, ep_out, interface);
- std::unique_ptr<usb_handle> usb(new usb_handle);
- usb->path = dev_name;
- usb->ep_in = ep_in;
- usb->ep_out = ep_out;
- usb->zero_mask = zero_mask;
-
- // Initialize mark so we don't get garbage collected after the device scan.
- usb->mark = true;
-
- usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
- if (usb->fd == -1) {
- // Opening RW failed, so see if we have RO access.
- usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
- if (usb->fd == -1) {
- D("[ usb open %s failed: %s]\n", usb->path.c_str(), strerror(errno));
- return;
- }
- usb->writeable = 0;
- }
-
- D("[ usb opened %s%s, fd=%d]\n",
- usb->path.c_str(), (usb->writeable ? "" : " (read-only)"), usb->fd);
-
- if (usb->writeable) {
- if (ioctl(usb->fd, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
- D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n", usb->fd, strerror(errno));
- return;
- }
- }
-
- // Read the device's serial number.
- std::string serial_path = android::base::StringPrintf(
- "/sys/bus/usb/devices/%s/serial", dev_path + 4);
- std::string serial;
- if (!android::base::ReadFileToString(serial_path, &serial)) {
- D("[ usb read %s failed: %s ]\n", serial_path.c_str(), strerror(errno));
- // We don't actually want to treat an unknown serial as an error because
- // devices aren't able to communicate a serial number in early bringup.
- // http://b/20883914
- serial = "";
- }
- serial = android::base::Trim(serial);
-
- // Add to the end of the active handles.
- usb_handle* done_usb = usb.release();
- {
- std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
- g_usb_handles.push_back(done_usb);
- }
- register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
-}
-
-static void* device_poll_thread(void* unused) {
- D("Created device thread\n");
- while (true) {
- // TODO: Use inotify.
- find_usb_device("/dev/bus/usb", register_device);
- kick_disconnected_devices();
- sleep(1);
- }
- return nullptr;
-}
-
-void usb_init() {
- struct sigaction actions;
- memset(&actions, 0, sizeof(actions));
- sigemptyset(&actions.sa_mask);
- actions.sa_flags = 0;
- actions.sa_handler = [](int) {};
- sigaction(SIGALRM, &actions, nullptr);
-
- if (!adb_thread_create(device_poll_thread, nullptr)) {
- fatal_errno("cannot create input thread");
- }
-}
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
deleted file mode 100644
index b1b3538..0000000
--- a/adb/usb_linux_client.cpp
+++ /dev/null
@@ -1,521 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_USB
-
-#include "sysdeps.h"
-
-#include <cutils/properties.h>
-#include <dirent.h>
-#include <errno.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/functionfs.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "adb.h"
-#include "transport.h"
-
-#define MAX_PACKET_SIZE_FS 64
-#define MAX_PACKET_SIZE_HS 512
-#define MAX_PACKET_SIZE_SS 1024
-
-#define cpu_to_le16(x) htole16(x)
-#define cpu_to_le32(x) htole32(x)
-
-struct usb_handle
-{
- adb_cond_t notify;
- adb_mutex_t lock;
-
- int (*write)(usb_handle *h, const void *data, int len);
- int (*read)(usb_handle *h, void *data, int len);
- void (*kick)(usb_handle *h);
-
- // Legacy f_adb
- int fd;
-
- // FunctionFS
- int control;
- int bulk_out; /* "out" from the host's perspective => source for adbd */
- int bulk_in; /* "in" from the host's perspective => sink for adbd */
-};
-
-struct func_desc {
- struct usb_interface_descriptor intf;
- struct usb_endpoint_descriptor_no_audio source;
- struct usb_endpoint_descriptor_no_audio sink;
-} __attribute__((packed));
-
-struct desc_v1 {
- struct usb_functionfs_descs_head_v1 {
- __le32 magic;
- __le32 length;
- __le32 fs_count;
- __le32 hs_count;
- } __attribute__((packed)) header;
- struct func_desc fs_descs, hs_descs;
-} __attribute__((packed));
-
-struct desc_v2 {
- struct usb_functionfs_descs_head_v2 header;
- // The rest of the structure depends on the flags in the header.
- __le32 fs_count;
- __le32 hs_count;
- struct func_desc fs_descs, hs_descs;
-} __attribute__((packed));
-
-static struct func_desc fs_descriptors = {
- .intf = {
- .bLength = sizeof(fs_descriptors.intf),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bNumEndpoints = 2,
- .bInterfaceClass = ADB_CLASS,
- .bInterfaceSubClass = ADB_SUBCLASS,
- .bInterfaceProtocol = ADB_PROTOCOL,
- .iInterface = 1, /* first string from the provided table */
- },
- .source = {
- .bLength = sizeof(fs_descriptors.source),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 1 | USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_FS,
- },
- .sink = {
- .bLength = sizeof(fs_descriptors.sink),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 2 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_FS,
- },
-};
-
-static struct func_desc hs_descriptors = {
- .intf = {
- .bLength = sizeof(hs_descriptors.intf),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bNumEndpoints = 2,
- .bInterfaceClass = ADB_CLASS,
- .bInterfaceSubClass = ADB_SUBCLASS,
- .bInterfaceProtocol = ADB_PROTOCOL,
- .iInterface = 1, /* first string from the provided table */
- },
- .source = {
- .bLength = sizeof(hs_descriptors.source),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 1 | USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_HS,
- },
- .sink = {
- .bLength = sizeof(hs_descriptors.sink),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 2 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_HS,
- },
-};
-
-#define STR_INTERFACE_ "ADB Interface"
-
-static const struct {
- struct usb_functionfs_strings_head header;
- struct {
- __le16 code;
- const char str1[sizeof(STR_INTERFACE_)];
- } __attribute__((packed)) lang0;
-} __attribute__((packed)) strings = {
- .header = {
- .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
- .length = cpu_to_le32(sizeof(strings)),
- .str_count = cpu_to_le32(1),
- .lang_count = cpu_to_le32(1),
- },
- .lang0 = {
- cpu_to_le16(0x0409), /* en-us */
- STR_INTERFACE_,
- },
-};
-
-
-
-static void *usb_adb_open_thread(void *x)
-{
- struct usb_handle *usb = (struct usb_handle *)x;
- int fd;
-
- while (true) {
- // wait until the USB device needs opening
- adb_mutex_lock(&usb->lock);
- while (usb->fd != -1)
- adb_cond_wait(&usb->notify, &usb->lock);
- adb_mutex_unlock(&usb->lock);
-
- D("[ usb_thread - opening device ]\n");
- do {
- /* XXX use inotify? */
- fd = unix_open("/dev/android_adb", O_RDWR);
- if (fd < 0) {
- // to support older kernels
- fd = unix_open("/dev/android", O_RDWR);
- }
- if (fd < 0) {
- adb_sleep_ms(1000);
- }
- } while (fd < 0);
- D("[ opening device succeeded ]\n");
-
- close_on_exec(fd);
- usb->fd = fd;
-
- D("[ usb_thread - registering device ]\n");
- register_usb_transport(usb, 0, 0, 1);
- }
-
- // never gets here
- return 0;
-}
-
-static int usb_adb_write(usb_handle *h, const void *data, int len)
-{
- int n;
-
- D("about to write (fd=%d, len=%d)\n", h->fd, len);
- n = unix_write(h->fd, data, len);
- if(n != len) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
- h->fd, n, errno, strerror(errno));
- return -1;
- }
- D("[ done fd=%d ]\n", h->fd);
- return 0;
-}
-
-static int usb_adb_read(usb_handle *h, void *data, int len)
-{
- D("about to read (fd=%d, len=%d)\n", h->fd, len);
- while (len > 0) {
- // The kernel implementation of adb_read in f_adb.c doesn't support
- // reads larger then 4096 bytes. Read the data in 4096 byte chunks to
- // avoid the issue. (The ffs implementation doesn't have this limit.)
- int bytes_to_read = len < 4096 ? len : 4096;
- int n = unix_read(h->fd, data, bytes_to_read);
- if (n != bytes_to_read) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
- h->fd, n, errno, strerror(errno));
- return -1;
- }
- len -= n;
- data = ((char*)data) + n;
- }
- D("[ done fd=%d ]\n", h->fd);
- return 0;
-}
-
-static void usb_adb_kick(usb_handle *h)
-{
- D("usb_kick\n");
- adb_mutex_lock(&h->lock);
- unix_close(h->fd);
- h->fd = -1;
-
- // notify usb_adb_open_thread that we are disconnected
- adb_cond_signal(&h->notify);
- adb_mutex_unlock(&h->lock);
-}
-
-static void usb_adb_init()
-{
- usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
- if (h == nullptr) fatal("couldn't allocate usb_handle");
-
- h->write = usb_adb_write;
- h->read = usb_adb_read;
- h->kick = usb_adb_kick;
- h->fd = -1;
-
- adb_cond_init(&h->notify, 0);
- adb_mutex_init(&h->lock, 0);
-
- // Open the file /dev/android_adb_enable to trigger
- // the enabling of the adb USB function in the kernel.
- // We never touch this file again - just leave it open
- // indefinitely so the kernel will know when we are running
- // and when we are not.
- int fd = unix_open("/dev/android_adb_enable", O_RDWR);
- if (fd < 0) {
- D("failed to open /dev/android_adb_enable\n");
- } else {
- close_on_exec(fd);
- }
-
- D("[ usb_init - starting thread ]\n");
- if (!adb_thread_create(usb_adb_open_thread, h)) {
- fatal_errno("cannot create usb thread");
- }
-}
-
-
-static void init_functionfs(struct usb_handle *h)
-{
- ssize_t ret;
- struct desc_v1 v1_descriptor;
- struct desc_v2 v2_descriptor;
-
- v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
- v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
- v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
- v2_descriptor.fs_count = 3;
- v2_descriptor.hs_count = 3;
- v2_descriptor.fs_descs = fs_descriptors;
- v2_descriptor.hs_descs = hs_descriptors;
-
- if (h->control < 0) { // might have already done this before
- D("OPENING %s\n", USB_FFS_ADB_EP0);
- h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
- if (h->control < 0) {
- D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno);
- goto err;
- }
-
- ret = adb_write(h->control, &v2_descriptor, sizeof(v2_descriptor));
- if (ret < 0) {
- v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
- v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
- v1_descriptor.header.fs_count = 3;
- v1_descriptor.header.hs_count = 3;
- v1_descriptor.fs_descs = fs_descriptors;
- v1_descriptor.hs_descs = hs_descriptors;
- D("[ %s: Switching to V1_descriptor format errno=%d ]\n", USB_FFS_ADB_EP0, errno);
- ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor));
- if (ret < 0) {
- D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno);
- goto err;
- }
- }
-
- ret = adb_write(h->control, &strings, sizeof(strings));
- if (ret < 0) {
- D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno);
- goto err;
- }
- }
-
- h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
- if (h->bulk_out < 0) {
- D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno);
- goto err;
- }
-
- h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
- if (h->bulk_in < 0) {
- D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno);
- goto err;
- }
-
- return;
-
-err:
- if (h->bulk_in > 0) {
- adb_close(h->bulk_in);
- h->bulk_in = -1;
- }
- if (h->bulk_out > 0) {
- adb_close(h->bulk_out);
- h->bulk_out = -1;
- }
- if (h->control > 0) {
- adb_close(h->control);
- h->control = -1;
- }
- return;
-}
-
-static void *usb_ffs_open_thread(void *x)
-{
- struct usb_handle *usb = (struct usb_handle *)x;
-
- while (true) {
- // wait until the USB device needs opening
- adb_mutex_lock(&usb->lock);
- while (usb->control != -1 && usb->bulk_in != -1 && usb->bulk_out != -1)
- adb_cond_wait(&usb->notify, &usb->lock);
- adb_mutex_unlock(&usb->lock);
-
- while (true) {
- init_functionfs(usb);
-
- if (usb->control >= 0 && usb->bulk_in >= 0 && usb->bulk_out >= 0)
- break;
-
- adb_sleep_ms(1000);
- }
- property_set("sys.usb.ffs.ready", "1");
-
- D("[ usb_thread - registering device ]\n");
- register_usb_transport(usb, 0, 0, 1);
- }
-
- // never gets here
- return 0;
-}
-
-static int bulk_write(int bulk_in, const uint8_t* buf, size_t length)
-{
- size_t count = 0;
- int ret;
-
- do {
- ret = adb_write(bulk_in, buf + count, length - count);
- if (ret < 0) {
- if (errno != EINTR)
- return ret;
- } else {
- count += ret;
- }
- } while (count < length);
-
- D("[ bulk_write done fd=%d ]\n", bulk_in);
- return count;
-}
-
-static int usb_ffs_write(usb_handle* h, const void* data, int len)
-{
- D("about to write (fd=%d, len=%d)\n", h->bulk_in, len);
- int n = bulk_write(h->bulk_in, reinterpret_cast<const uint8_t*>(data), len);
- if (n != len) {
- D("ERROR: fd = %d, n = %d: %s\n", h->bulk_in, n, strerror(errno));
- return -1;
- }
- D("[ done fd=%d ]\n", h->bulk_in);
- return 0;
-}
-
-static int bulk_read(int bulk_out, uint8_t* buf, size_t length)
-{
- size_t count = 0;
- int ret;
-
- do {
- ret = adb_read(bulk_out, buf + count, length - count);
- if (ret < 0) {
- if (errno != EINTR) {
- D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n",
- bulk_out, length, count);
- return ret;
- }
- } else {
- count += ret;
- }
- } while (count < length);
-
- return count;
-}
-
-static int usb_ffs_read(usb_handle* h, void* data, int len)
-{
- D("about to read (fd=%d, len=%d)\n", h->bulk_out, len);
- int n = bulk_read(h->bulk_out, reinterpret_cast<uint8_t*>(data), len);
- if (n != len) {
- D("ERROR: fd = %d, n = %d: %s\n", h->bulk_out, n, strerror(errno));
- return -1;
- }
- D("[ done fd=%d ]\n", h->bulk_out);
- return 0;
-}
-
-static void usb_ffs_kick(usb_handle *h)
-{
- int err;
-
- err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
- if (err < 0)
- D("[ kick: source (fd=%d) clear halt failed (%d) ]\n", h->bulk_in, errno);
-
- err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
- if (err < 0)
- D("[ kick: sink (fd=%d) clear halt failed (%d) ]\n", h->bulk_out, errno);
-
- adb_mutex_lock(&h->lock);
-
- // don't close ep0 here, since we may not need to reinitialize it with
- // the same descriptors again. if however ep1/ep2 fail to re-open in
- // init_functionfs, only then would we close and open ep0 again.
- adb_close(h->bulk_out);
- adb_close(h->bulk_in);
- h->bulk_out = h->bulk_in = -1;
-
- // notify usb_ffs_open_thread that we are disconnected
- adb_cond_signal(&h->notify);
- adb_mutex_unlock(&h->lock);
-}
-
-static void usb_ffs_init()
-{
- D("[ usb_init - using FunctionFS ]\n");
-
- usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
- if (h == nullptr) fatal("couldn't allocate usb_handle");
-
- h->write = usb_ffs_write;
- h->read = usb_ffs_read;
- h->kick = usb_ffs_kick;
- h->control = -1;
- h->bulk_out = -1;
- h->bulk_out = -1;
-
- adb_cond_init(&h->notify, 0);
- adb_mutex_init(&h->lock, 0);
-
- D("[ usb_init - starting thread ]\n");
- if (!adb_thread_create(usb_ffs_open_thread, h)) {
- fatal_errno("[ cannot create usb thread ]\n");
- }
-}
-
-void usb_init()
-{
- if (access(USB_FFS_ADB_EP0, F_OK) == 0)
- usb_ffs_init();
- else
- usb_adb_init();
-}
-
-int usb_write(usb_handle *h, const void *data, int len)
-{
- return h->write(h, data, len);
-}
-
-int usb_read(usb_handle *h, void *data, int len)
-{
- return h->read(h, data, len);
-}
-int usb_close(usb_handle *h)
-{
- return 0;
-}
-
-void usb_kick(usb_handle *h)
-{
- h->kick(h);
-}
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
deleted file mode 100644
index afa08c1..0000000
--- a/adb/usb_osx.cpp
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_USB
-
-#include "sysdeps.h"
-
-#include <CoreFoundation/CoreFoundation.h>
-
-#include <IOKit/IOKitLib.h>
-#include <IOKit/IOCFPlugIn.h>
-#include <IOKit/usb/IOUSBLib.h>
-#include <IOKit/IOMessage.h>
-#include <mach/mach_port.h>
-
-#include <inttypes.h>
-#include <stdio.h>
-
-#include "adb.h"
-#include "transport.h"
-
-#define DBG D
-
-// There's no strerror equivalent for the errors returned by IOKit.
-// https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Handling_Errors/AH_Handling_Errors.html
-// Search the web for "IOReturn.h" to find a complete up to date list.
-
-static IONotificationPortRef notificationPort = 0;
-static io_iterator_t notificationIterator;
-
-struct usb_handle
-{
- UInt8 bulkIn;
- UInt8 bulkOut;
- IOUSBInterfaceInterface190** interface;
- io_object_t usbNotification;
- unsigned int zero_mask;
-};
-
-static CFRunLoopRef currentRunLoop = 0;
-static pthread_mutex_t start_lock;
-static pthread_cond_t start_cond;
-
-
-static void AndroidInterfaceAdded(void *refCon, io_iterator_t iterator);
-static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator,
- natural_t messageType,
- void *messageArgument);
-static usb_handle* CheckInterface(IOUSBInterfaceInterface190 **iface,
- UInt16 vendor, UInt16 product);
-
-static int
-InitUSB()
-{
- CFMutableDictionaryRef matchingDict;
- CFRunLoopSourceRef runLoopSource;
-
- //* To set up asynchronous notifications, create a notification port and
- //* add its run loop event source to the program's run loop
- notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
- runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
-
- //* Create our matching dictionary to find the Android device's
- //* adb interface
- //* IOServiceAddMatchingNotification consumes the reference, so we do
- //* not need to release this
- matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
-
- if (!matchingDict) {
- DBG("ERR: Couldn't create USB matching dictionary.\n");
- return -1;
- }
-
- //* We have to get notifications for all potential candidates and test them
- //* at connection time because the matching rules don't allow for a
- //* USB interface class of 0xff for class+subclass+protocol matches
- //* See https://developer.apple.com/library/mac/qa/qa1076/_index.html
- IOServiceAddMatchingNotification(
- notificationPort,
- kIOFirstMatchNotification,
- matchingDict,
- AndroidInterfaceAdded,
- NULL,
- ¬ificationIterator);
-
- //* Iterate over set of matching interfaces to access already-present
- //* devices and to arm the notification
- AndroidInterfaceAdded(NULL, notificationIterator);
-
- return 0;
-}
-
-static void
-AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
-{
- kern_return_t kr;
- io_service_t usbDevice;
- io_service_t usbInterface;
- IOCFPlugInInterface **plugInInterface = NULL;
- IOUSBInterfaceInterface220 **iface = NULL;
- IOUSBDeviceInterface197 **dev = NULL;
- HRESULT result;
- SInt32 score;
- UInt32 locationId;
- UInt8 if_class, subclass, protocol;
- UInt16 vendor;
- UInt16 product;
- UInt8 serialIndex;
- char serial[256];
- char devpathBuf[64];
- char *devpath = NULL;
-
- while ((usbInterface = IOIteratorNext(iterator))) {
- //* Create an intermediate interface plugin
- kr = IOCreatePlugInInterfaceForService(usbInterface,
- kIOUSBInterfaceUserClientTypeID,
- kIOCFPlugInInterfaceID,
- &plugInInterface, &score);
- IOObjectRelease(usbInterface);
- if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
- DBG("ERR: Unable to create an interface plug-in (%08x)\n", kr);
- continue;
- }
-
- //* This gets us the interface object
- result = (*plugInInterface)->QueryInterface(
- plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID*)&iface);
- //* We only needed the plugin to get the interface, so discard it
- (*plugInInterface)->Release(plugInInterface);
- if (result || !iface) {
- DBG("ERR: Couldn't query the interface (%08x)\n", (int) result);
- continue;
- }
-
- kr = (*iface)->GetInterfaceClass(iface, &if_class);
- kr = (*iface)->GetInterfaceSubClass(iface, &subclass);
- kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
- if(if_class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
- // Ignore non-ADB devices.
- DBG("Ignoring interface with incorrect class/subclass/protocol - %d, %d, %d\n", if_class, subclass, protocol);
- (*iface)->Release(iface);
- continue;
- }
-
- //* this gets us an ioservice, with which we will find the actual
- //* device; after getting a plugin, and querying the interface, of
- //* course.
- //* Gotta love OS X
- kr = (*iface)->GetDevice(iface, &usbDevice);
- if (kIOReturnSuccess != kr || !usbDevice) {
- DBG("ERR: Couldn't grab device from interface (%08x)\n", kr);
- continue;
- }
-
- plugInInterface = NULL;
- score = 0;
- //* create an intermediate device plugin
- kr = IOCreatePlugInInterfaceForService(usbDevice,
- kIOUSBDeviceUserClientTypeID,
- kIOCFPlugInInterfaceID,
- &plugInInterface, &score);
- //* only needed this to find the plugin
- (void)IOObjectRelease(usbDevice);
- if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
- DBG("ERR: Unable to create a device plug-in (%08x)\n", kr);
- continue;
- }
-
- result = (*plugInInterface)->QueryInterface(plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&dev);
- //* only needed this to query the plugin
- (*plugInInterface)->Release(plugInInterface);
- if (result || !dev) {
- DBG("ERR: Couldn't create a device interface (%08x)\n",
- (int) result);
- continue;
- }
-
- //* Now after all that, we actually have a ref to the device and
- //* the interface that matched our criteria
- kr = (*dev)->GetDeviceVendor(dev, &vendor);
- kr = (*dev)->GetDeviceProduct(dev, &product);
- kr = (*dev)->GetLocationID(dev, &locationId);
- if (kr == 0) {
- snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X",
- (unsigned int)locationId);
- devpath = devpathBuf;
- }
- kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
-
- if (serialIndex > 0) {
- IOUSBDevRequest req;
- UInt16 buffer[256];
- UInt16 languages[128];
-
- memset(languages, 0, sizeof(languages));
-
- req.bmRequestType =
- USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
- req.bRequest = kUSBRqGetDescriptor;
- req.wValue = (kUSBStringDesc << 8) | 0;
- req.wIndex = 0;
- req.pData = languages;
- req.wLength = sizeof(languages);
- kr = (*dev)->DeviceRequest(dev, &req);
-
- if (kr == kIOReturnSuccess && req.wLenDone > 0) {
-
- int langCount = (req.wLenDone - 2) / 2, lang;
-
- for (lang = 1; lang <= langCount; lang++) {
-
- memset(buffer, 0, sizeof(buffer));
- memset(&req, 0, sizeof(req));
-
- req.bmRequestType =
- USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
- req.bRequest = kUSBRqGetDescriptor;
- req.wValue = (kUSBStringDesc << 8) | serialIndex;
- req.wIndex = languages[lang];
- req.pData = buffer;
- req.wLength = sizeof(buffer);
- kr = (*dev)->DeviceRequest(dev, &req);
-
- if (kr == kIOReturnSuccess && req.wLenDone > 0) {
- int i, count;
-
- // skip first word, and copy the rest to the serial string,
- // changing shorts to bytes.
- count = (req.wLenDone - 1) / 2;
- for (i = 0; i < count; i++)
- serial[i] = buffer[i + 1];
- serial[i] = 0;
- break;
- }
- }
- }
- }
- (*dev)->Release(dev);
-
- DBG("INFO: Found vid=%04x pid=%04x serial=%s\n", vendor, product,
- serial);
-
- usb_handle* handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
- vendor, product);
- if (handle == NULL) {
- DBG("ERR: Could not find device interface: %08x\n", kr);
- (*iface)->Release(iface);
- continue;
- }
-
- DBG("AndroidDeviceAdded calling register_usb_transport\n");
- register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1);
-
- // Register for an interest notification of this device being removed.
- // Pass the reference to our private data as the refCon for the
- // notification.
- kr = IOServiceAddInterestNotification(notificationPort,
- usbInterface,
- kIOGeneralInterest,
- AndroidInterfaceNotify,
- handle,
- &handle->usbNotification);
-
- if (kIOReturnSuccess != kr) {
- DBG("ERR: Unable to create interest notification (%08x)\n", kr);
- }
- }
-}
-
-static void
-AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
-{
- usb_handle *handle = (usb_handle *)refCon;
-
- if (messageType == kIOMessageServiceIsTerminated) {
- if (!handle) {
- DBG("ERR: NULL handle\n");
- return;
- }
- DBG("AndroidInterfaceNotify\n");
- IOObjectRelease(handle->usbNotification);
- usb_kick(handle);
- }
-}
-
-// Used to clear both the endpoints before starting.
-// When adb quits, we might clear the host endpoint but not the device.
-// So we make sure both sides are clear before starting up.
-static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface190** interface, UInt8 bulkEp) {
- IOReturn rc = (*interface)->ClearPipeStallBothEnds(interface, bulkEp);
- if (rc != kIOReturnSuccess) {
- DBG("ERR: Could not clear pipe: (%08x)\n", rc);
- return false;
- }
- return true;
-}
-
-//* TODO: simplify this further since we only register to get ADB interface
-//* subclass+protocol events
-static usb_handle*
-CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product)
-{
- usb_handle* handle;
- IOReturn kr;
- UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
- UInt8 endpoint;
-
- //* Now open the interface. This will cause the pipes associated with
- //* the endpoints in the interface descriptor to be instantiated
- kr = (*interface)->USBInterfaceOpen(interface);
- if (kr != kIOReturnSuccess) {
- DBG("ERR: Could not open interface: (%08x)\n", kr);
- return NULL;
- }
-
- //* Get the number of endpoints associated with this interface
- kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
- if (kr != kIOReturnSuccess) {
- DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
- goto err_get_num_ep;
- }
-
- //* Get interface class, subclass and protocol
- if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
- (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
- (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess) {
- DBG("ERR: Unable to get interface class, subclass and protocol\n");
- goto err_get_interface_class;
- }
-
- //* check to make sure interface class, subclass and protocol match ADB
- //* avoid opening mass storage endpoints
- if (!is_adb_interface(vendor, product, interfaceClass, interfaceSubClass, interfaceProtocol)) {
- goto err_bad_adb_interface;
- }
-
- handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
- if (handle == nullptr) goto err_bad_adb_interface;
-
- //* Iterate over the endpoints for this interface and find the first
- //* bulk in/out pipes available. These will be our read/write pipes.
- for (endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
- UInt8 transferType;
- UInt16 maxPacketSize;
- UInt8 interval;
- UInt8 number;
- UInt8 direction;
-
- kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
- &number, &transferType, &maxPacketSize, &interval);
- if (kr != kIOReturnSuccess) {
- DBG("ERR: FindDeviceInterface - could not get pipe properties (%08x)\n", kr);
- goto err_get_pipe_props;
- }
-
- if (kUSBBulk != transferType) continue;
-
- if (kUSBIn == direction) {
- handle->bulkIn = endpoint;
- if (!ClearPipeStallBothEnds(interface, handle->bulkIn)) goto err_get_pipe_props;
- }
-
- if (kUSBOut == direction) {
- handle->bulkOut = endpoint;
- if (!ClearPipeStallBothEnds(interface, handle->bulkOut)) goto err_get_pipe_props;
- }
-
- handle->zero_mask = maxPacketSize - 1;
- }
-
- handle->interface = interface;
- return handle;
-
-err_get_pipe_props:
- free(handle);
-err_bad_adb_interface:
-err_get_interface_class:
-err_get_num_ep:
- (*interface)->USBInterfaceClose(interface);
- return NULL;
-}
-
-
-void* RunLoopThread(void* unused)
-{
- InitUSB();
-
- currentRunLoop = CFRunLoopGetCurrent();
-
- // Signal the parent that we are running
- adb_mutex_lock(&start_lock);
- adb_cond_signal(&start_cond);
- adb_mutex_unlock(&start_lock);
-
- CFRunLoopRun();
- currentRunLoop = 0;
-
- IOObjectRelease(notificationIterator);
- IONotificationPortDestroy(notificationPort);
-
- DBG("RunLoopThread done\n");
- return NULL;
-}
-
-static void usb_cleanup() {
- DBG("usb_cleanup\n");
- close_usb_devices();
- if (currentRunLoop)
- CFRunLoopStop(currentRunLoop);
-}
-
-void usb_init() {
- static bool initialized = false;
- if (!initialized) {
- atexit(usb_cleanup);
-
- adb_mutex_init(&start_lock, NULL);
- adb_cond_init(&start_cond, NULL);
-
- if (!adb_thread_create(RunLoopThread, nullptr)) {
- fatal_errno("cannot create input thread");
- }
-
- // Wait for initialization to finish
- adb_mutex_lock(&start_lock);
- adb_cond_wait(&start_cond, &start_lock);
- adb_mutex_unlock(&start_lock);
-
- adb_mutex_destroy(&start_lock);
- adb_cond_destroy(&start_cond);
-
- initialized = true;
- }
-}
-
-int usb_write(usb_handle *handle, const void *buf, int len)
-{
- IOReturn result;
-
- if (!len)
- return 0;
-
- if (!handle)
- return -1;
-
- if (NULL == handle->interface) {
- DBG("ERR: usb_write interface was null\n");
- return -1;
- }
-
- if (0 == handle->bulkOut) {
- DBG("ERR: bulkOut endpoint not assigned\n");
- return -1;
- }
-
- result =
- (*handle->interface)->WritePipe(
- handle->interface, handle->bulkOut, (void *)buf, len);
-
- if ((result == 0) && (handle->zero_mask)) {
- /* we need 0-markers and our transfer */
- if(!(len & handle->zero_mask)) {
- result =
- (*handle->interface)->WritePipe(
- handle->interface, handle->bulkOut, (void *)buf, 0);
- }
- }
-
- if (0 == result)
- return 0;
-
- DBG("ERR: usb_write failed with status %d\n", result);
- return -1;
-}
-
-int usb_read(usb_handle *handle, void *buf, int len)
-{
- IOReturn result;
- UInt32 numBytes = len;
-
- if (!len) {
- return 0;
- }
-
- if (!handle) {
- return -1;
- }
-
- if (NULL == handle->interface) {
- DBG("ERR: usb_read interface was null\n");
- return -1;
- }
-
- if (0 == handle->bulkIn) {
- DBG("ERR: bulkIn endpoint not assigned\n");
- return -1;
- }
-
- result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
-
- if (kIOUSBPipeStalled == result) {
- DBG(" Pipe stalled, clearing stall.\n");
- (*handle->interface)->ClearPipeStall(handle->interface, handle->bulkIn);
- result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
- }
-
- if (kIOReturnSuccess == result)
- return 0;
- else {
- DBG("ERR: usb_read failed with status %x\n", result);
- }
-
- return -1;
-}
-
-int usb_close(usb_handle *handle)
-{
- return 0;
-}
-
-void usb_kick(usb_handle *handle)
-{
- /* release the interface */
- if (!handle)
- return;
-
- if (handle->interface)
- {
- (*handle->interface)->USBInterfaceClose(handle->interface);
- (*handle->interface)->Release(handle->interface);
- handle->interface = 0;
- }
-}
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
deleted file mode 100644
index b8cc5cf..0000000
--- a/adb/usb_windows.cpp
+++ /dev/null
@@ -1,666 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_USB
-
-#include "sysdeps.h"
-
-#include <winsock2.h> // winsock.h *must* be included before windows.h.
-#include <adb_api.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <usb100.h>
-#include <windows.h>
-#include <winerror.h>
-
-#include "adb.h"
-#include "transport.h"
-
-/** Structure usb_handle describes our connection to the usb device via
- AdbWinApi.dll. This structure is returned from usb_open() routine and
- is expected in each subsequent call that is accessing the device.
-
- Most members are protected by usb_lock, except for adb_{read,write}_pipe which
- rely on AdbWinApi.dll's handle validation and AdbCloseHandle(endpoint)'s
- ability to break a thread out of pipe IO.
-*/
-struct usb_handle {
- /// Previous entry in the list of opened usb handles
- usb_handle *prev;
-
- /// Next entry in the list of opened usb handles
- usb_handle *next;
-
- /// Handle to USB interface
- ADBAPIHANDLE adb_interface;
-
- /// Handle to USB read pipe (endpoint)
- ADBAPIHANDLE adb_read_pipe;
-
- /// Handle to USB write pipe (endpoint)
- ADBAPIHANDLE adb_write_pipe;
-
- /// Interface name
- char* interface_name;
-
- /// Mask for determining when to use zero length packets
- unsigned zero_mask;
-};
-
-/// Class ID assigned to the device by androidusb.sys
-static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
-
-/// List of opened usb handles
-static usb_handle handle_list = {
- .prev = &handle_list,
- .next = &handle_list,
-};
-
-/// Locker for the list of opened usb handles
-ADB_MUTEX_DEFINE( usb_lock );
-
-/// Checks if there is opened usb handle in handle_list for this device.
-int known_device(const char* dev_name);
-
-/// Checks if there is opened usb handle in handle_list for this device.
-/// usb_lock mutex must be held before calling this routine.
-int known_device_locked(const char* dev_name);
-
-/// Registers opened usb handle (adds it to handle_list).
-int register_new_device(usb_handle* handle);
-
-/// Checks if interface (device) matches certain criteria
-int recognized_device(usb_handle* handle);
-
-/// Enumerates present and available interfaces (devices), opens new ones and
-/// registers usb transport for them.
-void find_devices();
-
-/// Kicks all USB devices
-static void kick_devices();
-
-/// Entry point for thread that polls (every second) for new usb interfaces.
-/// This routine calls find_devices in infinite loop.
-void* device_poll_thread(void* unused);
-
-/// Initializes this module
-void usb_init();
-
-/// Opens usb interface (device) by interface (device) name.
-usb_handle* do_usb_open(const wchar_t* interface_name);
-
-/// Writes data to the opened usb handle
-int usb_write(usb_handle* handle, const void* data, int len);
-
-/// Reads data using the opened usb handle
-int usb_read(usb_handle *handle, void* data, int len);
-
-/// Cleans up opened usb handle
-void usb_cleanup_handle(usb_handle* handle);
-
-/// Cleans up (but don't close) opened usb handle
-void usb_kick(usb_handle* handle);
-
-/// Closes opened usb handle
-int usb_close(usb_handle* handle);
-
-int known_device_locked(const char* dev_name) {
- usb_handle* usb;
-
- if (NULL != dev_name) {
- // Iterate through the list looking for the name match.
- for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
- // In Windows names are not case sensetive!
- if((NULL != usb->interface_name) &&
- (0 == stricmp(usb->interface_name, dev_name))) {
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-int known_device(const char* dev_name) {
- int ret = 0;
-
- if (NULL != dev_name) {
- adb_mutex_lock(&usb_lock);
- ret = known_device_locked(dev_name);
- adb_mutex_unlock(&usb_lock);
- }
-
- return ret;
-}
-
-int register_new_device(usb_handle* handle) {
- if (NULL == handle)
- return 0;
-
- adb_mutex_lock(&usb_lock);
-
- // Check if device is already in the list
- if (known_device_locked(handle->interface_name)) {
- adb_mutex_unlock(&usb_lock);
- return 0;
- }
-
- // Not in the list. Add this handle to the list.
- handle->next = &handle_list;
- handle->prev = handle_list.prev;
- handle->prev->next = handle;
- handle->next->prev = handle;
-
- adb_mutex_unlock(&usb_lock);
-
- return 1;
-}
-
-void* device_poll_thread(void* unused) {
- D("Created device thread\n");
-
- while(1) {
- find_devices();
- adb_sleep_ms(1000);
- }
-
- return NULL;
-}
-
-static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
- LPARAM lParam) {
- switch (uMsg) {
- case WM_POWERBROADCAST:
- switch (wParam) {
- case PBT_APMRESUMEAUTOMATIC:
- // Resuming from sleep or hibernation, so kick all existing USB devices
- // and then allow the device_poll_thread to redetect USB devices from
- // scratch. If we don't do this, existing USB devices will never respond
- // to us because they'll be waiting for the connect/auth handshake.
- D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
- "so kicking all USB devices\n");
- kick_devices();
- return TRUE;
- }
- }
- return DefWindowProcW(hwnd, uMsg, wParam, lParam);
-}
-
-static void* _power_notification_thread(void* unused) {
- // This uses a thread with its own window message pump to get power
- // notifications. If adb runs from a non-interactive service account, this
- // might not work (not sure). If that happens to not work, we could use
- // heavyweight WMI APIs to get power notifications. But for the common case
- // of a developer's interactive session, a window message pump is more
- // appropriate.
- D("Created power notification thread\n");
-
- // Window class names are process specific.
- static const WCHAR kPowerNotificationWindowClassName[] =
- L"PowerNotificationWindow";
-
- // Get the HINSTANCE corresponding to the module that _power_window_proc
- // is in (the main module).
- const HINSTANCE instance = GetModuleHandleW(NULL);
- if (!instance) {
- // This is such a common API call that this should never fail.
- fatal("GetModuleHandleW failed: %s",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
-
- WNDCLASSEXW wndclass;
- memset(&wndclass, 0, sizeof(wndclass));
- wndclass.cbSize = sizeof(wndclass);
- wndclass.lpfnWndProc = _power_window_proc;
- wndclass.hInstance = instance;
- wndclass.lpszClassName = kPowerNotificationWindowClassName;
- if (!RegisterClassExW(&wndclass)) {
- fatal("RegisterClassExW failed: %s",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
-
- if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
- L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0,
- NULL, NULL, instance, NULL)) {
- fatal("CreateWindowExW failed: %s",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
-
- MSG msg;
- while (GetMessageW(&msg, NULL, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessageW(&msg);
- }
-
- // GetMessageW() will return false if a quit message is posted. We don't
- // do that, but it might be possible for that to occur when logging off or
- // shutting down. Not a big deal since the whole process will be going away
- // soon anyway.
- D("Power notification thread exiting\n");
-
- return NULL;
-}
-
-void usb_init() {
- if (!adb_thread_create(device_poll_thread, nullptr)) {
- fatal_errno("cannot create device poll thread");
- }
- if (!adb_thread_create(_power_notification_thread, nullptr)) {
- fatal_errno("cannot create power notification thread");
- }
-}
-
-usb_handle* do_usb_open(const wchar_t* interface_name) {
- unsigned long name_len = 0;
-
- // Allocate our handle
- usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
- if (NULL == ret) {
- D("Could not allocate %u bytes for usb_handle: %s\n", sizeof(usb_handle),
- strerror(errno));
- goto fail;
- }
-
- // Set linkers back to the handle
- ret->next = ret;
- ret->prev = ret;
-
- // Create interface.
- ret->adb_interface = AdbCreateInterfaceByName(interface_name);
- if (NULL == ret->adb_interface) {
- D("AdbCreateInterfaceByName failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- goto fail;
- }
-
- // Open read pipe (endpoint)
- ret->adb_read_pipe =
- AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
- AdbOpenAccessTypeReadWrite,
- AdbOpenSharingModeReadWrite);
- if (NULL == ret->adb_read_pipe) {
- D("AdbOpenDefaultBulkReadEndpoint failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- goto fail;
- }
-
- // Open write pipe (endpoint)
- ret->adb_write_pipe =
- AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
- AdbOpenAccessTypeReadWrite,
- AdbOpenSharingModeReadWrite);
- if (NULL == ret->adb_write_pipe) {
- D("AdbOpenDefaultBulkWriteEndpoint failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- goto fail;
- }
-
- // Save interface name
- // First get expected name length
- AdbGetInterfaceName(ret->adb_interface,
- NULL,
- &name_len,
- true);
- if (0 == name_len) {
- D("AdbGetInterfaceName returned name length of zero: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- goto fail;
- }
-
- ret->interface_name = (char*)malloc(name_len);
- if (NULL == ret->interface_name) {
- D("Could not allocate %lu bytes for interface_name: %s\n", name_len,
- strerror(errno));
- goto fail;
- }
-
- // Now save the name
- if (!AdbGetInterfaceName(ret->adb_interface,
- ret->interface_name,
- &name_len,
- true)) {
- D("AdbGetInterfaceName failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- goto fail;
- }
-
- // We're done at this point
- return ret;
-
-fail:
- if (NULL != ret) {
- usb_cleanup_handle(ret);
- free(ret);
- }
-
- return NULL;
-}
-
-int usb_write(usb_handle* handle, const void* data, int len) {
- unsigned long time_out = 5000;
- unsigned long written = 0;
- int err = 0;
-
- D("usb_write %d\n", len);
- if (NULL == handle) {
- D("usb_write was passed NULL handle\n");
- err = EINVAL;
- goto fail;
- }
-
- // Perform write
- if (!AdbWriteEndpointSync(handle->adb_write_pipe,
- (void*)data,
- (unsigned long)len,
- &written,
- time_out)) {
- D("AdbWriteEndpointSync failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- err = EIO;
- goto fail;
- }
-
- // Make sure that we've written what we were asked to write
- D("usb_write got: %ld, expected: %d\n", written, len);
- if (written != (unsigned long)len) {
- // If this occurs, this code should be changed to repeatedly call
- // AdbWriteEndpointSync() until all bytes are written.
- D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld\n",
- len, written);
- err = EIO;
- goto fail;
- }
-
- if (handle->zero_mask && (len & handle->zero_mask) == 0) {
- // Send a zero length packet
- if (!AdbWriteEndpointSync(handle->adb_write_pipe,
- (void*)data,
- 0,
- &written,
- time_out)) {
- D("AdbWriteEndpointSync of zero length packet failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- err = EIO;
- goto fail;
- }
- }
-
- return 0;
-
-fail:
- // Any failure should cause us to kick the device instead of leaving it a
- // zombie state with potential to hang.
- if (NULL != handle) {
- D("Kicking device due to error in usb_write\n");
- usb_kick(handle);
- }
-
- D("usb_write failed\n");
- errno = err;
- return -1;
-}
-
-int usb_read(usb_handle *handle, void* data, int len) {
- unsigned long time_out = 0;
- unsigned long read = 0;
- int err = 0;
-
- D("usb_read %d\n", len);
- if (NULL == handle) {
- D("usb_read was passed NULL handle\n");
- err = EINVAL;
- goto fail;
- }
-
- while (len > 0) {
- if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
- time_out)) {
- D("AdbReadEndpointSync failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- err = EIO;
- goto fail;
- }
- D("usb_read got: %ld, expected: %d\n", read, len);
-
- data = (char *)data + read;
- len -= read;
- }
-
- return 0;
-
-fail:
- // Any failure should cause us to kick the device instead of leaving it a
- // zombie state with potential to hang.
- if (NULL != handle) {
- D("Kicking device due to error in usb_read\n");
- usb_kick(handle);
- }
-
- D("usb_read failed\n");
- errno = err;
- return -1;
-}
-
-// Wrapper around AdbCloseHandle() that logs diagnostics.
-static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
- if (!AdbCloseHandle(adb_handle)) {
- D("AdbCloseHandle(%p) failed: %s\n", adb_handle,
- SystemErrorCodeToString(GetLastError()).c_str());
- }
-}
-
-void usb_cleanup_handle(usb_handle* handle) {
- D("usb_cleanup_handle\n");
- if (NULL != handle) {
- if (NULL != handle->interface_name)
- free(handle->interface_name);
- // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
- // wait until the pipe no longer uses the interface. Then we can
- // AdbCloseHandle() the interface.
- if (NULL != handle->adb_write_pipe)
- _adb_close_handle(handle->adb_write_pipe);
- if (NULL != handle->adb_read_pipe)
- _adb_close_handle(handle->adb_read_pipe);
- if (NULL != handle->adb_interface)
- _adb_close_handle(handle->adb_interface);
-
- handle->interface_name = NULL;
- handle->adb_write_pipe = NULL;
- handle->adb_read_pipe = NULL;
- handle->adb_interface = NULL;
- }
-}
-
-static void usb_kick_locked(usb_handle* handle) {
- // The reason the lock must be acquired before calling this function is in
- // case multiple threads are trying to kick the same device at the same time.
- usb_cleanup_handle(handle);
-}
-
-void usb_kick(usb_handle* handle) {
- D("usb_kick\n");
- if (NULL != handle) {
- adb_mutex_lock(&usb_lock);
-
- usb_kick_locked(handle);
-
- adb_mutex_unlock(&usb_lock);
- } else {
- errno = EINVAL;
- }
-}
-
-int usb_close(usb_handle* handle) {
- D("usb_close\n");
-
- if (NULL != handle) {
- // Remove handle from the list
- adb_mutex_lock(&usb_lock);
-
- if ((handle->next != handle) && (handle->prev != handle)) {
- handle->next->prev = handle->prev;
- handle->prev->next = handle->next;
- handle->prev = handle;
- handle->next = handle;
- }
-
- adb_mutex_unlock(&usb_lock);
-
- // Cleanup handle
- usb_cleanup_handle(handle);
- free(handle);
- }
-
- return 0;
-}
-
-int recognized_device(usb_handle* handle) {
- if (NULL == handle)
- return 0;
-
- // Check vendor and product id first
- USB_DEVICE_DESCRIPTOR device_desc;
-
- if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
- &device_desc)) {
- D("AdbGetUsbDeviceDescriptor failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- return 0;
- }
-
- // Then check interface properties
- USB_INTERFACE_DESCRIPTOR interf_desc;
-
- if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
- &interf_desc)) {
- D("AdbGetUsbInterfaceDescriptor failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- return 0;
- }
-
- // Must have two endpoints
- if (2 != interf_desc.bNumEndpoints) {
- return 0;
- }
-
- if (is_adb_interface(device_desc.idVendor, device_desc.idProduct,
- interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass, interf_desc.bInterfaceProtocol)) {
-
- if(interf_desc.bInterfaceProtocol == 0x01) {
- AdbEndpointInformation endpoint_info;
- // assuming zero is a valid bulk endpoint ID
- if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
- handle->zero_mask = endpoint_info.max_packet_size - 1;
- D("device zero_mask: 0x%x\n", handle->zero_mask);
- } else {
- D("AdbGetEndpointInformation failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
- }
-
- return 1;
- }
-
- return 0;
-}
-
-void find_devices() {
- usb_handle* handle = NULL;
- char entry_buffer[2048];
- char interf_name[2048];
- AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
- unsigned long entry_buffer_size = sizeof(entry_buffer);
- char* copy_name;
-
- // Enumerate all present and active interfaces.
- ADBAPIHANDLE enum_handle =
- AdbEnumInterfaces(usb_class_id, true, true, true);
-
- if (NULL == enum_handle) {
- D("AdbEnumInterfaces failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- return;
- }
-
- while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
- // TODO: FIXME - temp hack converting wchar_t into char.
- // It would be better to change AdbNextInterface so it will return
- // interface name as single char string.
- const wchar_t* wchar_name = next_interface->device_name;
- for(copy_name = interf_name;
- L'\0' != *wchar_name;
- wchar_name++, copy_name++) {
- *copy_name = (char)(*wchar_name);
- }
- *copy_name = '\0';
-
- // Lets see if we already have this device in the list
- if (!known_device(interf_name)) {
- // This seems to be a new device. Open it!
- handle = do_usb_open(next_interface->device_name);
- if (NULL != handle) {
- // Lets see if this interface (device) belongs to us
- if (recognized_device(handle)) {
- D("adding a new device %s\n", interf_name);
- char serial_number[512];
- unsigned long serial_number_len = sizeof(serial_number);
- if (AdbGetSerialNumber(handle->adb_interface,
- serial_number,
- &serial_number_len,
- true)) {
- // Lets make sure that we don't duplicate this device
- if (register_new_device(handle)) {
- register_usb_transport(handle, serial_number, NULL, 1);
- } else {
- D("register_new_device failed for %s\n", interf_name);
- usb_cleanup_handle(handle);
- free(handle);
- }
- } else {
- D("cannot get serial number: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- usb_cleanup_handle(handle);
- free(handle);
- }
- } else {
- usb_cleanup_handle(handle);
- free(handle);
- }
- }
- }
-
- entry_buffer_size = sizeof(entry_buffer);
- }
-
- if (GetLastError() != ERROR_NO_MORE_ITEMS) {
- // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
- D("AdbNextInterface failed: %s\n",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
-
- _adb_close_handle(enum_handle);
-}
-
-static void kick_devices() {
- // Need to acquire lock to safely walk the list which might be modified
- // by another thread.
- adb_mutex_lock(&usb_lock);
- for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
- usb_kick_locked(usb);
- }
- adb_mutex_unlock(&usb_lock);
-}
diff --git a/adf/Android.bp b/adf/Android.bp
new file mode 100644
index 0000000..b44c296
--- /dev/null
+++ b/adf/Android.bp
@@ -0,0 +1 @@
+subdirs = ["*"]
diff --git a/adf/Android.mk b/adf/Android.mk
deleted file mode 100644
index 64d486e..0000000
--- a/adf/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp
new file mode 100644
index 0000000..1a81a49
--- /dev/null
+++ b/adf/libadf/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+ name: "libadf",
+ srcs: ["adf.c"],
+ cflags: ["-Werror"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+subdirs = ["tests"]
diff --git a/adf/libadf/Android.mk b/adf/libadf/Android.mk
deleted file mode 100644
index 7df354b..0000000
--- a/adf/libadf/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := adf.c
-LOCAL_MODULE := libadf
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -Werror
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-include $(BUILD_STATIC_LIBRARY)
diff --git a/adf/libadf/include/video/adf.h b/adf/libadf/include/video/adf.h
new file mode 100644
index 0000000..77203a5
--- /dev/null
+++ b/adf/libadf/include/video/adf.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_VIDEO_ADF_H_
+#define _UAPI_VIDEO_ADF_H_
+#include <linux/ioctl.h>
+#include <linux/types.h>
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#include <drm/drm_fourcc.h>
+#include <drm/drm_mode.h>
+#define ADF_NAME_LEN 32
+#define ADF_MAX_CUSTOM_DATA_SIZE 4096
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+enum adf_interface_type {
+ ADF_INTF_DSI = 0,
+ ADF_INTF_eDP = 1,
+ ADF_INTF_DPI = 2,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ADF_INTF_VGA = 3,
+ ADF_INTF_DVI = 4,
+ ADF_INTF_HDMI = 5,
+ ADF_INTF_MEMORY = 6,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
+ ADF_INTF_TYPE_MAX = (~(__u32) 0),
+};
+#define ADF_INTF_FLAG_PRIMARY (1 << 0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
+enum adf_event_type {
+ ADF_EVENT_VSYNC = 0,
+ ADF_EVENT_HOTPLUG = 1,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ADF_EVENT_DEVICE_CUSTOM = 128,
+ ADF_EVENT_TYPE_MAX = 255,
+};
+struct adf_set_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u8 type;
+ __u8 enabled;
+};
+struct adf_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u8 type;
+ __u32 length;
+};
+struct adf_vsync_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct adf_event base;
+ __aligned_u64 timestamp;
+};
+struct adf_hotplug_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct adf_event base;
+ __u8 connected;
+};
+#define ADF_MAX_PLANES 4
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_buffer_config {
+ __u32 overlay_engine;
+ __u32 w;
+ __u32 h;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u32 format;
+ __s32 fd[ADF_MAX_PLANES];
+ __u32 offset[ADF_MAX_PLANES];
+ __u32 pitch[ADF_MAX_PLANES];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u8 n_planes;
+ __s32 acquire_fence;
+};
+#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_post_config {
+ size_t n_interfaces;
+ __u32 __user * interfaces;
+ size_t n_bufs;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct adf_buffer_config __user * bufs;
+ size_t custom_data_size;
+ void __user * custom_data;
+ __s32 complete_fence;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
+struct adf_simple_buffer_alloc {
+ __u16 w;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u16 h;
+ __u32 format;
+ __s32 fd;
+ __u32 offset;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u32 pitch;
+};
+struct adf_simple_post_config {
+ struct adf_buffer_config buf;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __s32 complete_fence;
+};
+struct adf_attachment_config {
+ __u32 overlay_engine;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u32 interface;
+};
+struct adf_device_data {
+ char name[ADF_NAME_LEN];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t n_attachments;
+ struct adf_attachment_config __user * attachments;
+ size_t n_allowed_attachments;
+ struct adf_attachment_config __user * allowed_attachments;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t custom_data_size;
+ void __user * custom_data;
+};
+#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_interface_data {
+ char name[ADF_NAME_LEN];
+ __u32 type;
+ __u32 id;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u32 flags;
+ __u8 dpms_state;
+ __u8 hotplug_detect;
+ __u16 width_mm;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u16 height_mm;
+ struct drm_mode_modeinfo current_mode;
+ size_t n_available_modes;
+ struct drm_mode_modeinfo __user * available_modes;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t custom_data_size;
+ void __user * custom_data;
+};
+#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_overlay_engine_data {
+ char name[ADF_NAME_LEN];
+ size_t n_supported_formats;
+ __u32 __user * supported_formats;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t custom_data_size;
+ void __user * custom_data;
+};
+#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_IOCTL_TYPE 'D'
+#define ADF_IOCTL_NR_CUSTOM 128
+#define ADF_SET_EVENT _IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
+#define ADF_BLANK _IOW(ADF_IOCTL_TYPE, 1, __u8)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
+#define ADF_SET_MODE _IOW(ADF_IOCTL_TYPE, 3, struct drm_mode_modeinfo)
+#define ADF_GET_DEVICE_DATA _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
+#define ADF_GET_INTERFACE_DATA _IOR(ADF_IOCTL_TYPE, 5, struct adf_interface_data)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_GET_OVERLAY_ENGINE_DATA _IOR(ADF_IOCTL_TYPE, 6, struct adf_overlay_engine_data)
+#define ADF_SIMPLE_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 7, struct adf_simple_post_config)
+#define ADF_SIMPLE_BUFFER_ALLOC _IOW(ADF_IOCTL_TYPE, 8, struct adf_simple_buffer_alloc)
+#define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, struct adf_attachment_config)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, struct adf_attachment_config)
+#endif
diff --git a/adf/libadf/original-kernel-headers/video/adf.h b/adf/libadf/original-kernel-headers/video/adf.h
new file mode 100644
index 0000000..c5d2e62
--- /dev/null
+++ b/adf/libadf/original-kernel-headers/video/adf.h
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_VIDEO_ADF_H_
+#define _UAPI_VIDEO_ADF_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_mode.h>
+
+#define ADF_NAME_LEN 32
+#define ADF_MAX_CUSTOM_DATA_SIZE 4096
+
+enum adf_interface_type {
+ ADF_INTF_DSI = 0,
+ ADF_INTF_eDP = 1,
+ ADF_INTF_DPI = 2,
+ ADF_INTF_VGA = 3,
+ ADF_INTF_DVI = 4,
+ ADF_INTF_HDMI = 5,
+ ADF_INTF_MEMORY = 6,
+ ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
+ ADF_INTF_TYPE_MAX = (~(__u32)0),
+};
+
+#define ADF_INTF_FLAG_PRIMARY (1 << 0)
+#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
+
+enum adf_event_type {
+ ADF_EVENT_VSYNC = 0,
+ ADF_EVENT_HOTPLUG = 1,
+ ADF_EVENT_DEVICE_CUSTOM = 128,
+ ADF_EVENT_TYPE_MAX = 255,
+};
+
+/**
+ * struct adf_set_event - start or stop subscribing to ADF events
+ *
+ * @type: the type of event to (un)subscribe
+ * @enabled: subscribe or unsubscribe
+ *
+ * After subscribing to an event, userspace may poll() the ADF object's fd
+ * to wait for events or read() to consume the event's data.
+ *
+ * ADF reserves event types 0 to %ADF_EVENT_DEVICE_CUSTOM-1 for its own events.
+ * Devices may use event types %ADF_EVENT_DEVICE_CUSTOM to %ADF_EVENT_TYPE_MAX-1
+ * for driver-private events.
+ */
+struct adf_set_event {
+ __u8 type;
+ __u8 enabled;
+};
+
+/**
+ * struct adf_event - common header for ADF event data
+ *
+ * @type: event type
+ * @length: total size of event data, header inclusive
+ */
+struct adf_event {
+ __u8 type;
+ __u32 length;
+};
+
+/**
+ * struct adf_vsync_event - ADF vsync event
+ *
+ * @base: event header (see &struct adf_event)
+ * @timestamp: time of vsync event, in nanoseconds
+ */
+struct adf_vsync_event {
+ struct adf_event base;
+ __aligned_u64 timestamp;
+};
+
+/**
+ * struct adf_vsync_event - ADF display hotplug event
+ *
+ * @base: event header (see &struct adf_event)
+ * @connected: whether a display is now connected to the interface
+ */
+struct adf_hotplug_event {
+ struct adf_event base;
+ __u8 connected;
+};
+
+#define ADF_MAX_PLANES 4
+/**
+ * struct adf_buffer_config - description of buffer displayed by adf_post_config
+ *
+ * @overlay_engine: id of the target overlay engine
+ * @w: width of display region in pixels
+ * @h: height of display region in pixels
+ * @format: DRM-style fourcc, see drm_fourcc.h for standard formats
+ * @fd: dma_buf fd for each plane
+ * @offset: location of first pixel to scan out, in bytes
+ * @pitch: stride (i.e. length of a scanline including padding) in bytes
+ * @n_planes: number of planes in buffer
+ * @acquire_fence: sync_fence fd which will clear when the buffer is
+ * ready for display, or <0 if the buffer is already ready
+ */
+struct adf_buffer_config {
+ __u32 overlay_engine;
+
+ __u32 w;
+ __u32 h;
+ __u32 format;
+
+ __s32 fd[ADF_MAX_PLANES];
+ __u32 offset[ADF_MAX_PLANES];
+ __u32 pitch[ADF_MAX_PLANES];
+ __u8 n_planes;
+
+ __s32 acquire_fence;
+};
+#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
+
+/**
+ * struct adf_post_config - request to flip to a new set of buffers
+ *
+ * @n_interfaces: number of interfaces targeted by the flip (input)
+ * @interfaces: ids of interfaces targeted by the flip (input)
+ * @n_bufs: number of buffers displayed (input)
+ * @bufs: description of buffers displayed (input)
+ * @custom_data_size: size of driver-private data (input)
+ * @custom_data: driver-private data (input)
+ * @complete_fence: sync_fence fd which will clear when this
+ * configuration has left the screen (output)
+ */
+struct adf_post_config {
+ size_t n_interfaces;
+ __u32 __user *interfaces;
+
+ size_t n_bufs;
+ struct adf_buffer_config __user *bufs;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+
+ __s32 complete_fence;
+};
+#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
+
+/**
+ * struct adf_simple_buffer_allocate - request to allocate a "simple" buffer
+ *
+ * @w: width of buffer in pixels (input)
+ * @h: height of buffer in pixels (input)
+ * @format: DRM-style fourcc (input)
+ *
+ * @fd: dma_buf fd (output)
+ * @offset: location of first pixel, in bytes (output)
+ * @pitch: length of a scanline including padding, in bytes (output)
+ *
+ * Simple buffers are analogous to DRM's "dumb" buffers. They have a single
+ * plane of linear RGB data which can be allocated and scanned out without
+ * any driver-private ioctls or data.
+ *
+ * @format must be a standard RGB format defined in drm_fourcc.h.
+ *
+ * ADF clients must NOT assume that an interface can scan out a simple buffer
+ * allocated by a different ADF interface, even if the two interfaces belong to
+ * the same ADF device.
+ */
+struct adf_simple_buffer_alloc {
+ __u16 w;
+ __u16 h;
+ __u32 format;
+
+ __s32 fd;
+ __u32 offset;
+ __u32 pitch;
+};
+
+/**
+ * struct adf_simple_post_config - request to flip to a single buffer without
+ * driver-private data
+ *
+ * @buf: description of buffer displayed (input)
+ * @complete_fence: sync_fence fd which will clear when this buffer has left the
+ * screen (output)
+ */
+struct adf_simple_post_config {
+ struct adf_buffer_config buf;
+ __s32 complete_fence;
+};
+
+/**
+ * struct adf_attachment_config - description of attachment between an overlay
+ * engine and an interface
+ *
+ * @overlay_engine: id of the overlay engine
+ * @interface: id of the interface
+ */
+struct adf_attachment_config {
+ __u32 overlay_engine;
+ __u32 interface;
+};
+
+/**
+ * struct adf_device_data - describes a display device
+ *
+ * @name: display device's name
+ * @n_attachments: the number of current attachments
+ * @attachments: list of current attachments
+ * @n_allowed_attachments: the number of allowed attachments
+ * @allowed_attachments: list of allowed attachments
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_device_data {
+ char name[ADF_NAME_LEN];
+
+ size_t n_attachments;
+ struct adf_attachment_config __user *attachments;
+
+ size_t n_allowed_attachments;
+ struct adf_attachment_config __user *allowed_attachments;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config))
+
+/**
+ * struct adf_device_data - describes a display interface
+ *
+ * @name: display interface's name
+ * @type: interface type (see enum @adf_interface_type)
+ * @id: which interface of type @type;
+ * e.g. interface DSI.1 -> @type=@ADF_INTF_TYPE_DSI, @id=1
+ * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values)
+ * @dpms_state: DPMS state (one of @DRM_MODE_DPMS_* defined in drm_mode.h)
+ * @hotplug_detect: whether a display is plugged in
+ * @width_mm: screen width in millimeters, or 0 if unknown
+ * @height_mm: screen height in millimeters, or 0 if unknown
+ * @current_mode: current display mode
+ * @n_available_modes: the number of hardware display modes
+ * @available_modes: list of hardware display modes
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_interface_data {
+ char name[ADF_NAME_LEN];
+
+ __u32 type;
+ __u32 id;
+ /* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */
+ __u32 flags;
+
+ __u8 dpms_state;
+ __u8 hotplug_detect;
+ __u16 width_mm;
+ __u16 height_mm;
+
+ struct drm_mode_modeinfo current_mode;
+ size_t n_available_modes;
+ struct drm_mode_modeinfo __user *available_modes;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo))
+
+/**
+ * struct adf_overlay_engine_data - describes an overlay engine
+ *
+ * @name: overlay engine's name
+ * @n_supported_formats: number of supported formats
+ * @supported_formats: list of supported formats
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_overlay_engine_data {
+ char name[ADF_NAME_LEN];
+
+ size_t n_supported_formats;
+ __u32 __user *supported_formats;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
+
+#define ADF_IOCTL_TYPE 'D'
+#define ADF_IOCTL_NR_CUSTOM 128
+
+#define ADF_SET_EVENT _IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
+#define ADF_BLANK _IOW(ADF_IOCTL_TYPE, 1, __u8)
+#define ADF_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
+#define ADF_SET_MODE _IOW(ADF_IOCTL_TYPE, 3, \
+ struct drm_mode_modeinfo)
+#define ADF_GET_DEVICE_DATA _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
+#define ADF_GET_INTERFACE_DATA _IOR(ADF_IOCTL_TYPE, 5, \
+ struct adf_interface_data)
+#define ADF_GET_OVERLAY_ENGINE_DATA \
+ _IOR(ADF_IOCTL_TYPE, 6, \
+ struct adf_overlay_engine_data)
+#define ADF_SIMPLE_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 7, \
+ struct adf_simple_post_config)
+#define ADF_SIMPLE_BUFFER_ALLOC _IOW(ADF_IOCTL_TYPE, 8, \
+ struct adf_simple_buffer_alloc)
+#define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, \
+ struct adf_attachment_config)
+#define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, \
+ struct adf_attachment_config)
+
+#endif /* _UAPI_VIDEO_ADF_H_ */
diff --git a/adf/libadf/tests/Android.bp b/adf/libadf/tests/Android.bp
new file mode 100644
index 0000000..7b33300
--- /dev/null
+++ b/adf/libadf/tests/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "adf-unit-tests",
+ srcs: ["adf_test.cpp"],
+ static_libs: ["libadf"],
+ cflags: ["-Werror"],
+}
diff --git a/adf/libadf/tests/Android.mk b/adf/libadf/tests/Android.mk
deleted file mode 100644
index 68e5817..0000000
--- a/adf/libadf/tests/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := adf_test.cpp
-LOCAL_MODULE := adf-unit-tests
-LOCAL_STATIC_LIBRARIES := libadf
-LOCAL_CFLAGS += -Werror
-include $(BUILD_NATIVE_TEST)
diff --git a/adf/libadf/tests/adf_test.cpp b/adf/libadf/tests/adf_test.cpp
index 01b2785..eaa9342 100644
--- a/adf/libadf/tests/adf_test.cpp
+++ b/adf/libadf/tests/adf_test.cpp
@@ -149,11 +149,13 @@
int eng;
private:
- const static adf_id_t dev_id = 0;
+ const static adf_id_t dev_id;
const static __u32 fmt8888[];
const static size_t n_fmt8888;
};
+const adf_id_t AdfTest::dev_id = 0;
+
const __u32 AdfTest::fmt8888[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
diff --git a/adf/libadfhwc/Android.bp b/adf/libadfhwc/Android.bp
new file mode 100644
index 0000000..57a8d76
--- /dev/null
+++ b/adf/libadfhwc/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+ name: "libadfhwc",
+ srcs: ["adfhwc.cpp"],
+ static_libs: [
+ "libadf",
+ "liblog",
+ "libutils",
+ ],
+ cflags: [
+ "-DLOG_TAG=\"adfhwc\"",
+ "-Werror",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
diff --git a/adf/libadfhwc/Android.mk b/adf/libadfhwc/Android.mk
deleted file mode 100644
index 898f9c9..0000000
--- a/adf/libadfhwc/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := adfhwc.cpp
-LOCAL_MODULE := libadfhwc
-LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_LIBRARIES := libadf liblog libutils
-LOCAL_CFLAGS += -DLOG_TAG=\"adfhwc\" -Werror
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-include $(BUILD_STATIC_LIBRARY)
diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp
index 21f245e..a97862a 100644
--- a/adf/libadfhwc/adfhwc.cpp
+++ b/adf/libadfhwc/adfhwc.cpp
@@ -20,12 +20,12 @@
#include <pthread.h>
#include <sys/resource.h>
+#include <log/log.h>
+#include <utils/Vector.h>
+
#include <adf/adf.h>
#include <adfhwc/adfhwc.h>
-#include <cutils/log.h>
-#include <utils/Vector.h>
-
struct adf_hwc_helper {
adf_hwc_event_callbacks const *event_cb;
void *event_cb_data;
diff --git a/base/Android.bp b/base/Android.bp
new file mode 100644
index 0000000..b9a6e0b
--- /dev/null
+++ b/base/Android.bp
@@ -0,0 +1,110 @@
+//
+// 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.
+//
+
+libbase_cppflags = [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+]
+
+cc_library {
+ name: "libbase",
+ clang: true,
+ host_supported: true,
+ srcs: [
+ "file.cpp",
+ "logging.cpp",
+ "parsenetaddress.cpp",
+ "quick_exit.cpp",
+ "stringprintf.cpp",
+ "strings.cpp",
+ "test_utils.cpp",
+ ],
+ local_include_dirs: ["include"],
+ cppflags: libbase_cppflags,
+ export_include_dirs: ["include"],
+ shared_libs: ["liblog"],
+ target: {
+ android: {
+ srcs: [
+ "errors_unix.cpp",
+ "properties.cpp",
+ ],
+ cppflags: ["-Wexit-time-destructors"],
+ },
+ darwin: {
+ srcs: ["errors_unix.cpp"],
+ cppflags: ["-Wexit-time-destructors"],
+ },
+ linux_bionic: {
+ srcs: ["errors_unix.cpp"],
+ cppflags: ["-Wexit-time-destructors"],
+ enabled: true,
+ },
+ linux: {
+ srcs: ["errors_unix.cpp"],
+ cppflags: ["-Wexit-time-destructors"],
+ },
+ windows: {
+ srcs: [
+ "errors_windows.cpp",
+ "utf8.cpp",
+ ],
+ enabled: true,
+ },
+ },
+}
+
+// Tests
+// ------------------------------------------------------------------------------
+cc_test {
+ name: "libbase_test",
+ host_supported: true,
+ clang: true,
+ srcs: [
+ "errors_test.cpp",
+ "file_test.cpp",
+ "logging_test.cpp",
+ "parsedouble_test.cpp",
+ "parseint_test.cpp",
+ "parsenetaddress_test.cpp",
+ "quick_exit_test.cpp",
+ "stringprintf_test.cpp",
+ "strings_test.cpp",
+ "test_main.cpp",
+ ],
+ target: {
+ android: {
+ srcs: ["properties_test.cpp"],
+ },
+ windows: {
+ srcs: ["utf8_test.cpp"],
+ enabled: true,
+ },
+ },
+ local_include_dirs: ["."],
+ cppflags: libbase_cppflags,
+ shared_libs: ["libbase"],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+}
diff --git a/base/Android.mk b/base/Android.mk
deleted file mode 100644
index 4e135f6..0000000
--- a/base/Android.mk
+++ /dev/null
@@ -1,105 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-libbase_src_files := \
- file.cpp \
- logging.cpp \
- stringprintf.cpp \
- strings.cpp \
- test_utils.cpp \
-
-libbase_test_src_files := \
- file_test.cpp \
- logging_test.cpp \
- stringprintf_test.cpp \
- strings_test.cpp \
- test_main.cpp \
-
-libbase_cppflags := \
- -Wall \
- -Wextra \
- -Werror \
-
-# Device
-# ------------------------------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(libbase_src_files)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(libbase_cppflags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libcutils
-LOCAL_MULTILIB := both
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase
-LOCAL_CLANG := true
-LOCAL_WHOLE_STATIC_LIBRARIES := libbase
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := libcutils
-LOCAL_MULTILIB := both
-include $(BUILD_SHARED_LIBRARY)
-
-# Host
-# ------------------------------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase
-LOCAL_SRC_FILES := $(libbase_src_files)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(libbase_cppflags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libcutils
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase
-LOCAL_WHOLE_STATIC_LIBRARIES := libbase
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libcutils
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-# Tests
-# ------------------------------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase_test
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(libbase_test_src_files)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)
-LOCAL_CPPFLAGS := $(libbase_cppflags)
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase_test
-LOCAL_SRC_FILES := $(libbase_test_src_files)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)
-LOCAL_CPPFLAGS := $(libbase_cppflags)
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/base/errors_test.cpp b/base/errors_test.cpp
new file mode 100644
index 0000000..8e7cdd1
--- /dev/null
+++ b/base/errors_test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/errors.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+// Error strings aren't consistent enough across systems to test the output,
+// just make sure we can compile correctly and nothing crashes even if we send
+// it possibly bogus error codes.
+TEST(ErrorsTest, TestSystemErrorString) {
+ SystemErrorCodeToString(-1);
+ SystemErrorCodeToString(0);
+ SystemErrorCodeToString(1);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/errors_unix.cpp b/base/errors_unix.cpp
new file mode 100644
index 0000000..296995e
--- /dev/null
+++ b/base/errors_unix.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/errors.h"
+
+#include <errno.h>
+
+namespace android {
+namespace base {
+
+std::string SystemErrorCodeToString(int error_code) {
+ return strerror(error_code);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/errors_windows.cpp b/base/errors_windows.cpp
new file mode 100644
index 0000000..a5ff511
--- /dev/null
+++ b/base/errors_windows.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/errors.h"
+
+#include <windows.h>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+#include "android-base/utf8.h"
+
+// A Windows error code is a DWORD. It's simpler to use an int error code for
+// both Unix and Windows if possible, but if this fails we'll need a different
+// function signature for each.
+static_assert(sizeof(int) >= sizeof(DWORD),
+ "Windows system error codes are too large to fit in an int.");
+
+namespace android {
+namespace base {
+
+static constexpr DWORD kErrorMessageBufferSize = 256;
+
+std::string SystemErrorCodeToString(int int_error_code) {
+ WCHAR msgbuf[kErrorMessageBufferSize];
+ DWORD error_code = int_error_code;
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+ DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
+ kErrorMessageBufferSize, nullptr);
+ if (len == 0) {
+ return android::base::StringPrintf(
+ "Error %lu while retrieving message for error %lu", GetLastError(),
+ error_code);
+ }
+
+ // Convert UTF-16 to UTF-8.
+ std::string msg;
+ if (!android::base::WideToUTF8(msgbuf, &msg)) {
+ return android::base::StringPrintf(
+ "Error %lu while converting message for error %lu from UTF-16 to UTF-8",
+ GetLastError(), error_code);
+ }
+
+ // Messages returned by the system end with line breaks.
+ msg = android::base::Trim(msg);
+
+ // There are many Windows error messages compared to POSIX, so include the
+ // numeric error code for easier, quicker, accurate identification. Use
+ // decimal instead of hex because there are decimal ranges like 10000-11999
+ // for Winsock.
+ android::base::StringAppendF(&msg, " (%lu)", error_code);
+ return msg;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/file.cpp b/base/file.cpp
index 9a340b7..6284b04 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -14,23 +14,36 @@
* limitations under the License.
*/
-#include "base/file.h"
+#include "android-base/file.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <unistd.h>
+#include <memory>
#include <string>
+#include <vector>
-#include "base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
-#define LOG_TAG "base.file"
-#include "cutils/log.h"
+#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/logging.h"
+#include "android-base/utf8.h"
#include "utils/Compat.h"
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#endif
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
namespace android {
namespace base {
+// Versions of standard library APIs that support UTF-8 strings.
+using namespace android::base::utf8;
+
bool ReadFdToString(int fd, std::string* content) {
content->clear();
@@ -42,11 +55,11 @@
return (n == 0) ? true : false;
}
-bool ReadFileToString(const std::string& path, std::string* content) {
+bool ReadFileToString(const std::string& path, std::string* content, bool follow_symlinks) {
content->clear();
- int fd =
- TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
+ int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags));
if (fd == -1) {
return false;
}
@@ -79,27 +92,28 @@
#if !defined(_WIN32)
bool WriteStringToFile(const std::string& content, const std::string& path,
- mode_t mode, uid_t owner, gid_t group) {
- int fd = TEMP_FAILURE_RETRY(
- open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
- mode));
+ mode_t mode, uid_t owner, gid_t group,
+ bool follow_symlinks) {
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
+ (follow_symlinks ? 0 : O_NOFOLLOW);
+ int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
if (fd == -1) {
- ALOGE("android::WriteStringToFile open failed: %s", strerror(errno));
+ PLOG(ERROR) << "android::WriteStringToFile open failed";
return false;
}
// We do an explicit fchmod here because we assume that the caller really
// meant what they said and doesn't want the umask-influenced mode.
if (fchmod(fd, mode) == -1) {
- ALOGE("android::WriteStringToFile fchmod failed: %s", strerror(errno));
+ PLOG(ERROR) << "android::WriteStringToFile fchmod failed";
return CleanUpAfterFailedWrite(path);
}
if (fchown(fd, owner, group) == -1) {
- ALOGE("android::WriteStringToFile fchown failed: %s", strerror(errno));
+ PLOG(ERROR) << "android::WriteStringToFile fchown failed";
return CleanUpAfterFailedWrite(path);
}
if (!WriteStringToFd(content, fd)) {
- ALOGE("android::WriteStringToFile write failed: %s", strerror(errno));
+ PLOG(ERROR) << "android::WriteStringToFile write failed";
return CleanUpAfterFailedWrite(path);
}
close(fd);
@@ -107,10 +121,11 @@
}
#endif
-bool WriteStringToFile(const std::string& content, const std::string& path) {
- int fd = TEMP_FAILURE_RETRY(
- open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
- DEFFILEMODE));
+bool WriteStringToFile(const std::string& content, const std::string& path,
+ bool follow_symlinks) {
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
+ (follow_symlinks ? 0 : O_NOFOLLOW);
+ int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
if (fd == -1) {
return false;
}
@@ -144,5 +159,82 @@
return true;
}
+bool RemoveFileIfExists(const std::string& path, std::string* err) {
+ struct stat st;
+#if defined(_WIN32)
+ //TODO: Windows version can't handle symbol link correctly.
+ int result = stat(path.c_str(), &st);
+ bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
+#else
+ int result = lstat(path.c_str(), &st);
+ bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
+#endif
+ if (result == 0) {
+ if (!file_type_removable) {
+ if (err != nullptr) {
+ *err = "is not a regular or symbol link file";
+ }
+ return false;
+ }
+ if (unlink(path.c_str()) == -1) {
+ if (err != nullptr) {
+ *err = strerror(errno);
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+#if !defined(_WIN32)
+bool Readlink(const std::string& path, std::string* result) {
+ result->clear();
+
+ // Most Linux file systems (ext2 and ext4, say) limit symbolic links to
+ // 4095 bytes. Since we'll copy out into the string anyway, it doesn't
+ // waste memory to just start there. We add 1 so that we can recognize
+ // whether it actually fit (rather than being truncated to 4095).
+ std::vector<char> buf(4095 + 1);
+ while (true) {
+ ssize_t size = readlink(path.c_str(), &buf[0], buf.size());
+ // Unrecoverable error?
+ if (size == -1) return false;
+ // It fit! (If size == buf.size(), it may have been truncated.)
+ if (static_cast<size_t>(size) < buf.size()) {
+ result->assign(&buf[0], size);
+ return true;
+ }
+ // Double our buffer and try again.
+ buf.resize(buf.size() * 2);
+ }
+}
+#endif
+
+std::string GetExecutablePath() {
+#if defined(__linux__)
+ std::string path;
+ android::base::Readlink("/proc/self/exe", &path);
+ return path;
+#elif defined(__APPLE__)
+ char path[PATH_MAX + 1];
+ uint32_t path_len = sizeof(path);
+ int rc = _NSGetExecutablePath(path, &path_len);
+ if (rc < 0) {
+ std::unique_ptr<char> path_buf(new char[path_len]);
+ _NSGetExecutablePath(path_buf.get(), &path_len);
+ return path_buf.get();
+ }
+ return path;
+#elif defined(_WIN32)
+ char path[PATH_MAX + 1];
+ DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
+ if (result == 0 || result == sizeof(path) - 1) return "";
+ path[PATH_MAX - 1] = 0;
+ return path;
+#else
+#error unknown OS
+#endif
+}
+
} // namespace base
} // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 77b9268..ed39ce9 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/file.h"
+#include "android-base/file.h"
#include <gtest/gtest.h>
@@ -24,7 +24,7 @@
#include <string>
-#include "base/test_utils.h"
+#include "android-base/test_utils.h"
TEST(file, ReadFileToString_ENOENT) {
std::string s("hello");
@@ -45,6 +45,24 @@
EXPECT_EQ("abc", s);
}
+// symlinks require elevated privileges on Windows.
+#if !defined(_WIN32)
+TEST(file, ReadFileToString_WriteStringToFile_symlink) {
+ TemporaryFile target, link;
+ ASSERT_EQ(0, unlink(link.path));
+ ASSERT_EQ(0, symlink(target.path, link.path));
+ ASSERT_FALSE(android::base::WriteStringToFile("foo", link.path, false));
+ ASSERT_EQ(ELOOP, errno);
+ ASSERT_TRUE(android::base::WriteStringToFile("foo", link.path, true));
+
+ std::string s;
+ ASSERT_FALSE(android::base::ReadFileToString(link.path, &s));
+ ASSERT_EQ(ELOOP, errno);
+ ASSERT_TRUE(android::base::ReadFileToString(link.path, &s, true));
+ ASSERT_EQ("foo", s);
+}
+#endif
+
// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
// sense on Windows.
#if !defined(_WIN32)
@@ -96,3 +114,51 @@
s.resize(1024);
ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
}
+
+TEST(file, RemoveFileIfExist) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.fd = -1;
+ std::string err;
+ ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err)) << err;
+ ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path));
+ TemporaryDir td;
+ ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
+ ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
+ ASSERT_EQ("is not a regular or symbol link file", err);
+}
+
+TEST(file, Readlink) {
+#if !defined(_WIN32)
+ // Linux doesn't allow empty symbolic links.
+ std::string min("x");
+ // ext2 and ext4 both have PAGE_SIZE limits.
+ // If file encryption is enabled, there's extra overhead to store the
+ // size of the encrypted symlink target. There's also an off-by-one
+ // in current kernels (and marlin/sailfish where we're seeing this
+ // failure are still on 3.18, far from current). http://b/33306057.
+ std::string max(static_cast<size_t>(4096 - 2 - 1 - 1), 'x');
+
+ TemporaryDir td;
+ std::string min_path{std::string(td.path) + "/" + "min"};
+ std::string max_path{std::string(td.path) + "/" + "max"};
+
+ ASSERT_EQ(0, symlink(min.c_str(), min_path.c_str()));
+ ASSERT_EQ(0, symlink(max.c_str(), max_path.c_str()));
+
+ std::string result;
+
+ result = "wrong";
+ ASSERT_TRUE(android::base::Readlink(min_path, &result));
+ ASSERT_EQ(min, result);
+
+ result = "wrong";
+ ASSERT_TRUE(android::base::Readlink(max_path, &result));
+ ASSERT_EQ(max, result);
+#endif
+}
+
+TEST(file, GetExecutablePath) {
+ ASSERT_NE("", android::base::GetExecutablePath());
+}
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
new file mode 100644
index 0000000..04c299c
--- /dev/null
+++ b/base/include/android-base/errors.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Portable error handling functions. This is only necessary for host-side
+// code that needs to be cross-platform; code that is only run on Unix should
+// just use errno and strerror() for simplicity.
+//
+// There is some complexity since Windows has (at least) three different error
+// numbers, not all of which share the same type:
+// * errno: for C runtime errors.
+// * GetLastError(): Windows non-socket errors.
+// * WSAGetLastError(): Windows socket errors.
+// errno can be passed to strerror() on all platforms, but the other two require
+// special handling to get the error string. Refer to Microsoft documentation
+// to determine which error code to check for each function.
+
+#ifndef ANDROID_BASE_ERRORS_H
+#define ANDROID_BASE_ERRORS_H
+
+#include <string>
+
+namespace android {
+namespace base {
+
+// Returns a string describing the given system error code. |error_code| must
+// be errno on Unix or GetLastError()/WSAGetLastError() on Windows. Passing
+// errno on Windows has undefined behavior.
+std::string SystemErrorCodeToString(int error_code);
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_ERRORS_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
new file mode 100644
index 0000000..cd64262
--- /dev/null
+++ b/base/include/android-base/file.h
@@ -0,0 +1,58 @@
+/*
+ * 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 ANDROID_BASE_FILE_H
+#define ANDROID_BASE_FILE_H
+
+#include <sys/stat.h>
+#include <string>
+
+#if !defined(_WIN32) && !defined(O_BINARY)
+#define O_BINARY 0
+#endif
+
+namespace android {
+namespace base {
+
+bool ReadFdToString(int fd, std::string* content);
+bool ReadFileToString(const std::string& path, std::string* content,
+ bool follow_symlinks = false);
+
+bool WriteStringToFile(const std::string& content, const std::string& path,
+ bool follow_symlinks = false);
+bool WriteStringToFd(const std::string& content, int fd);
+
+#if !defined(_WIN32)
+bool WriteStringToFile(const std::string& content, const std::string& path,
+ mode_t mode, uid_t owner, gid_t group,
+ bool follow_symlinks = false);
+#endif
+
+bool ReadFully(int fd, void* data, size_t byte_count);
+bool WriteFully(int fd, const void* data, size_t byte_count);
+
+bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
+
+#if !defined(_WIN32)
+bool Readlink(const std::string& path, std::string* result);
+#endif
+
+std::string GetExecutablePath();
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_FILE_H
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
new file mode 100644
index 0000000..50677a3
--- /dev/null
+++ b/base/include/android-base/logging.h
@@ -0,0 +1,395 @@
+/*
+ * 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 ANDROID_BASE_LOGGING_H
+#define ANDROID_BASE_LOGGING_H
+
+// NOTE: For Windows, you must include logging.h after windows.h to allow the
+// following code to suppress the evil ERROR macro:
+#ifdef _WIN32
+// windows.h includes wingdi.h which defines an evil macro ERROR.
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+#include <functional>
+#include <memory>
+#include <ostream>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace base {
+
+enum LogSeverity {
+ VERBOSE,
+ DEBUG,
+ INFO,
+ WARNING,
+ ERROR,
+ FATAL_WITHOUT_ABORT,
+ FATAL,
+};
+
+enum LogId {
+ DEFAULT,
+ MAIN,
+ SYSTEM,
+};
+
+using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*,
+ unsigned int, const char*)>;
+using AbortFunction = std::function<void(const char*)>;
+
+void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+
+void DefaultAborter(const char* abort_message);
+
+#ifdef __ANDROID__
+// We expose this even though it is the default because a user that wants to
+// override the default log buffer will have to construct this themselves.
+class LogdLogger {
+ public:
+ explicit LogdLogger(LogId default_log_id = android::base::MAIN);
+
+ void operator()(LogId, LogSeverity, const char* tag, const char* file,
+ unsigned int line, const char* message);
+
+ private:
+ LogId default_log_id_;
+};
+#endif
+
+// Configure logging based on ANDROID_LOG_TAGS environment variable.
+// We need to parse a string that looks like
+//
+// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+//
+// The tag (or '*' for the global level) comes first, followed by a colon and a
+// letter indicating the minimum priority level we're expected to log. This can
+// be used to reveal or conceal logs with specific tags.
+#ifdef __ANDROID__
+#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
+#else
+#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger
+#endif
+void InitLogging(char* argv[],
+ LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,
+ AbortFunction&& aborter = DefaultAborter);
+#undef INIT_LOGGING_DEFAULT_LOGGER
+
+// Replace the current logger.
+void SetLogger(LogFunction&& logger);
+
+// Replace the current aborter.
+void SetAborter(AbortFunction&& aborter);
+
+class ErrnoRestorer {
+ public:
+ ErrnoRestorer()
+ : saved_errno_(errno) {
+ }
+
+ ~ErrnoRestorer() {
+ errno = saved_errno_;
+ }
+
+ // Allow this object to be used as part of && operation.
+ operator bool() const {
+ return true;
+ }
+
+ private:
+ const int saved_errno_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
+};
+
+// A helper macro that produces an expression that accepts both a qualified name and an
+// unqualified name for a LogSeverity, and returns a LogSeverity value.
+// Note: DO NOT USE DIRECTLY. This is an implementation detail.
+#define SEVERITY_LAMBDA(severity) ([&]() { \
+ using ::android::base::VERBOSE; \
+ using ::android::base::DEBUG; \
+ using ::android::base::INFO; \
+ using ::android::base::WARNING; \
+ using ::android::base::ERROR; \
+ using ::android::base::FATAL_WITHOUT_ABORT; \
+ using ::android::base::FATAL; \
+ return (severity); }())
+
+// Defines whether the given severity will be logged or silently swallowed.
+#define WOULD_LOG(severity) \
+ UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity())
+
+// Get an ostream that can be used for logging at the given severity and to the default
+// destination.
+//
+// Notes:
+// 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter
+// usage manually.
+// 2) This does not save and restore errno.
+#define LOG_STREAM(severity) LOG_STREAM_TO(DEFAULT, severity)
+
+// Get an ostream that can be used for logging at the given severity and to the
+// given destination. The same notes as for LOG_STREAM apply.
+#define LOG_STREAM_TO(dest, severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, \
+ ::android::base::dest, \
+ SEVERITY_LAMBDA(severity), -1).stream()
+
+// Logs a message to logcat on Android otherwise to stderr. If the severity is
+// FATAL it also causes an abort. For example:
+//
+// LOG(FATAL) << "We didn't expect to reach here";
+#define LOG(severity) LOG_TO(DEFAULT, severity)
+
+// Logs a message to logcat with the specified log ID on Android otherwise to
+// stderr. If the severity is FATAL it also causes an abort.
+// Use an if-else statement instead of just an if statement here. So if there is a
+// else statement after LOG() macro, it won't bind to the if statement in the macro.
+// do-while(0) statement doesn't work here. Because we need to support << operator
+// following the macro, like "LOG(DEBUG) << xxx;".
+
+#define LOG_TO(dest, severity) \
+ WOULD_LOG(severity) && \
+ ::android::base::ErrnoRestorer() && \
+ LOG_STREAM_TO(dest, severity)
+
+// A variant of LOG that also logs the current errno value. To be used when
+// library calls fail.
+#define PLOG(severity) PLOG_TO(DEFAULT, severity)
+
+// Behaves like PLOG, but logs to the specified log ID.
+#define PLOG_TO(dest, severity) \
+ WOULD_LOG(SEVERITY_LAMBDA(severity)) && \
+ ::android::base::ErrnoRestorer() && \
+ ::android::base::LogMessage(__FILE__, __LINE__, \
+ ::android::base::dest, \
+ SEVERITY_LAMBDA(severity), errno).stream()
+
+// Marker that code is yet to be implemented.
+#define UNIMPLEMENTED(level) \
+ LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+
+#ifdef __clang_analyzer__
+// ClangL static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;;abort())
+#else
+#define ABORT_AFTER_LOG_FATAL
+#endif
+
+// Check whether condition x holds and LOG(FATAL) if not. The value of the
+// expression x is only evaluated once. Extra logging can be appended using <<
+// after. For example:
+//
+// CHECK(false == true) results in a log message of
+// "Check failed: false == true".
+#define CHECK(x) \
+ LIKELY((x)) || \
+ ABORT_AFTER_LOG_FATAL \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::FATAL, -1).stream() \
+ << "Check failed: " #x << " "
+
+// Helper for CHECK_xx(x,y) macros.
+#define CHECK_OP(LHS, RHS, OP) \
+ for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
+ UNLIKELY(!(_values.lhs OP _values.rhs)); \
+ /* empty */) \
+ ABORT_AFTER_LOG_FATAL \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::FATAL, -1).stream() \
+ << "Check failed: " << #LHS << " " << #OP << " " << #RHS \
+ << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
+
+// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
+// of the expressions x and y is evaluated once. Extra logging can be appended
+// using << after. For example:
+//
+// CHECK_NE(0 == 1, false) results in
+// "Check failed: false != false (0==1=false, false=false) ".
+#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
+#define CHECK_NE(x, y) CHECK_OP(x, y, != )
+#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
+#define CHECK_LT(x, y) CHECK_OP(x, y, < )
+#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
+#define CHECK_GT(x, y) CHECK_OP(x, y, > )
+
+// Helper for CHECK_STRxx(s1,s2) macros.
+#define CHECK_STROP(s1, s2, sense) \
+ while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
+ ABORT_AFTER_LOG_FATAL \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::FATAL, -1).stream() \
+ << "Check failed: " << "\"" << (s1) << "\"" \
+ << ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
+
+// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
+#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
+#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
+
+// Perform the pthread function call(args), LOG(FATAL) on error.
+#define CHECK_PTHREAD_CALL(call, args, what) \
+ do { \
+ int rc = call args; \
+ if (rc != 0) { \
+ errno = rc; \
+ ABORT_AFTER_LOG_FATAL \
+ PLOG(FATAL) << #call << " failed for " << (what); \
+ } \
+ } while (false)
+
+// CHECK that can be used in a constexpr function. For example:
+//
+// constexpr int half(int n) {
+// return
+// DCHECK_CONSTEXPR(n >= 0, , 0)
+// CHECK_CONSTEXPR((n & 1) == 0),
+// << "Extra debugging output: n = " << n, 0)
+// n / 2;
+// }
+#define CHECK_CONSTEXPR(x, out, dummy) \
+ (UNLIKELY(!(x))) \
+ ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
+ :
+
+// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
+// CHECK should be used unless profiling identifies a CHECK as being in
+// performance critical code.
+#if defined(NDEBUG)
+static constexpr bool kEnableDChecks = false;
+#else
+static constexpr bool kEnableDChecks = true;
+#endif
+
+#define DCHECK(x) \
+ if (::android::base::kEnableDChecks) CHECK(x)
+#define DCHECK_EQ(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
+#define DCHECK_NE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_NE(x, y)
+#define DCHECK_LE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LE(x, y)
+#define DCHECK_LT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LT(x, y)
+#define DCHECK_GE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GE(x, y)
+#define DCHECK_GT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GT(x, y)
+#define DCHECK_STREQ(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
+#define DCHECK_STRNE(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
+#if defined(NDEBUG)
+#define DCHECK_CONSTEXPR(x, out, dummy)
+#else
+#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
+#endif
+
+// Temporary class created to evaluate the LHS and RHS, used with
+// MakeEagerEvaluator to infer the types of LHS and RHS.
+template <typename LHS, typename RHS>
+struct EagerEvaluator {
+ constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
+ }
+ LHS lhs;
+ RHS rhs;
+};
+
+// Helper function for CHECK_xx.
+template <typename LHS, typename RHS>
+constexpr EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
+ return EagerEvaluator<LHS, RHS>(lhs, rhs);
+}
+
+// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
+// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
+// signed/unsigned warnings to protect you against combinations not explicitly
+// listed below.
+#define EAGER_PTR_EVALUATOR(T1, T2) \
+ template <> \
+ struct EagerEvaluator<T1, T2> { \
+ EagerEvaluator(T1 l, T2 r) \
+ : lhs(reinterpret_cast<const void*>(l)), \
+ rhs(reinterpret_cast<const void*>(r)) { \
+ } \
+ const void* lhs; \
+ const void* rhs; \
+ }
+EAGER_PTR_EVALUATOR(const char*, const char*);
+EAGER_PTR_EVALUATOR(const char*, char*);
+EAGER_PTR_EVALUATOR(char*, const char*);
+EAGER_PTR_EVALUATOR(char*, char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(const signed char*, signed char*);
+EAGER_PTR_EVALUATOR(signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(signed char*, signed char*);
+
+// Data for the log message, not stored in LogMessage to avoid increasing the
+// stack size.
+class LogMessageData;
+
+// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
+// of a CHECK. The destructor will abort if the severity is FATAL.
+class LogMessage {
+ public:
+ LogMessage(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, int error);
+
+ ~LogMessage();
+
+ // Returns the stream associated with the message, the LogMessage performs
+ // output when it goes out of scope.
+ std::ostream& stream();
+
+ // The routine that performs the actual logging.
+ static void LogLine(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, const char* msg);
+
+ private:
+ const std::unique_ptr<LogMessageData> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Get the minimum severity level for logging.
+LogSeverity GetMinimumLogSeverity();
+
+// Set the minimum severity level for logging, returning the old severity.
+LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
+
+// Allows to temporarily change the minimum severity level for logging.
+class ScopedLogSeverity {
+ public:
+ explicit ScopedLogSeverity(LogSeverity level);
+ ~ScopedLogSeverity();
+
+ private:
+ LogSeverity old_;
+};
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
new file mode 100644
index 0000000..88bbe8a
--- /dev/null
+++ b/base/include/android-base/macros.h
@@ -0,0 +1,182 @@
+/*
+ * 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 ANDROID_BASE_MACROS_H
+#define ANDROID_BASE_MACROS_H
+
+#include <stddef.h> // for size_t
+#include <unistd.h> // for TEMP_FAILURE_RETRY
+
+// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(exp) \
+ ({ \
+ decltype(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; \
+ })
+#endif
+
+// A macro to disallow the copy constructor and operator= functions
+// This must be placed in the private: declarations for a class.
+//
+// For disallowing only assign or copy, delete the relevant operator or
+// constructor, for example:
+// void operator=(const TypeName&) = delete;
+// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
+// semantically, one should either use disallow both or neither. Try to
+// avoid these in new code.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName() = delete; \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example. If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function. In these rare
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
+// due to a limitation in C++'s template system. The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
+// but can be used on anonymous types or types defined inside
+// functions. It's less safe than arraysize as it accepts some
+// (although not all) pointers. Therefore, you should use arraysize
+// whenever possible.
+//
+// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
+// size_t.
+//
+// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
+//
+// "warning: division by zero in ..."
+//
+// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
+// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
+//
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+//
+// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element). If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array. Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+//
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+//
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size. Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+#define ARRAYSIZE_UNSAFE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+// Changing this definition will cause you a lot of pain. A majority of
+// vendor code defines LIKELY and UNLIKELY this way, and includes
+// this header through an indirect path.
+#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
+#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
+
+#define WARN_UNUSED __attribute__((warn_unused_result))
+
+// A deprecated function to call to create a false use of the parameter, for
+// example:
+// int foo(int x) { UNUSED(x); return 10; }
+// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
+template <typename... T>
+void UNUSED(const T&...) {
+}
+
+// An attribute to place on a parameter to a function, for example:
+// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
+// to avoid compiler warnings.
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+
+// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
+// between switch labels:
+// switch (x) {
+// case 40:
+// case 41:
+// if (truth_is_out_there) {
+// ++x;
+// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
+// // comments.
+// } else {
+// return x;
+// }
+// case 42:
+// ...
+//
+// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
+// followed by a semicolon. It is designed to mimic control-flow statements
+// like 'break;', so it can be placed in most places where 'break;' can, but
+// only if there are no statements on the execution path between it and the
+// next switch label.
+//
+// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
+// [[clang::fallthrough]] attribute, which is analysed when performing switch
+// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
+// documentation on language extensions for details:
+// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
+//
+// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
+// effect on diagnostics.
+//
+// In either case this macro has no effect on runtime behavior and performance
+// of code.
+#if defined(__clang__) && defined(__has_warning)
+#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
+#endif
+#endif
+
+#ifndef FALLTHROUGH_INTENDED
+#define FALLTHROUGH_INTENDED \
+ do { \
+ } while (0)
+#endif
+
+#endif // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
new file mode 100644
index 0000000..9971226
--- /dev/null
+++ b/base/include/android-base/memory.h
@@ -0,0 +1,41 @@
+/*
+ * 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 ANDROID_BASE_MEMORY_H
+#define ANDROID_BASE_MEMORY_H
+
+namespace android {
+namespace base {
+
+// Use memcpy for access to unaligned data on targets with alignment
+// restrictions. The compiler will generate appropriate code to access these
+// structures without generating alignment exceptions.
+template <typename T>
+static inline T get_unaligned(const void* address) {
+ T result;
+ memcpy(&result, address, sizeof(T));
+ return result;
+}
+
+template <typename T>
+static inline void put_unaligned(void* address, T v) {
+ memcpy(address, &v, sizeof(T));
+}
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_MEMORY_H
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
new file mode 100644
index 0000000..daa6902
--- /dev/null
+++ b/base/include/android-base/parsedouble.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_PARSEDOUBLE_H
+#define ANDROID_BASE_PARSEDOUBLE_H
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <limits>
+
+namespace android {
+namespace base {
+
+// Parse double value in the string 's' and sets 'out' to that value.
+// Optionally allows the caller to define a 'min' and 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success.
+static inline bool ParseDouble(const char* s, double* out,
+ double min = std::numeric_limits<double>::lowest(),
+ double max = std::numeric_limits<double>::max()) {
+ errno = 0;
+ char* end;
+ double result = strtod(s, &end);
+ if (errno != 0 || s == end || *end != '\0') {
+ return false;
+ }
+ if (result < min || max < result) {
+ return false;
+ }
+ *out = result;
+ return true;
+}
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_PARSEDOUBLE_H
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
new file mode 100644
index 0000000..2c8570e
--- /dev/null
+++ b/base/include/android-base/parseint.h
@@ -0,0 +1,90 @@
+/*
+ * 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 ANDROID_BASE_PARSEINT_H
+#define ANDROID_BASE_PARSEINT_H
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <limits>
+#include <string>
+
+namespace android {
+namespace base {
+
+// Parses the unsigned decimal integer in the string 's' and sets 'out' to
+// that value. Optionally allows the caller to define a 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success; 'out'
+// is untouched if parsing fails.
+template <typename T>
+bool ParseUint(const char* s, T* out,
+ T max = std::numeric_limits<T>::max()) {
+ int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
+ errno = 0;
+ char* end;
+ unsigned long long int result = strtoull(s, &end, base);
+ if (errno != 0 || s == end || *end != '\0') {
+ return false;
+ }
+ if (max < result) {
+ return false;
+ }
+ *out = static_cast<T>(result);
+ return true;
+}
+
+// TODO: string_view
+template <typename T>
+bool ParseUint(const std::string& s, T* out,
+ T max = std::numeric_limits<T>::max()) {
+ return ParseUint(s.c_str(), out, max);
+}
+
+// Parses the signed decimal integer in the string 's' and sets 'out' to
+// that value. Optionally allows the caller to define a 'min' and 'max
+// beyond which otherwise valid values will be rejected. Returns boolean
+// success; 'out' is untouched if parsing fails.
+template <typename T>
+bool ParseInt(const char* s, T* out,
+ T min = std::numeric_limits<T>::min(),
+ T max = std::numeric_limits<T>::max()) {
+ int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
+ errno = 0;
+ char* end;
+ long long int result = strtoll(s, &end, base);
+ if (errno != 0 || s == end || *end != '\0') {
+ return false;
+ }
+ if (result < min || max < result) {
+ return false;
+ }
+ *out = static_cast<T>(result);
+ return true;
+}
+
+// TODO: string_view
+template <typename T>
+bool ParseInt(const std::string& s, T* out,
+ T min = std::numeric_limits<T>::min(),
+ T max = std::numeric_limits<T>::max()) {
+ return ParseInt(s.c_str(), out, min, max);
+}
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_PARSEINT_H
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
new file mode 100644
index 0000000..b4ac025
--- /dev/null
+++ b/base/include/android-base/parsenetaddress.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_PARSENETADDRESS_H
+#define ANDROID_BASE_PARSENETADDRESS_H
+
+#include <string>
+
+namespace android {
+namespace base {
+
+// Parses |address| into |host| and |port|.
+//
+// If |address| doesn't contain a port number, the default value is taken from
+// |port|. If |canonical_address| is non-null it will be set to "host:port" or
+// "[host]:port" as appropriate.
+//
+// On failure, returns false and fills |error|.
+bool ParseNetAddress(const std::string& address, std::string* host, int* port,
+ std::string* canonical_address, std::string* error);
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_PARSENETADDRESS_H
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
new file mode 100644
index 0000000..4d7082a
--- /dev/null
+++ b/base/include/android-base/properties.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_PROPERTIES_H
+#define ANDROID_BASE_PROPERTIES_H
+
+#include <sys/cdefs.h>
+
+#if !defined(__BIONIC__)
+#error Only bionic supports system properties.
+#endif
+
+#include <limits>
+#include <string>
+
+namespace android {
+namespace base {
+
+// Returns the current value of the system property `key`,
+// or `default_value` if the property is empty or doesn't exist.
+std::string GetProperty(const std::string& key, const std::string& default_value);
+
+// Returns true if the system property `key` has the value "1", "y", "yes", "on", or "true",
+// false for "0", "n", "no", "off", or "false", or `default_value` otherwise.
+bool GetBoolProperty(const std::string& key, bool default_value);
+
+// Returns the signed integer corresponding to the system property `key`.
+// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
+// the optional bounds, returns `default_value`.
+template <typename T> T GetIntProperty(const std::string& key,
+ T default_value,
+ T min = std::numeric_limits<T>::min(),
+ T max = std::numeric_limits<T>::max());
+
+// Returns the unsigned integer corresponding to the system property `key`.
+// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
+// the optional bound, returns `default_value`.
+template <typename T> T GetUintProperty(const std::string& key,
+ T default_value,
+ T max = std::numeric_limits<T>::max());
+
+// Sets the system property `key` to `value`.
+// Note that system property setting is inherently asynchronous so a return value of `true`
+// isn't particularly meaningful, and immediately reading back the value won't necessarily
+// tell you whether or not your call succeeded. A `false` return value definitely means failure.
+bool SetProperty(const std::string& key, const std::string& value);
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_PROPERTIES_H
diff --git a/base/include/android-base/quick_exit.h b/base/include/android-base/quick_exit.h
new file mode 100644
index 0000000..a03b14f
--- /dev/null
+++ b/base/include/android-base/quick_exit.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+// Provide emulation for at_quick_exit/quick_exit on platforms that don't have it.
+namespace android {
+namespace base {
+
+// Bionic and glibc have quick_exit, Darwin and Windows don't.
+#if !defined(__linux__)
+ void quick_exit(int exit_code) __attribute__((noreturn));
+ int at_quick_exit(void (*func)());
+#else
+ using ::at_quick_exit;
+ using ::quick_exit;
+#endif
+}
+}
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
new file mode 100644
index 0000000..cf666ab
--- /dev/null
+++ b/base/include/android-base/stringprintf.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_STRINGPRINTF_H
+#define ANDROID_BASE_STRINGPRINTF_H
+
+#include <stdarg.h>
+#include <string>
+
+namespace android {
+namespace base {
+
+// These printf-like functions are implemented in terms of vsnprintf, so they
+// use the same attribute for compile-time format string checking. On Windows,
+// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
+// in %zd and PRIu64 (and related) to be recognized by the compile-time
+// checking.
+#define FORMAT_ARCHETYPE __printf__
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+#undef FORMAT_ARCHETYPE
+#define FORMAT_ARCHETYPE gnu_printf
+#endif
+#endif
+
+// Returns a string corresponding to printf-like formatting of the arguments.
+std::string StringPrintf(const char* fmt, ...)
+ __attribute__((__format__(FORMAT_ARCHETYPE, 1, 2)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendF(std::string* dst, const char* fmt, ...)
+ __attribute__((__format__(FORMAT_ARCHETYPE, 2, 3)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendV(std::string* dst, const char* format, va_list ap)
+ __attribute__((__format__(FORMAT_ARCHETYPE, 2, 0)));
+
+#undef FORMAT_ARCHETYPE
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_STRINGPRINTF_H
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
new file mode 100644
index 0000000..f5f5c11
--- /dev/null
+++ b/base/include/android-base/strings.h
@@ -0,0 +1,73 @@
+/*
+ * 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 ANDROID_BASE_STRINGS_H
+#define ANDROID_BASE_STRINGS_H
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace base {
+
+// Splits a string into a vector of strings.
+//
+// The string is split at each occurrence of a character in delimiters.
+//
+// The empty string is not a valid delimiter list.
+std::vector<std::string> Split(const std::string& s,
+ const std::string& delimiters);
+
+// Trims whitespace off both ends of the given string.
+std::string Trim(const std::string& s);
+
+// Joins a container of things into a single string, using the given separator.
+template <typename ContainerT, typename SeparatorT>
+std::string Join(const ContainerT& things, SeparatorT separator) {
+ if (things.empty()) {
+ return "";
+ }
+
+ std::ostringstream result;
+ result << *things.begin();
+ for (auto it = std::next(things.begin()); it != things.end(); ++it) {
+ result << separator << *it;
+ }
+ return result.str();
+}
+
+// We instantiate the common cases in strings.cpp.
+extern template std::string Join(const std::vector<std::string>&, char);
+extern template std::string Join(const std::vector<const char*>&, char);
+extern template std::string Join(const std::vector<std::string>&, const std::string&);
+extern template std::string Join(const std::vector<const char*>&, const std::string&);
+
+// Tests whether 's' starts with 'prefix'.
+bool StartsWith(const std::string& s, const char* prefix);
+bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
+
+// Tests whether 's' ends with 'suffix'.
+bool EndsWith(const std::string& s, const char* suffix);
+bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
+
+// Tests whether 'lhs' equals 'rhs', ignoring case.
+bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_STRINGS_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
new file mode 100644
index 0000000..c0bf0c1
--- /dev/null
+++ b/base/include/android-base/test_utils.h
@@ -0,0 +1,68 @@
+/*
+ * 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 ANDROID_BASE_TEST_UTILS_H
+#define ANDROID_BASE_TEST_UTILS_H
+
+#include <string>
+
+#include <android-base/macros.h>
+
+class TemporaryFile {
+ public:
+ TemporaryFile();
+ ~TemporaryFile();
+
+ int fd;
+ char path[1024];
+
+ private:
+ void init(const std::string& tmp_dir);
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
+};
+
+class TemporaryDir {
+ public:
+ TemporaryDir();
+ ~TemporaryDir();
+
+ char path[1024];
+
+ private:
+ bool init(const std::string& tmp_dir);
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
+};
+
+class CapturedStderr {
+ public:
+ CapturedStderr();
+ ~CapturedStderr();
+
+ int fd() const;
+
+ private:
+ void init();
+ void reset();
+
+ TemporaryFile temp_file_;
+ int old_stderr_;
+
+ DISALLOW_COPY_AND_ASSIGN(CapturedStderr);
+};
+
+#endif // ANDROID_BASE_TEST_UTILS_H
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
new file mode 100644
index 0000000..fbb5923
--- /dev/null
+++ b/base/include/android-base/thread_annotations.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_THREAD_ANNOTATIONS_H
+#define ANDROID_BASE_THREAD_ANNOTATIONS_H
+
+#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
+#endif
+
+#define CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY \
+ THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define SHARED_CAPABILITY(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_capability(__VA_ARGS__))
+
+#define GUARDED_BY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define EXCLUSIVE_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
+
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
+
+#define SHARED_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
+
+#define SHARED_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
+
+#define UNLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
+
+#define SCOPED_LOCKABLE \
+ THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define LOCK_RETURNED(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS \
+ THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+#endif // ANDROID_BASE_THREAD_ANNOTATIONS_H
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
new file mode 100644
index 0000000..6cfcfcd
--- /dev/null
+++ b/base/include/android-base/unique_fd.h
@@ -0,0 +1,104 @@
+/*
+ * 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 ANDROID_BASE_UNIQUE_FD_H
+#define ANDROID_BASE_UNIQUE_FD_H
+
+#include <unistd.h>
+
+// DO NOT INCLUDE OTHER LIBBASE HEADERS!
+// This file gets used in libbinder, and libbinder is used everywhere.
+// Including other headers from libbase frequently results in inclusion of
+// android-base/macros.h, which causes macro collisions.
+
+// Container for a file descriptor that automatically closes the descriptor as
+// it goes out of scope.
+//
+// unique_fd ufd(open("/some/path", "r"));
+// if (ufd.get() == -1) return error;
+//
+// // Do something useful, possibly including 'return'.
+//
+// return 0; // Descriptor is closed for you.
+//
+// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
+// you find this class if you're searching for one of those names.
+namespace android {
+namespace base {
+
+struct DefaultCloser {
+ static void Close(int fd) {
+ // Even if close(2) fails with EINTR, the fd will have been closed.
+ // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
+ // else's fd.
+ // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+ ::close(fd);
+ }
+};
+
+template <typename Closer>
+class unique_fd_impl final {
+ public:
+ unique_fd_impl() : value_(-1) {}
+
+ explicit unique_fd_impl(int value) : value_(value) {}
+ ~unique_fd_impl() { reset(); }
+
+ unique_fd_impl(unique_fd_impl&& other) : value_(other.release()) {}
+ unique_fd_impl& operator=(unique_fd_impl&& s) {
+ reset(s.release());
+ return *this;
+ }
+
+ void reset(int new_value = -1) {
+ if (value_ != -1) {
+ Closer::Close(value_);
+ }
+ value_ = new_value;
+ }
+
+ int get() const { return value_; }
+ operator int() const { return get(); }
+
+ int release() __attribute__((warn_unused_result)) {
+ int ret = value_;
+ value_ = -1;
+ return ret;
+ }
+
+ private:
+ int value_;
+
+ unique_fd_impl(const unique_fd_impl&);
+ void operator=(const unique_fd_impl&);
+};
+
+using unique_fd = unique_fd_impl<DefaultCloser>;
+
+} // namespace base
+} // namespace android
+
+template <typename T>
+int close(const android::base::unique_fd_impl<T>&)
+#if defined(__clang__)
+ __attribute__((__unavailable__(
+#else
+ __attribute__((__error__(
+#endif
+ "close called on unique_fd"
+ )));
+
+#endif // ANDROID_BASE_UNIQUE_FD_H
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
new file mode 100755
index 0000000..2d5a6f6
--- /dev/null
+++ b/base/include/android-base/utf8.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_UTF8_H
+#define ANDROID_BASE_UTF8_H
+
+#ifdef _WIN32
+#include <string>
+#else
+// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
+#include <fcntl.h> // open
+#include <unistd.h> // unlink
+#endif
+
+namespace android {
+namespace base {
+
+// Only available on Windows because this is only needed on Windows.
+#ifdef _WIN32
+// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
+// conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
+
+// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
+// whether the conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
+
+// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
+// UTF-8. Returns whether the conversion was done successfully.
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
+
+// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
+// was done successfully.
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
+
+// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
+// whether the conversion was done successfully.
+bool UTF8ToWide(const char* utf8, std::wstring* utf16);
+
+// Convert a UTF-8 std::string (including any embedded NULL characters) to
+// UTF-16. Returns whether the conversion was done successfully.
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
+#endif
+
+// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
+// are wrappers, for non-Windows these just expose existing APIs. To call these
+// functions, use:
+//
+// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
+// namespace {
+// // Import functions into anonymous namespace.
+// using namespace android::base::utf8;
+//
+// void SomeFunction(const char* name) {
+// int fd = open(name, ...); // Calls android::base::utf8::open().
+// ...
+// unlink(name); // Calls android::base::utf8::unlink().
+// }
+// }
+namespace utf8 {
+
+#ifdef _WIN32
+int open(const char* name, int flags, ...);
+int unlink(const char* name);
+#else
+using ::open;
+using ::unlink;
+#endif
+
+} // namespace utf8
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_UTF8_H
diff --git a/base/include/base/file.h b/base/include/base/file.h
deleted file mode 100644
index acd29b3..0000000
--- a/base/include/base/file.h
+++ /dev/null
@@ -1,43 +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 BASE_FILE_H
-#define BASE_FILE_H
-
-#include <sys/stat.h>
-#include <string>
-
-namespace android {
-namespace base {
-
-bool ReadFdToString(int fd, std::string* content);
-bool ReadFileToString(const std::string& path, std::string* content);
-
-bool WriteStringToFile(const std::string& content, const std::string& path);
-bool WriteStringToFd(const std::string& content, int fd);
-
-#if !defined(_WIN32)
-bool WriteStringToFile(const std::string& content, const std::string& path,
- mode_t mode, uid_t owner, gid_t group);
-#endif
-
-bool ReadFully(int fd, void* data, size_t byte_count);
-bool WriteFully(int fd, const void* data, size_t byte_count);
-
-} // namespace base
-} // namespace android
-
-#endif // BASE_FILE_H
diff --git a/base/include/base/logging.h b/base/include/base/logging.h
deleted file mode 100644
index 283a7bc..0000000
--- a/base/include/base/logging.h
+++ /dev/null
@@ -1,305 +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 BASE_LOGGING_H
-#define BASE_LOGGING_H
-
-// NOTE: For Windows, you must include logging.h after windows.h to allow the
-// following code to suppress the evil ERROR macro:
-#ifdef _WIN32
-// windows.h includes wingdi.h which defines an evil macro ERROR.
-#ifdef ERROR
-#undef ERROR
-#endif
-#endif
-
-#include <functional>
-#include <memory>
-#include <ostream>
-
-#include "base/macros.h"
-
-namespace android {
-namespace base {
-
-enum LogSeverity {
- VERBOSE,
- DEBUG,
- INFO,
- WARNING,
- ERROR,
- FATAL,
-};
-
-enum LogId {
- DEFAULT,
- MAIN,
- SYSTEM,
-};
-
-typedef std::function<void(LogId, LogSeverity, const char*, const char*,
- unsigned int, const char*)> LogFunction;
-
-extern void StderrLogger(LogId, LogSeverity, const char*, const char*,
- unsigned int, const char*);
-
-#ifdef __ANDROID__
-// We expose this even though it is the default because a user that wants to
-// override the default log buffer will have to construct this themselves.
-class LogdLogger {
- public:
- explicit LogdLogger(LogId default_log_id = android::base::MAIN);
-
- void operator()(LogId, LogSeverity, const char* tag, const char* file,
- unsigned int line, const char* message);
-
- private:
- LogId default_log_id_;
-};
-#endif
-
-// Configure logging based on ANDROID_LOG_TAGS environment variable.
-// We need to parse a string that looks like
-//
-// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
-//
-// The tag (or '*' for the global level) comes first, followed by a colon and a
-// letter indicating the minimum priority level we're expected to log. This can
-// be used to reveal or conceal logs with specific tags.
-extern void InitLogging(char* argv[], LogFunction&& logger);
-
-// Configures logging using the default logger (logd for the device, stderr for
-// the host).
-extern void InitLogging(char* argv[]);
-
-// Replace the current logger.
-extern void SetLogger(LogFunction&& logger);
-
-// Logs a message to logcat on Android otherwise to stderr. If the severity is
-// FATAL it also causes an abort. For example:
-//
-// LOG(FATAL) << "We didn't expect to reach here";
-#define LOG(severity) \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
- ::android::base::severity, -1).stream()
-
-// Logs a message to logcat with the specified log ID on Android otherwise to
-// stderr. If the severity is FATAL it also causes an abort.
-#define LOG_TO(dest, severity) \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
- ::android::base::severity, -1).stream()
-
-// A variant of LOG that also logs the current errno value. To be used when
-// library calls fail.
-#define PLOG(severity) \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
- ::android::base::severity, errno).stream()
-
-// Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity) \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
- ::android::base::severity, errno).stream()
-
-// Marker that code is yet to be implemented.
-#define UNIMPLEMENTED(level) \
- LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
-
-// Check whether condition x holds and LOG(FATAL) if not. The value of the
-// expression x is only evaluated once. Extra logging can be appended using <<
-// after. For example:
-//
-// CHECK(false == true) results in a log message of
-// "Check failed: false == true".
-#define CHECK(x) \
- if (UNLIKELY(!(x))) \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
- ::android::base::FATAL, -1).stream() \
- << "Check failed: " #x << " "
-
-// Helper for CHECK_xx(x,y) macros.
-#define CHECK_OP(LHS, RHS, OP) \
- for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
- UNLIKELY(!(_values.lhs OP _values.rhs)); \
- /* empty */) \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
- ::android::base::FATAL, -1).stream() \
- << "Check failed: " << #LHS << " " << #OP << " " << #RHS \
- << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
-
-// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
-// of the expressions x and y is evaluated once. Extra logging can be appended
-// using << after. For example:
-//
-// CHECK_NE(0 == 1, false) results in
-// "Check failed: false != false (0==1=false, false=false) ".
-#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
-#define CHECK_NE(x, y) CHECK_OP(x, y, != )
-#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
-#define CHECK_LT(x, y) CHECK_OP(x, y, < )
-#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
-#define CHECK_GT(x, y) CHECK_OP(x, y, > )
-
-// Helper for CHECK_STRxx(s1,s2) macros.
-#define CHECK_STROP(s1, s2, sense) \
- if (UNLIKELY((strcmp(s1, s2) == 0) != sense)) \
- LOG(FATAL) << "Check failed: " \
- << "\"" << s1 << "\"" \
- << (sense ? " == " : " != ") << "\"" << s2 << "\""
-
-// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
-#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
-#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
-
-// Perform the pthread function call(args), LOG(FATAL) on error.
-#define CHECK_PTHREAD_CALL(call, args, what) \
- do { \
- int rc = call args; \
- if (rc != 0) { \
- errno = rc; \
- PLOG(FATAL) << #call << " failed for " << what; \
- } \
- } while (false)
-
-// CHECK that can be used in a constexpr function. For example:
-//
-// constexpr int half(int n) {
-// return
-// DCHECK_CONSTEXPR(n >= 0, , 0)
-// CHECK_CONSTEXPR((n & 1) == 0),
-// << "Extra debugging output: n = " << n, 0)
-// n / 2;
-// }
-#define CHECK_CONSTEXPR(x, out, dummy) \
- (UNLIKELY(!(x))) \
- ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
- :
-
-// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
-// CHECK should be used unless profiling identifies a CHECK as being in
-// performance critical code.
-#if defined(NDEBUG)
-static constexpr bool kEnableDChecks = false;
-#else
-static constexpr bool kEnableDChecks = true;
-#endif
-
-#define DCHECK(x) \
- if (::android::base::kEnableDChecks) CHECK(x)
-#define DCHECK_EQ(x, y) \
- if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
-#define DCHECK_NE(x, y) \
- if (::android::base::kEnableDChecks) CHECK_NE(x, y)
-#define DCHECK_LE(x, y) \
- if (::android::base::kEnableDChecks) CHECK_LE(x, y)
-#define DCHECK_LT(x, y) \
- if (::android::base::kEnableDChecks) CHECK_LT(x, y)
-#define DCHECK_GE(x, y) \
- if (::android::base::kEnableDChecks) CHECK_GE(x, y)
-#define DCHECK_GT(x, y) \
- if (::android::base::kEnableDChecks) CHECK_GT(x, y)
-#define DCHECK_STREQ(s1, s2) \
- if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
-#define DCHECK_STRNE(s1, s2) \
- if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
-#if defined(NDEBUG)
-#define DCHECK_CONSTEXPR(x, out, dummy)
-#else
-#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
-#endif
-
-// Temporary class created to evaluate the LHS and RHS, used with
-// MakeEagerEvaluator to infer the types of LHS and RHS.
-template <typename LHS, typename RHS>
-struct EagerEvaluator {
- EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
- }
- LHS lhs;
- RHS rhs;
-};
-
-// Helper function for CHECK_xx.
-template <typename LHS, typename RHS>
-static inline EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
- return EagerEvaluator<LHS, RHS>(lhs, rhs);
-}
-
-// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
-// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
-// signed/unsigned warnings to protect you against combinations not explicitly
-// listed below.
-#define EAGER_PTR_EVALUATOR(T1, T2) \
- template <> \
- struct EagerEvaluator<T1, T2> { \
- EagerEvaluator(T1 l, T2 r) \
- : lhs(reinterpret_cast<const void*>(l)), \
- rhs(reinterpret_cast<const void*>(r)) { \
- } \
- const void* lhs; \
- const void* rhs; \
- }
-EAGER_PTR_EVALUATOR(const char*, const char*);
-EAGER_PTR_EVALUATOR(const char*, char*);
-EAGER_PTR_EVALUATOR(char*, const char*);
-EAGER_PTR_EVALUATOR(char*, char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(const signed char*, signed char*);
-EAGER_PTR_EVALUATOR(signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(signed char*, signed char*);
-
-// Data for the log message, not stored in LogMessage to avoid increasing the
-// stack size.
-class LogMessageData;
-
-// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
-// of a CHECK. The destructor will abort if the severity is FATAL.
-class LogMessage {
- public:
- LogMessage(const char* file, unsigned int line, LogId id,
- LogSeverity severity, int error);
-
- ~LogMessage();
-
- // Returns the stream associated with the message, the LogMessage performs
- // output when it goes out of scope.
- std::ostream& stream();
-
- // The routine that performs the actual logging.
- static void LogLine(const char* file, unsigned int line, LogId id,
- LogSeverity severity, const char* msg);
-
- private:
- const std::unique_ptr<LogMessageData> data_;
-
- DISALLOW_COPY_AND_ASSIGN(LogMessage);
-};
-
-// Allows to temporarily change the minimum severity level for logging.
-class ScopedLogSeverity {
- public:
- explicit ScopedLogSeverity(LogSeverity level);
- ~ScopedLogSeverity();
-
- private:
- LogSeverity old_;
-};
-
-} // namespace base
-} // namespace android
-
-#endif // BASE_LOGGING_H
diff --git a/base/include/base/macros.h b/base/include/base/macros.h
deleted file mode 100644
index b1ce7c6..0000000
--- a/base/include/base/macros.h
+++ /dev/null
@@ -1,188 +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 UTILS_MACROS_H
-#define UTILS_MACROS_H
-
-#include <stddef.h> // for size_t
-#include <unistd.h> // for TEMP_FAILURE_RETRY
-
-// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(exp) \
- ({ \
- decltype(exp) _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; \
- })
-#endif
-
-// A macro to disallow the copy constructor and operator= functions
-// This must be placed in the private: declarations for a class.
-//
-// For disallowing only assign or copy, delete the relevant operator or
-// constructor, for example:
-// void operator=(const TypeName&) = delete;
-// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
-// semantically, one should either use disallow both or neither. Try to
-// avoid these in new code.
-//
-// When building with C++11 toolchains, just use the language support
-// for explicitly deleted methods.
-#if __cplusplus >= 201103L
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&) = delete; \
- void operator=(const TypeName&) = delete
-#else
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-#endif
-
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
-//
-// This should be used in the private: declarations for a class
-// that wants to prevent anyone from instantiating it. This is
-// especially useful for classes containing only static methods.
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName(); \
- DISALLOW_COPY_AND_ASSIGN(TypeName)
-
-// The arraysize(arr) macro returns the # of elements in an array arr.
-// The expression is a compile-time constant, and therefore can be
-// used in defining new arrays, for example. If you use arraysize on
-// a pointer by mistake, you will get a compile-time error.
-//
-// One caveat is that arraysize() doesn't accept any array of an
-// anonymous type or a type defined inside a function. In these rare
-// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
-// due to a limitation in C++'s template system. The limitation might
-// eventually be removed, but it hasn't happened yet.
-
-// This template function declaration is used in defining arraysize.
-// Note that the function doesn't need an implementation, as we only
-// use its type.
-template <typename T, size_t N>
-char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
-
-#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-
-// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
-// but can be used on anonymous types or types defined inside
-// functions. It's less safe than arraysize as it accepts some
-// (although not all) pointers. Therefore, you should use arraysize
-// whenever possible.
-//
-// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
-// size_t.
-//
-// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
-//
-// "warning: division by zero in ..."
-//
-// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
-// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
-//
-// The following comments are on the implementation details, and can
-// be ignored by the users.
-//
-// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
-// the array) and sizeof(*(arr)) (the # of bytes in one array
-// element). If the former is divisible by the latter, perhaps arr is
-// indeed an array, in which case the division result is the # of
-// elements in the array. Otherwise, arr cannot possibly be an array,
-// and we generate a compiler error to prevent the code from
-// compiling.
-//
-// Since the size of bool is implementation-defined, we need to cast
-// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
-// result has type size_t.
-//
-// This macro is not perfect as it wrongfully accepts certain
-// pointers, namely where the pointer size is divisible by the pointee
-// size. Since all our code has to go through a 32-bit compiler,
-// where a pointer is 4 bytes, this means all pointers to a type whose
-// size is 3 or greater than 4 will be (righteously) rejected.
-#define ARRAYSIZE_UNSAFE(a) \
- ((sizeof(a) / sizeof(*(a))) / \
- static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
-
-#define LIKELY(x) __builtin_expect((x), true)
-#define UNLIKELY(x) __builtin_expect((x), false)
-
-#define WARN_UNUSED __attribute__((warn_unused_result))
-
-// A deprecated function to call to create a false use of the parameter, for
-// example:
-// int foo(int x) { UNUSED(x); return 10; }
-// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
-template <typename... T>
-void UNUSED(const T&...) {
-}
-
-// An attribute to place on a parameter to a function, for example:
-// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
-// to avoid compiler warnings.
-#define ATTRIBUTE_UNUSED __attribute__((__unused__))
-
-// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
-// between switch labels:
-// switch (x) {
-// case 40:
-// case 41:
-// if (truth_is_out_there) {
-// ++x;
-// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
-// // comments.
-// } else {
-// return x;
-// }
-// case 42:
-// ...
-//
-// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
-// followed by a semicolon. It is designed to mimic control-flow statements
-// like 'break;', so it can be placed in most places where 'break;' can, but
-// only if there are no statements on the execution path between it and the
-// next switch label.
-//
-// When compiled with clang in C++11 mode, the FALLTHROUGH_INTENDED macro is
-// expanded to [[clang::fallthrough]] attribute, which is analysed when
-// performing switch labels fall-through diagnostic ('-Wimplicit-fallthrough').
-// See clang documentation on language extensions for details:
-// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
-//
-// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
-// effect on diagnostics.
-//
-// In either case this macro has no effect on runtime behavior and performance
-// of code.
-#if defined(__clang__) && __cplusplus >= 201103L && defined(__has_warning)
-#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
-#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
-#endif
-#endif
-
-#ifndef FALLTHROUGH_INTENDED
-#define FALLTHROUGH_INTENDED \
- do { \
- } while (0)
-#endif
-
-#endif // UTILS_MACROS_H
diff --git a/base/include/base/memory.h b/base/include/base/memory.h
deleted file mode 100644
index 882582f..0000000
--- a/base/include/base/memory.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef BASE_MEMORY_H
-#define BASE_MEMORY_H
-
-namespace android {
-namespace base {
-
-// Use packed structures for access to unaligned data on targets with alignment
-// restrictions. The compiler will generate appropriate code to access these
-// structures without generating alignment exceptions.
-template <typename T>
-static inline T get_unaligned(const T* address) {
- struct unaligned {
- T v;
- } __attribute__((packed));
- const unaligned* p = reinterpret_cast<const unaligned*>(address);
- return p->v;
-}
-
-template <typename T>
-static inline void put_unaligned(T* address, T v) {
- struct unaligned {
- T v;
- } __attribute__((packed));
- unaligned* p = reinterpret_cast<unaligned*>(address);
- p->v = v;
-}
-
-} // namespace base
-} // namespace android
-
-#endif // BASE_MEMORY_H
diff --git a/base/include/base/stringprintf.h b/base/include/base/stringprintf.h
deleted file mode 100644
index d68af87..0000000
--- a/base/include/base/stringprintf.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 BASE_STRINGPRINTF_H
-#define BASE_STRINGPRINTF_H
-
-#include <stdarg.h>
-#include <string>
-
-namespace android {
-namespace base {
-
-// These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking. On Windows,
-// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
-// in %zd and PRIu64 (and related) to be recognized by the compile-time
-// checking.
-#define FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef FORMAT_ARCHETYPE
-#define FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
-
-// Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...)
- __attribute__((__format__(FORMAT_ARCHETYPE, 1, 2)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendF(std::string* dst, const char* fmt, ...)
- __attribute__((__format__(FORMAT_ARCHETYPE, 2, 3)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendV(std::string* dst, const char* format, va_list ap)
- __attribute__((__format__(FORMAT_ARCHETYPE, 2, 0)));
-
-#undef FORMAT_ARCHETYPE
-
-} // namespace base
-} // namespace android
-
-#endif // BASE_STRINGPRINTF_H
diff --git a/base/include/base/strings.h b/base/include/base/strings.h
deleted file mode 100644
index 638f845..0000000
--- a/base/include/base/strings.h
+++ /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.
- */
-
-#ifndef BASE_STRINGS_H
-#define BASE_STRINGS_H
-
-#include <sstream>
-#include <string>
-#include <vector>
-
-namespace android {
-namespace base {
-
-// Splits a string into a vector of strings.
-//
-// The string is split at each occurrence of a character in delimiters.
-//
-// The empty string is not a valid delimiter list.
-std::vector<std::string> Split(const std::string& s,
- const std::string& delimiters);
-
-// Trims whitespace off both ends of the given string.
-std::string Trim(const std::string& s);
-
-// Joins a container of things into a single string, using the given separator.
-template <typename ContainerT>
-std::string Join(const ContainerT& things, char separator) {
- if (things.empty()) {
- return "";
- }
-
- std::ostringstream result;
- result << *things.begin();
- for (auto it = std::next(things.begin()); it != things.end(); ++it) {
- result << separator << *it;
- }
- return result.str();
-}
-
-// We instantiate the common cases in strings.cpp.
-extern template std::string Join(const std::vector<std::string>&, char);
-extern template std::string Join(const std::vector<const char*>&, char);
-
-// Tests whether 's' starts with 'prefix'.
-bool StartsWith(const std::string& s, const char* prefix);
-
-// Tests whether 's' ends with 'suffix'.
-bool EndsWith(const std::string& s, const char* suffix);
-
-} // namespace base
-} // namespace android
-
-#endif // BASE_STRINGS_H
diff --git a/base/include/base/test_utils.h b/base/include/base/test_utils.h
deleted file mode 100644
index 402e0a5..0000000
--- a/base/include/base/test_utils.h
+++ /dev/null
@@ -1,51 +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 TEST_UTILS_H
-#define TEST_UTILS_H
-
-#include <string>
-
-#include <base/macros.h>
-
-class TemporaryFile {
- public:
- TemporaryFile();
- ~TemporaryFile();
-
- int fd;
- char path[1024];
-
- private:
- void init(const std::string& tmp_dir);
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
-};
-
-class TemporaryDir {
- public:
- TemporaryDir();
- ~TemporaryDir();
-
- char path[1024];
-
- private:
- bool init(const std::string& tmp_dir);
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
-};
-
-#endif // TEST_UTILS_H
diff --git a/base/logging.cpp b/base/logging.cpp
index e9e06df..6357b4b 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#ifdef _WIN32
+#if defined(_WIN32)
#include <windows.h>
#endif
-#include "base/logging.h"
+#include "android-base/logging.h"
+#include <fcntl.h>
#include <libgen.h>
+#include <time.h>
// For getprogname(3) or program_invocation_short_name.
#if defined(__ANDROID__) || defined(__APPLE__)
@@ -29,42 +31,69 @@
#include <errno.h>
#endif
+#if defined(__linux__)
+#include <sys/uio.h>
+#endif
+
#include <iostream>
#include <limits>
+#include <mutex>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
-#ifndef _WIN32
-#include <mutex>
-#endif
-
-#include "base/macros.h"
-#include "base/strings.h"
-#include "cutils/threads.h"
-
// Headers for LogMessage::LogLine.
#ifdef __ANDROID__
+#include <log/log.h>
#include <android/set_abort_message.h>
-#include "cutils/log.h"
#else
#include <sys/types.h>
#include <unistd.h>
#endif
-namespace {
-#ifndef _WIN32
-using std::mutex;
-using std::lock_guard;
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+// For gettid.
+#if defined(__APPLE__)
+#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#include <unistd.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+#if defined(_WIN32)
+typedef uint32_t thread_id;
+#else
+typedef pid_t thread_id;
+#endif
+
+static thread_id GetThreadId() {
+#if defined(__BIONIC__)
+ return gettid();
+#elif defined(__APPLE__)
+ return syscall(SYS_thread_selfid);
+#elif defined(__linux__)
+ return syscall(__NR_gettid);
+#elif defined(_WIN32)
+ return GetCurrentThreadId();
+#endif
+}
+
+namespace {
#if defined(__GLIBC__)
const char* getprogname() {
return program_invocation_short_name;
}
-#endif
-
-#else
+#elif defined(_WIN32)
const char* getprogname() {
static bool first = true;
static char progname[MAX_PATH] = {};
@@ -83,78 +112,108 @@
return progname;
}
-
-class mutex {
- public:
- mutex() {
- InitializeCriticalSection(&critical_section_);
- }
- ~mutex() {
- DeleteCriticalSection(&critical_section_);
- }
-
- void lock() {
- EnterCriticalSection(&critical_section_);
- }
-
- void unlock() {
- LeaveCriticalSection(&critical_section_);
- }
-
- private:
- CRITICAL_SECTION critical_section_;
-};
-
-template <typename LockT>
-class lock_guard {
- public:
- explicit lock_guard(LockT& lock) : lock_(lock) {
- lock_.lock();
- }
-
- ~lock_guard() {
- lock_.unlock();
- }
-
- private:
- LockT& lock_;
-
- DISALLOW_COPY_AND_ASSIGN(lock_guard);
-};
#endif
} // namespace
namespace android {
namespace base {
-static mutex logging_lock;
+static std::mutex& LoggingLock() {
+ static auto& logging_lock = *new std::mutex();
+ return logging_lock;
+}
+static LogFunction& Logger() {
#ifdef __ANDROID__
-static LogFunction gLogger = LogdLogger();
+ static auto& logger = *new LogFunction(LogdLogger());
#else
-static LogFunction gLogger = StderrLogger;
+ static auto& logger = *new LogFunction(StderrLogger);
#endif
+ return logger;
+}
+
+static AbortFunction& Aborter() {
+ static auto& aborter = *new AbortFunction(DefaultAborter);
+ return aborter;
+}
+
+static std::string& ProgramInvocationName() {
+ static auto& programInvocationName = *new std::string(getprogname());
+ return programInvocationName;
+}
static bool gInitialized = false;
static LogSeverity gMinimumLogSeverity = INFO;
-static std::unique_ptr<std::string> gProgramInvocationName;
-static const char* ProgramInvocationName() {
- if (gProgramInvocationName == nullptr) {
- gProgramInvocationName.reset(new std::string(getprogname()));
+#if defined(__linux__)
+void KernelLogger(android::base::LogId, android::base::LogSeverity severity,
+ const char* tag, const char*, unsigned int, const char* msg) {
+ // clang-format off
+ static constexpr int kLogSeverityToKernelLogLevel[] = {
+ [android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
+ // level)
+ [android::base::DEBUG] = 7, // KERN_DEBUG
+ [android::base::INFO] = 6, // KERN_INFO
+ [android::base::WARNING] = 4, // KERN_WARNING
+ [android::base::ERROR] = 3, // KERN_ERROR
+ [android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT
+ [android::base::FATAL] = 2, // KERN_CRIT
+ };
+ // clang-format on
+ static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1,
+ "Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity");
+
+ static int klog_fd = TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
+ if (klog_fd == -1) return;
+
+ int level = kLogSeverityToKernelLogLevel[severity];
+
+ // The kernel's printk buffer is only 1024 bytes.
+ // TODO: should we automatically break up long lines into multiple lines?
+ // Or we could log but with something like "..." at the end?
+ char buf[1024];
+ size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg);
+ if (size > sizeof(buf)) {
+ size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
+ level, tag, size);
}
- return gProgramInvocationName->c_str();
+ iovec iov[1];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = size;
+ TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
}
+#endif
void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
unsigned int line, const char* message) {
- static const char log_characters[] = "VDIWEF";
+ struct tm now;
+ time_t t = time(nullptr);
+
+#if defined(_WIN32)
+ localtime_s(&now, &t);
+#else
+ localtime_r(&t, &now);
+#endif
+
+ char timestamp[32];
+ strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+ static const char log_characters[] = "VDIWEFF";
static_assert(arraysize(log_characters) - 1 == FATAL + 1,
"Mismatch in size of log_characters and values in LogSeverity");
char severity_char = log_characters[severity];
- fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(),
- severity_char, getpid(), gettid(), file, line, message);
+ fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", ProgramInvocationName().c_str(),
+ severity_char, timestamp, getpid(), GetThreadId(), file, line, message);
+}
+
+void DefaultAborter(const char* abort_message) {
+#ifdef __ANDROID__
+ android_set_abort_message(abort_message);
+#else
+ UNUSED(abort_message);
+#endif
+ abort();
}
@@ -162,28 +221,27 @@
LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
}
-static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
- ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
- ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL,
-};
-static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
- "Mismatch in size of kLogSeverityToAndroidLogPriority and values "
- "in LogSeverity");
-
-static const log_id kLogIdToAndroidLogId[] = {
- LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM,
-};
-static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
- "Mismatch in size of kLogIdToAndroidLogId and values in LogId");
-
void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
const char* file, unsigned int line,
const char* message) {
+ static constexpr android_LogPriority kLogSeverityToAndroidLogPriority[] = {
+ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
+ ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL,
+ ANDROID_LOG_FATAL,
+ };
+ static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
+ "Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity");
+
int priority = kLogSeverityToAndroidLogPriority[severity];
if (id == DEFAULT) {
id = default_log_id_;
}
+ static constexpr log_id kLogIdToAndroidLogId[] = {
+ LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM,
+ };
+ static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
+ "Mismatch in size of kLogIdToAndroidLogId and values in LogId");
log_id lg_id = kLogIdToAndroidLogId[id];
if (priority == ANDROID_LOG_FATAL) {
@@ -195,26 +253,10 @@
}
#endif
-void InitLogging(char* argv[], LogFunction&& logger) {
+void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
SetLogger(std::forward<LogFunction>(logger));
- InitLogging(argv);
-}
+ SetAborter(std::forward<AbortFunction>(aborter));
-// TODO: make this public; it's independently useful.
-class ErrnoRestorer {
- public:
- ErrnoRestorer(int saved_errno) : saved_errno_(saved_errno) {
- }
-
- ~ErrnoRestorer() {
- errno = saved_errno_;
- }
-
- private:
- const int saved_errno_;
-};
-
-void InitLogging(char* argv[]) {
if (gInitialized) {
return;
}
@@ -222,10 +264,11 @@
gInitialized = true;
// Stash the command line for later use. We can use /proc/self/cmdline on
- // Linux to recover this, but we don't have that luxury on the Mac, and there
- // are a couple of argv[0] variants that are commonly used.
+ // Linux to recover this, but we don't have that luxury on the Mac/Windows,
+ // and there are a couple of argv[0] variants that are commonly used.
if (argv != nullptr) {
- gProgramInvocationName.reset(new std::string(basename(argv[0])));
+ std::lock_guard<std::mutex> lock(LoggingLock());
+ ProgramInvocationName() = basename(argv[0]);
}
const char* tags = getenv("ANDROID_LOG_TAGS");
@@ -255,12 +298,12 @@
gMinimumLogSeverity = ERROR;
continue;
case 'f':
- gMinimumLogSeverity = FATAL;
+ gMinimumLogSeverity = FATAL_WITHOUT_ABORT;
continue;
// liblog will even suppress FATAL if you say 's' for silent, but that's
// crazy!
case 's':
- gMinimumLogSeverity = FATAL;
+ gMinimumLogSeverity = FATAL_WITHOUT_ABORT;
continue;
}
}
@@ -270,15 +313,29 @@
}
void SetLogger(LogFunction&& logger) {
- lock_guard<mutex> lock(logging_lock);
- gLogger = std::move(logger);
+ std::lock_guard<std::mutex> lock(LoggingLock());
+ Logger() = std::move(logger);
}
-// We can't use basename(3) because this code runs on the Mac, which doesn't
-// have a non-modifying basename.
+void SetAborter(AbortFunction&& aborter) {
+ std::lock_guard<std::mutex> lock(LoggingLock());
+ Aborter() = std::move(aborter);
+}
+
static const char* GetFileBasename(const char* file) {
+ // We can't use basename(3) even on Unix because the Mac doesn't
+ // have a non-modifying basename.
const char* last_slash = strrchr(file, '/');
- return (last_slash == nullptr) ? file : last_slash + 1;
+ if (last_slash != nullptr) {
+ return last_slash + 1;
+ }
+#if defined(_WIN32)
+ const char* last_backslash = strrchr(file, '\\');
+ if (last_backslash != nullptr) {
+ return last_backslash + 1;
+ }
+#endif
+ return file;
}
// This indirection greatly reduces the stack impact of having lots of
@@ -286,13 +343,12 @@
class LogMessageData {
public:
LogMessageData(const char* file, unsigned int line, LogId id,
- LogSeverity severity, int error, int saved_errno)
+ LogSeverity severity, int error)
: file_(GetFileBasename(file)),
line_number_(line),
id_(id),
severity_(severity),
- error_(error),
- errno_restorer_(saved_errno) {
+ error_(error) {
}
const char* GetFile() const {
@@ -330,19 +386,19 @@
const LogId id_;
const LogSeverity severity_;
const int error_;
- ErrnoRestorer errno_restorer_;
DISALLOW_COPY_AND_ASSIGN(LogMessageData);
};
LogMessage::LogMessage(const char* file, unsigned int line, LogId id,
LogSeverity severity, int error)
- : data_(new LogMessageData(file, line, id, severity, error, errno)) {
+ : data_(new LogMessageData(file, line, id, severity, error)) {
}
LogMessage::~LogMessage() {
- if (data_->GetSeverity() < gMinimumLogSeverity) {
- return; // No need to format something we're not going to output.
+ // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
+ if (!WOULD_LOG(data_->GetSeverity())) {
+ return;
}
// Finish constructing the message.
@@ -351,27 +407,30 @@
}
std::string msg(data_->ToString());
- if (msg.find('\n') == std::string::npos) {
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
- data_->GetSeverity(), msg.c_str());
- } else {
- msg += '\n';
- size_t i = 0;
- while (i < msg.size()) {
- size_t nl = msg.find('\n', i);
- msg[nl] = '\0';
+ {
+ // Do the actual logging with the lock held.
+ std::lock_guard<std::mutex> lock(LoggingLock());
+ if (msg.find('\n') == std::string::npos) {
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
- data_->GetSeverity(), &msg[i]);
- i = nl + 1;
+ data_->GetSeverity(), msg.c_str());
+ } else {
+ msg += '\n';
+ size_t i = 0;
+ while (i < msg.size()) {
+ size_t nl = msg.find('\n', i);
+ msg[nl] = '\0';
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
+ data_->GetSeverity(), &msg[i]);
+ // Undo the zero-termination so we can give the complete message to the aborter.
+ msg[nl] = '\n';
+ i = nl + 1;
+ }
}
}
// Abort if necessary.
if (data_->GetSeverity() == FATAL) {
-#ifdef __ANDROID__
- android_set_abort_message(msg.c_str());
-#endif
- abort();
+ Aborter()(msg.c_str());
}
}
@@ -381,18 +440,26 @@
void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
LogSeverity severity, const char* message) {
- const char* tag = ProgramInvocationName();
- lock_guard<mutex> lock(logging_lock);
- gLogger(id, severity, tag, file, line, message);
+ const char* tag = ProgramInvocationName().c_str();
+ Logger()(id, severity, tag, file, line, message);
}
-ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
- old_ = gMinimumLogSeverity;
- gMinimumLogSeverity = level;
+LogSeverity GetMinimumLogSeverity() {
+ return gMinimumLogSeverity;
+}
+
+LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
+ LogSeverity old_severity = gMinimumLogSeverity;
+ gMinimumLogSeverity = new_severity;
+ return old_severity;
+}
+
+ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
+ old_ = SetMinimumLogSeverity(new_severity);
}
ScopedLogSeverity::~ScopedLogSeverity() {
- gMinimumLogSeverity = old_;
+ SetMinimumLogSeverity(old_);
}
} // namespace base
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index c12dfa5..adb041b 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -14,16 +14,20 @@
* limitations under the License.
*/
-#include "base/logging.h"
+#include "android-base/logging.h"
#include <libgen.h>
+#if defined(_WIN32)
+#include <signal.h>
+#endif
+
#include <regex>
#include <string>
-#include "base/file.h"
-#include "base/stringprintf.h"
-#include "base/test_utils.h"
+#include "android-base/file.h"
+#include "android-base/stringprintf.h"
+#include "android-base/test_utils.h"
#include <gtest/gtest.h>
@@ -33,159 +37,564 @@
#define HOST_TEST(suite, name) TEST(suite, name)
#endif
-class CapturedStderr {
- public:
- CapturedStderr() : old_stderr_(-1) {
- init();
- }
+#if defined(_WIN32)
+static void ExitSignalAbortHandler(int) {
+ _exit(3);
+}
+#endif
- ~CapturedStderr() {
- reset();
- }
-
- int fd() const {
- return temp_file_.fd;
- }
-
- private:
- void init() {
- old_stderr_ = dup(STDERR_FILENO);
- ASSERT_NE(-1, old_stderr_);
- ASSERT_NE(-1, dup2(fd(), STDERR_FILENO));
- }
-
- void reset() {
- ASSERT_NE(-1, dup2(old_stderr_, STDERR_FILENO));
- ASSERT_EQ(0, close(old_stderr_));
- }
-
- TemporaryFile temp_file_;
- int old_stderr_;
-};
-
-TEST(logging, CHECK) {
- ASSERT_DEATH(CHECK(false), "Check failed: false ");
- CHECK(true);
-
- ASSERT_DEATH(CHECK_EQ(0, 1), "Check failed: 0 == 1 ");
- CHECK_EQ(0, 0);
-
- ASSERT_DEATH(CHECK_STREQ("foo", "bar"), R"(Check failed: "foo" == "bar")");
- CHECK_STREQ("foo", "foo");
+static void SuppressAbortUI() {
+#if defined(_WIN32)
+ // We really just want to call _set_abort_behavior(0, _CALL_REPORTFAULT) to
+ // suppress the Windows Error Reporting dialog box, but that API is not
+ // available in the OS-supplied C Runtime, msvcrt.dll, that we currently
+ // use (it is available in the Visual Studio C runtime).
+ //
+ // Instead, we setup a SIGABRT handler, which is called in abort() right
+ // before calling Windows Error Reporting. In the handler, we exit the
+ // process just like abort() does.
+ ASSERT_NE(SIG_ERR, signal(SIGABRT, ExitSignalAbortHandler));
+#endif
}
-std::string make_log_pattern(android::base::LogSeverity severity,
- const char* message) {
- static const char* log_characters = "VDIWEF";
+TEST(logging, CHECK) {
+ ASSERT_DEATH({SuppressAbortUI(); CHECK(false);}, "Check failed: false ");
+ CHECK(true);
+
+ ASSERT_DEATH({SuppressAbortUI(); CHECK_EQ(0, 1);}, "Check failed: 0 == 1 ");
+ CHECK_EQ(0, 0);
+
+ ASSERT_DEATH({SuppressAbortUI(); CHECK_STREQ("foo", "bar");},
+ R"(Check failed: "foo" == "bar")");
+ CHECK_STREQ("foo", "foo");
+
+ // Test whether CHECK() and CHECK_STREQ() have a dangling if with no else.
+ bool flag = false;
+ if (true)
+ CHECK(true);
+ else
+ flag = true;
+ EXPECT_FALSE(flag) << "CHECK macro probably has a dangling if with no else";
+
+ flag = false;
+ if (true)
+ CHECK_STREQ("foo", "foo");
+ else
+ flag = true;
+ EXPECT_FALSE(flag) << "CHECK_STREQ probably has a dangling if with no else";
+}
+
+TEST(logging, DCHECK) {
+ if (android::base::kEnableDChecks) {
+ ASSERT_DEATH({SuppressAbortUI(); DCHECK(false);}, "DCheck failed: false ");
+ }
+ DCHECK(true);
+
+ if (android::base::kEnableDChecks) {
+ ASSERT_DEATH({SuppressAbortUI(); DCHECK_EQ(0, 1);}, "DCheck failed: 0 == 1 ");
+ }
+ DCHECK_EQ(0, 0);
+
+ if (android::base::kEnableDChecks) {
+ ASSERT_DEATH({SuppressAbortUI(); DCHECK_STREQ("foo", "bar");},
+ R"(DCheck failed: "foo" == "bar")");
+ }
+ DCHECK_STREQ("foo", "foo");
+
+ // No testing whether we have a dangling else, possibly. That's inherent to the if (constexpr)
+ // setup we intentionally chose to force type-checks of debug code even in release builds (so
+ // we don't get more bit-rot).
+}
+
+
+#define CHECK_WOULD_LOG_DISABLED(severity) \
+ static_assert(android::base::severity < android::base::FATAL, "Bad input"); \
+ for (size_t i = static_cast<size_t>(android::base::severity) + 1; \
+ i <= static_cast<size_t>(android::base::FATAL); \
+ ++i) { \
+ { \
+ android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
+ EXPECT_FALSE(WOULD_LOG(severity)) << i; \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
+ EXPECT_FALSE(WOULD_LOG(::android::base::severity)) << i; \
+ } \
+ } \
+
+#define CHECK_WOULD_LOG_ENABLED(severity) \
+ for (size_t i = static_cast<size_t>(android::base::VERBOSE); \
+ i <= static_cast<size_t>(android::base::severity); \
+ ++i) { \
+ { \
+ android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
+ EXPECT_TRUE(WOULD_LOG(severity)) << i; \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
+ EXPECT_TRUE(WOULD_LOG(::android::base::severity)) << i; \
+ } \
+ } \
+
+TEST(logging, WOULD_LOG_FATAL) {
+ CHECK_WOULD_LOG_ENABLED(FATAL);
+}
+
+TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_disabled) {
+ CHECK_WOULD_LOG_DISABLED(FATAL_WITHOUT_ABORT);
+}
+
+TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_enabled) {
+ CHECK_WOULD_LOG_ENABLED(FATAL_WITHOUT_ABORT);
+}
+
+TEST(logging, WOULD_LOG_ERROR_disabled) {
+ CHECK_WOULD_LOG_DISABLED(ERROR);
+}
+
+TEST(logging, WOULD_LOG_ERROR_enabled) {
+ CHECK_WOULD_LOG_ENABLED(ERROR);
+}
+
+TEST(logging, WOULD_LOG_WARNING_disabled) {
+ CHECK_WOULD_LOG_DISABLED(WARNING);
+}
+
+TEST(logging, WOULD_LOG_WARNING_enabled) {
+ CHECK_WOULD_LOG_ENABLED(WARNING);
+}
+
+TEST(logging, WOULD_LOG_INFO_disabled) {
+ CHECK_WOULD_LOG_DISABLED(INFO);
+}
+
+TEST(logging, WOULD_LOG_INFO_enabled) {
+ CHECK_WOULD_LOG_ENABLED(INFO);
+}
+
+TEST(logging, WOULD_LOG_DEBUG_disabled) {
+ CHECK_WOULD_LOG_DISABLED(DEBUG);
+}
+
+TEST(logging, WOULD_LOG_DEBUG_enabled) {
+ CHECK_WOULD_LOG_ENABLED(DEBUG);
+}
+
+TEST(logging, WOULD_LOG_VERBOSE_disabled) {
+ CHECK_WOULD_LOG_DISABLED(VERBOSE);
+}
+
+TEST(logging, WOULD_LOG_VERBOSE_enabled) {
+ CHECK_WOULD_LOG_ENABLED(VERBOSE);
+}
+
+#undef CHECK_WOULD_LOG_DISABLED
+#undef CHECK_WOULD_LOG_ENABLED
+
+
+static std::string make_log_pattern(android::base::LogSeverity severity,
+ const char* message) {
+ static const char log_characters[] = "VDIWEFF";
+ static_assert(arraysize(log_characters) - 1 == android::base::FATAL + 1,
+ "Mismatch in size of log_characters and values in LogSeverity");
char log_char = log_characters[severity];
std::string holder(__FILE__);
return android::base::StringPrintf(
- "%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ %s:[[:digit:]]+] %s",
+ "%c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s:\\d+] %s",
log_char, basename(&holder[0]), message);
}
-TEST(logging, LOG) {
- ASSERT_DEATH(LOG(FATAL) << "foobar", "foobar");
+static void CheckMessage(const CapturedStderr& cap,
+ android::base::LogSeverity severity, const char* expected) {
+ std::string output;
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+ android::base::ReadFdToString(cap.fd(), &output);
// 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
// many characters are in the log message.
- {
- CapturedStderr cap;
- LOG(WARNING) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
-
- std::string output;
- android::base::ReadFdToString(cap.fd(), &output);
- ASSERT_GT(output.length(), strlen("foobar"));
+ ASSERT_GT(output.length(), strlen(expected));
+ ASSERT_NE(nullptr, strstr(output.c_str(), expected)) << output;
#if !defined(_WIN32)
- std::regex message_regex(
- make_log_pattern(android::base::WARNING, "foobar"));
- ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+ std::regex message_regex(make_log_pattern(severity, expected));
+ ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
#endif
+}
+
+
+#define CHECK_LOG_STREAM_DISABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ LOG_STREAM(severity) << "foo bar"; \
+ ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ LOG_STREAM(::android::base::severity) << "foo bar"; \
+ ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
+ } \
+
+#define CHECK_LOG_STREAM_ENABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ LOG_STREAM(severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar"); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ LOG_STREAM(::android::base::severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar"); \
+ } \
+
+TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_disabled) {
+ CHECK_LOG_STREAM_DISABLED(FATAL_WITHOUT_ABORT);
+}
+
+TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
+ CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT);
+}
+
+TEST(logging, LOG_STREAM_ERROR_disabled) {
+ CHECK_LOG_STREAM_DISABLED(ERROR);
+}
+
+TEST(logging, LOG_STREAM_ERROR_enabled) {
+ CHECK_LOG_STREAM_ENABLED(ERROR);
+}
+
+TEST(logging, LOG_STREAM_WARNING_disabled) {
+ CHECK_LOG_STREAM_DISABLED(WARNING);
+}
+
+TEST(logging, LOG_STREAM_WARNING_enabled) {
+ CHECK_LOG_STREAM_ENABLED(WARNING);
+}
+
+TEST(logging, LOG_STREAM_INFO_disabled) {
+ CHECK_LOG_STREAM_DISABLED(INFO);
+}
+
+TEST(logging, LOG_STREAM_INFO_enabled) {
+ CHECK_LOG_STREAM_ENABLED(INFO);
+}
+
+TEST(logging, LOG_STREAM_DEBUG_disabled) {
+ CHECK_LOG_STREAM_DISABLED(DEBUG);
+}
+
+TEST(logging, LOG_STREAM_DEBUG_enabled) {
+ CHECK_LOG_STREAM_ENABLED(DEBUG);
+}
+
+TEST(logging, LOG_STREAM_VERBOSE_disabled) {
+ CHECK_LOG_STREAM_DISABLED(VERBOSE);
+}
+
+TEST(logging, LOG_STREAM_VERBOSE_enabled) {
+ CHECK_LOG_STREAM_ENABLED(VERBOSE);
+}
+
+#undef CHECK_LOG_STREAM_DISABLED
+#undef CHECK_LOG_STREAM_ENABLED
+
+
+#define CHECK_LOG_DISABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ LOG(severity) << "foo bar"; \
+ ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ LOG(::android::base::severity) << "foo bar"; \
+ ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
+ } \
+
+#define CHECK_LOG_ENABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ LOG(severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar"); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ LOG(::android::base::severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar"); \
+ } \
+
+TEST(logging, LOG_FATAL) {
+ ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
+ ASSERT_DEATH({SuppressAbortUI(); LOG(::android::base::FATAL) << "foobar";}, "foobar");
+}
+
+TEST(logging, LOG_FATAL_WITHOUT_ABORT_disabled) {
+ CHECK_LOG_DISABLED(FATAL_WITHOUT_ABORT);
+}
+
+TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
+ CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT);
+}
+
+TEST(logging, LOG_ERROR_disabled) {
+ CHECK_LOG_DISABLED(ERROR);
+}
+
+TEST(logging, LOG_ERROR_enabled) {
+ CHECK_LOG_ENABLED(ERROR);
+}
+
+TEST(logging, LOG_WARNING_disabled) {
+ CHECK_LOG_DISABLED(WARNING);
+}
+
+TEST(logging, LOG_WARNING_enabled) {
+ CHECK_LOG_ENABLED(WARNING);
+}
+
+TEST(logging, LOG_INFO_disabled) {
+ CHECK_LOG_DISABLED(INFO);
+}
+
+TEST(logging, LOG_INFO_enabled) {
+ CHECK_LOG_ENABLED(INFO);
+}
+
+TEST(logging, LOG_DEBUG_disabled) {
+ CHECK_LOG_DISABLED(DEBUG);
+}
+
+TEST(logging, LOG_DEBUG_enabled) {
+ CHECK_LOG_ENABLED(DEBUG);
+}
+
+TEST(logging, LOG_VERBOSE_disabled) {
+ CHECK_LOG_DISABLED(VERBOSE);
+}
+
+TEST(logging, LOG_VERBOSE_enabled) {
+ CHECK_LOG_ENABLED(VERBOSE);
+}
+
+#undef CHECK_LOG_DISABLED
+#undef CHECK_LOG_ENABLED
+
+
+TEST(logging, LOG_complex_param) {
+#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info) \
+ { \
+ android::base::ScopedLogSeverity sls( \
+ (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING); \
+ CapturedStderr cap; \
+ LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING) \
+ << "foobar"; \
+ if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) { \
+ CheckMessage(cap, \
+ (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
+ "foobar"); \
+ } else { \
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_CUR)); \
+ } \
}
- {
- CapturedStderr cap;
+ CHECK_LOG_COMBINATION(false,false);
+ CHECK_LOG_COMBINATION(false,true);
+ CHECK_LOG_COMBINATION(true,false);
+ CHECK_LOG_COMBINATION(true,true);
+
+#undef CHECK_LOG_COMBINATION
+}
+
+
+TEST(logging, LOG_does_not_clobber_errno) {
+ CapturedStderr cap;
+ errno = 12345;
+ LOG(INFO) << (errno = 67890);
+ EXPECT_EQ(12345, errno) << "errno was not restored";
+
+ CheckMessage(cap, android::base::INFO, "67890");
+}
+
+TEST(logging, PLOG_does_not_clobber_errno) {
+ CapturedStderr cap;
+ errno = 12345;
+ PLOG(INFO) << (errno = 67890);
+ EXPECT_EQ(12345, errno) << "errno was not restored";
+
+ CheckMessage(cap, android::base::INFO, "67890");
+}
+
+TEST(logging, LOG_does_not_have_dangling_if) {
+ CapturedStderr cap; // So the logging below has no side-effects.
+
+ // Do the test two ways: once where we hypothesize that LOG()'s if
+ // will evaluate to true (when severity is high enough) and once when we
+ // expect it to evaluate to false (when severity is not high enough).
+ bool flag = false;
+ if (true)
LOG(INFO) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+ else
+ flag = true;
- std::string output;
- android::base::ReadFdToString(cap.fd(), &output);
- ASSERT_GT(output.length(), strlen("foobar"));
+ EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
-#if !defined(_WIN32)
- std::regex message_regex(
- make_log_pattern(android::base::INFO, "foobar"));
- ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
-#endif
- }
+ flag = false;
+ if (true)
+ LOG(VERBOSE) << "foobar";
+ else
+ flag = true;
- {
- CapturedStderr cap;
- LOG(DEBUG) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
-
- std::string output;
- android::base::ReadFdToString(cap.fd(), &output);
- ASSERT_TRUE(output.empty());
- }
-
- {
- android::base::ScopedLogSeverity severity(android::base::DEBUG);
- CapturedStderr cap;
- LOG(DEBUG) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
-
- std::string output;
- android::base::ReadFdToString(cap.fd(), &output);
- ASSERT_GT(output.length(), strlen("foobar"));
-
-#if !defined(_WIN32)
- std::regex message_regex(
- make_log_pattern(android::base::DEBUG, "foobar"));
- ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
-#endif
- }
+ EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
}
-TEST(logging, PLOG) {
- {
- CapturedStderr cap;
- errno = ENOENT;
- PLOG(INFO) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+#define CHECK_PLOG_DISABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ PLOG(severity) << "foo bar"; \
+ ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ PLOG(severity) << "foo bar"; \
+ ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
+ } \
- std::string output;
- android::base::ReadFdToString(cap.fd(), &output);
- ASSERT_GT(output.length(), strlen("foobar"));
+#define CHECK_PLOG_ENABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ errno = ENOENT; \
+ PLOG(severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ errno = ENOENT; \
+ PLOG(severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
+ } \
-#if !defined(_WIN32)
- std::regex message_regex(make_log_pattern(
- android::base::INFO, "foobar: No such file or directory"));
- ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
-#endif
- }
+TEST(logging, PLOG_FATAL) {
+ ASSERT_DEATH({SuppressAbortUI(); PLOG(FATAL) << "foobar";}, "foobar");
+ ASSERT_DEATH({SuppressAbortUI(); PLOG(::android::base::FATAL) << "foobar";}, "foobar");
}
+TEST(logging, PLOG_FATAL_WITHOUT_ABORT_disabled) {
+ CHECK_PLOG_DISABLED(FATAL_WITHOUT_ABORT);
+}
+
+TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
+ CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT);
+}
+
+TEST(logging, PLOG_ERROR_disabled) {
+ CHECK_PLOG_DISABLED(ERROR);
+}
+
+TEST(logging, PLOG_ERROR_enabled) {
+ CHECK_PLOG_ENABLED(ERROR);
+}
+
+TEST(logging, PLOG_WARNING_disabled) {
+ CHECK_PLOG_DISABLED(WARNING);
+}
+
+TEST(logging, PLOG_WARNING_enabled) {
+ CHECK_PLOG_ENABLED(WARNING);
+}
+
+TEST(logging, PLOG_INFO_disabled) {
+ CHECK_PLOG_DISABLED(INFO);
+}
+
+TEST(logging, PLOG_INFO_enabled) {
+ CHECK_PLOG_ENABLED(INFO);
+}
+
+TEST(logging, PLOG_DEBUG_disabled) {
+ CHECK_PLOG_DISABLED(DEBUG);
+}
+
+TEST(logging, PLOG_DEBUG_enabled) {
+ CHECK_PLOG_ENABLED(DEBUG);
+}
+
+TEST(logging, PLOG_VERBOSE_disabled) {
+ CHECK_PLOG_DISABLED(VERBOSE);
+}
+
+TEST(logging, PLOG_VERBOSE_enabled) {
+ CHECK_PLOG_ENABLED(VERBOSE);
+}
+
+#undef CHECK_PLOG_DISABLED
+#undef CHECK_PLOG_ENABLED
+
+
TEST(logging, UNIMPLEMENTED) {
+ std::string expected = android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
+
+ CapturedStderr cap;
+ errno = ENOENT;
+ UNIMPLEMENTED(ERROR);
+ CheckMessage(cap, android::base::ERROR, expected.c_str());
+}
+
+static void NoopAborter(const char* msg ATTRIBUTE_UNUSED) {
+ LOG(ERROR) << "called noop";
+}
+
+TEST(logging, LOG_FATAL_NOOP_ABORTER) {
{
+ android::base::SetAborter(NoopAborter);
+
+ android::base::ScopedLogSeverity sls(android::base::ERROR);
CapturedStderr cap;
- errno = ENOENT;
- UNIMPLEMENTED(ERROR);
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+ LOG(FATAL) << "foobar";
+ CheckMessage(cap, android::base::FATAL, "foobar");
+ CheckMessage(cap, android::base::ERROR, "called noop");
- std::string output;
- android::base::ReadFdToString(cap.fd(), &output);
- ASSERT_GT(output.length(), strlen("unimplemented"));
-
-#if !defined(_WIN32)
- std::string expected_message =
- android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
- std::regex message_regex(
- make_log_pattern(android::base::ERROR, expected_message.c_str()));
- ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
-#endif
+ android::base::SetAborter(android::base::DefaultAborter);
}
+
+ ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
+}
+
+struct CountLineAborter {
+ static void CountLineAborterFunction(const char* msg) {
+ while (*msg != 0) {
+ if (*msg == '\n') {
+ newline_count++;
+ }
+ msg++;
+ }
+ }
+ static size_t newline_count;
+};
+size_t CountLineAborter::newline_count = 0;
+
+TEST(logging, LOG_FATAL_ABORTER_MESSAGE) {
+ CountLineAborter::newline_count = 0;
+ android::base::SetAborter(CountLineAborter::CountLineAborterFunction);
+
+ android::base::ScopedLogSeverity sls(android::base::ERROR);
+ CapturedStderr cap;
+ LOG(FATAL) << "foo\nbar";
+
+ EXPECT_EQ(CountLineAborter::newline_count, 1U + 1U); // +1 for final '\n'.
+}
+
+__attribute__((constructor)) void TestLoggingInConstructor() {
+ LOG(ERROR) << "foobar";
}
diff --git a/base/parsedouble_test.cpp b/base/parsedouble_test.cpp
new file mode 100644
index 0000000..8734c42
--- /dev/null
+++ b/base/parsedouble_test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsedouble.h"
+
+#include <gtest/gtest.h>
+
+TEST(parsedouble, smoke) {
+ double d;
+ ASSERT_FALSE(android::base::ParseDouble("", &d));
+ ASSERT_FALSE(android::base::ParseDouble("x", &d));
+ ASSERT_FALSE(android::base::ParseDouble("123.4x", &d));
+
+ ASSERT_TRUE(android::base::ParseDouble("123.4", &d));
+ ASSERT_DOUBLE_EQ(123.4, d);
+ ASSERT_TRUE(android::base::ParseDouble("-123.4", &d));
+ ASSERT_DOUBLE_EQ(-123.4, d);
+
+ ASSERT_TRUE(android::base::ParseDouble("0", &d, 0.0));
+ ASSERT_DOUBLE_EQ(0.0, d);
+ ASSERT_FALSE(android::base::ParseDouble("0", &d, 1e-9));
+ ASSERT_FALSE(android::base::ParseDouble("3.0", &d, -1.0, 2.0));
+ ASSERT_TRUE(android::base::ParseDouble("1.0", &d, 0.0, 2.0));
+ ASSERT_DOUBLE_EQ(1.0, d);
+}
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
new file mode 100644
index 0000000..483b1d3
--- /dev/null
+++ b/base/parseint_test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parseint.h"
+
+#include <gtest/gtest.h>
+
+TEST(parseint, signed_smoke) {
+ int i = 0;
+ ASSERT_FALSE(android::base::ParseInt("x", &i));
+ ASSERT_FALSE(android::base::ParseInt("123x", &i));
+
+ ASSERT_TRUE(android::base::ParseInt("123", &i));
+ ASSERT_EQ(123, i);
+ ASSERT_TRUE(android::base::ParseInt("-123", &i));
+ ASSERT_EQ(-123, i);
+
+ short s = 0;
+ ASSERT_TRUE(android::base::ParseInt("1234", &s));
+ ASSERT_EQ(1234, s);
+
+ ASSERT_TRUE(android::base::ParseInt("12", &i, 0, 15));
+ ASSERT_EQ(12, i);
+ ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
+ ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
+}
+
+TEST(parseint, unsigned_smoke) {
+ unsigned int i = 0u;
+ ASSERT_FALSE(android::base::ParseUint("x", &i));
+ ASSERT_FALSE(android::base::ParseUint("123x", &i));
+
+ ASSERT_TRUE(android::base::ParseUint("123", &i));
+ ASSERT_EQ(123u, i);
+ ASSERT_FALSE(android::base::ParseUint("-123", &i));
+
+ unsigned short s = 0u;
+ ASSERT_TRUE(android::base::ParseUint("1234", &s));
+ ASSERT_EQ(1234u, s);
+
+ ASSERT_TRUE(android::base::ParseUint("12", &i, 15u));
+ ASSERT_EQ(12u, i);
+ ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
+ ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
+}
+
+TEST(parseint, no_implicit_octal) {
+ int i = 0;
+ ASSERT_TRUE(android::base::ParseInt("0123", &i));
+ ASSERT_EQ(123, i);
+
+ unsigned int u = 0u;
+ ASSERT_TRUE(android::base::ParseUint("0123", &u));
+ ASSERT_EQ(123u, u);
+}
+
+TEST(parseint, explicit_hex) {
+ int i = 0;
+ ASSERT_TRUE(android::base::ParseInt("0x123", &i));
+ ASSERT_EQ(0x123, i);
+
+ unsigned int u = 0u;
+ ASSERT_TRUE(android::base::ParseUint("0x123", &u));
+ ASSERT_EQ(0x123u, u);
+}
+
+TEST(parseint, string) {
+ int i = 0;
+ ASSERT_TRUE(android::base::ParseInt(std::string("123"), &i));
+ ASSERT_EQ(123, i);
+
+ unsigned int u = 0u;
+ ASSERT_TRUE(android::base::ParseUint(std::string("123"), &u));
+ ASSERT_EQ(123u, u);
+}
+
+TEST(parseint, untouched_on_failure) {
+ int i = 123;
+ ASSERT_FALSE(android::base::ParseInt("456x", &i));
+ ASSERT_EQ(123, i);
+
+ unsigned int u = 123u;
+ ASSERT_FALSE(android::base::ParseInt("456x", &u));
+ ASSERT_EQ(123u, u);
+}
diff --git a/base/parsenetaddress.cpp b/base/parsenetaddress.cpp
new file mode 100644
index 0000000..dd80f6d
--- /dev/null
+++ b/base/parsenetaddress.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsenetaddress.h"
+
+#include <algorithm>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+
+namespace android {
+namespace base {
+
+bool ParseNetAddress(const std::string& address, std::string* host, int* port,
+ std::string* canonical_address, std::string* error) {
+ host->clear();
+
+ bool ipv6 = true;
+ bool saw_port = false;
+ size_t colons = std::count(address.begin(), address.end(), ':');
+ size_t dots = std::count(address.begin(), address.end(), '.');
+ std::string port_str;
+ if (address[0] == '[') {
+ // [::1]:123
+ if (address.rfind("]:") == std::string::npos) {
+ *error = StringPrintf("bad IPv6 address '%s'", address.c_str());
+ return false;
+ }
+ *host = address.substr(1, (address.find("]:") - 1));
+ port_str = address.substr(address.rfind("]:") + 2);
+ saw_port = true;
+ } else if (dots == 0 && colons >= 2 && colons <= 7) {
+ // ::1
+ *host = address;
+ } else if (colons <= 1) {
+ // 1.2.3.4 or some.accidental.domain.com
+ ipv6 = false;
+ std::vector<std::string> pieces = Split(address, ":");
+ *host = pieces[0];
+ if (pieces.size() > 1) {
+ port_str = pieces[1];
+ saw_port = true;
+ }
+ }
+
+ if (host->empty()) {
+ *error = StringPrintf("no host in '%s'", address.c_str());
+ return false;
+ }
+
+ if (saw_port) {
+ if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 ||
+ *port > 65535) {
+ *error = StringPrintf("bad port number '%s' in '%s'", port_str.c_str(),
+ address.c_str());
+ return false;
+ }
+ }
+
+ if (canonical_address != nullptr) {
+ *canonical_address =
+ StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
+ }
+
+ return true;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/parsenetaddress_test.cpp b/base/parsenetaddress_test.cpp
new file mode 100644
index 0000000..a3bfac8
--- /dev/null
+++ b/base/parsenetaddress_test.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsenetaddress.h"
+
+#include <gtest/gtest.h>
+
+using android::base::ParseNetAddress;
+
+TEST(ParseNetAddressTest, TestUrl) {
+ std::string canonical, host, error;
+ int port = 123;
+
+ EXPECT_TRUE(
+ ParseNetAddress("www.google.com", &host, &port, &canonical, &error));
+ EXPECT_EQ("www.google.com:123", canonical);
+ EXPECT_EQ("www.google.com", host);
+ EXPECT_EQ(123, port);
+
+ EXPECT_TRUE(
+ ParseNetAddress("www.google.com:666", &host, &port, &canonical, &error));
+ EXPECT_EQ("www.google.com:666", canonical);
+ EXPECT_EQ("www.google.com", host);
+ EXPECT_EQ(666, port);
+}
+
+TEST(ParseNetAddressTest, TestIpv4) {
+ std::string canonical, host, error;
+ int port = 123;
+
+ EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, &canonical, &error));
+ EXPECT_EQ("1.2.3.4:123", canonical);
+ EXPECT_EQ("1.2.3.4", host);
+ EXPECT_EQ(123, port);
+
+ EXPECT_TRUE(ParseNetAddress("1.2.3.4:666", &host, &port, &canonical, &error));
+ EXPECT_EQ("1.2.3.4:666", canonical);
+ EXPECT_EQ("1.2.3.4", host);
+ EXPECT_EQ(666, port);
+}
+
+TEST(ParseNetAddressTest, TestIpv6) {
+ std::string canonical, host, error;
+ int port = 123;
+
+ EXPECT_TRUE(ParseNetAddress("::1", &host, &port, &canonical, &error));
+ EXPECT_EQ("[::1]:123", canonical);
+ EXPECT_EQ("::1", host);
+ EXPECT_EQ(123, port);
+
+ EXPECT_TRUE(ParseNetAddress("fe80::200:5aee:feaa:20a2", &host, &port,
+ &canonical, &error));
+ EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical);
+ EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
+ EXPECT_EQ(123, port);
+
+ EXPECT_TRUE(ParseNetAddress("[::1]:666", &host, &port, &canonical, &error));
+ EXPECT_EQ("[::1]:666", canonical);
+ EXPECT_EQ("::1", host);
+ EXPECT_EQ(666, port);
+
+ EXPECT_TRUE(ParseNetAddress("[fe80::200:5aee:feaa:20a2]:666", &host, &port,
+ &canonical, &error));
+ EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical);
+ EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
+ EXPECT_EQ(666, port);
+}
+
+TEST(ParseNetAddressTest, TestInvalidAddress) {
+ std::string canonical, host;
+ int port;
+
+ std::string failure_cases[] = {
+ // Invalid IPv4.
+ "1.2.3.4:",
+ "1.2.3.4::",
+ ":123",
+
+ // Invalid IPv6.
+ ":1",
+ "::::::::1",
+ "[::1",
+ "[::1]",
+ "[::1]:",
+ "[::1]::",
+
+ // Invalid port.
+ "1.2.3.4:-1",
+ "1.2.3.4:0",
+ "1.2.3.4:65536"
+ "1.2.3.4:hello",
+ "[::1]:-1",
+ "[::1]:0",
+ "[::1]:65536",
+ "[::1]:hello",
+ };
+
+ for (const auto& address : failure_cases) {
+ // Failure should give some non-empty error string.
+ std::string error;
+ EXPECT_FALSE(ParseNetAddress(address, &host, &port, &canonical, &error));
+ EXPECT_NE("", error);
+ }
+}
+
+// Null canonical address argument.
+TEST(ParseNetAddressTest, TestNullCanonicalAddress) {
+ std::string host, error;
+ int port = 42;
+
+ EXPECT_TRUE(ParseNetAddress("www.google.com", &host, &port, nullptr, &error));
+ EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, nullptr, &error));
+ EXPECT_TRUE(ParseNetAddress("::1", &host, &port, nullptr, &error));
+}
diff --git a/base/properties.cpp b/base/properties.cpp
new file mode 100644
index 0000000..37daf9a
--- /dev/null
+++ b/base/properties.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/properties.h"
+
+#include <sys/system_properties.h>
+
+#include <string>
+
+#include <android-base/parseint.h>
+
+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;
+
+ char buf[PROP_VALUE_MAX];
+ if (__system_property_read(pi, nullptr, buf) > 0) return buf;
+
+ // 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 default_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") {
+ return true;
+ } else if (value == "0" || value == "n" || value == "no" || value == "off" || value == "false") {
+ return false;
+ }
+ return default_value;
+}
+
+template <typename T>
+T GetIntProperty(const std::string& key, T default_value, T min, T max) {
+ T result;
+ std::string value = GetProperty(key, "");
+ if (!value.empty() && android::base::ParseInt(value, &result, min, max)) return result;
+ return default_value;
+}
+
+template <typename T>
+T GetUintProperty(const std::string& key, T default_value, T max) {
+ T result;
+ std::string value = GetProperty(key, "");
+ if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
+ return default_value;
+}
+
+template int8_t GetIntProperty(const std::string&, int8_t, int8_t, int8_t);
+template int16_t GetIntProperty(const std::string&, int16_t, int16_t, int16_t);
+template int32_t GetIntProperty(const std::string&, int32_t, int32_t, int32_t);
+template int64_t GetIntProperty(const std::string&, int64_t, int64_t, int64_t);
+
+template uint8_t GetUintProperty(const std::string&, uint8_t, uint8_t);
+template uint16_t GetUintProperty(const std::string&, uint16_t, uint16_t);
+template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
+template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
+
+bool SetProperty(const std::string& key, const std::string& value) {
+ return (__system_property_set(key.c_str(), value.c_str()) == 0);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
new file mode 100644
index 0000000..da89ec5
--- /dev/null
+++ b/base/properties_test.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/properties.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+TEST(properties, smoke) {
+ android::base::SetProperty("debug.libbase.property_test", "hello");
+
+ std::string s = android::base::GetProperty("debug.libbase.property_test", "");
+ ASSERT_EQ("hello", s);
+
+ android::base::SetProperty("debug.libbase.property_test", "world");
+ s = android::base::GetProperty("debug.libbase.property_test", "");
+ ASSERT_EQ("world", s);
+
+ s = android::base::GetProperty("this.property.does.not.exist", "");
+ ASSERT_EQ("", s);
+
+ s = android::base::GetProperty("this.property.does.not.exist", "default");
+ ASSERT_EQ("default", s);
+}
+
+TEST(properties, empty) {
+ // Because you can't delete a property, people "delete" them by
+ // setting them to the empty string. In that case we'd want to
+ // keep the default value (like cutils' property_get did).
+ android::base::SetProperty("debug.libbase.property_test", "");
+ std::string s = android::base::GetProperty("debug.libbase.property_test", "default");
+ ASSERT_EQ("default", s);
+}
+
+static void CheckGetBoolProperty(bool expected, const std::string& value, bool default_value) {
+ android::base::SetProperty("debug.libbase.property_test", value.c_str());
+ ASSERT_EQ(expected, android::base::GetBoolProperty("debug.libbase.property_test", default_value));
+}
+
+TEST(properties, GetBoolProperty_true) {
+ CheckGetBoolProperty(true, "1", false);
+ CheckGetBoolProperty(true, "y", false);
+ CheckGetBoolProperty(true, "yes", false);
+ CheckGetBoolProperty(true, "on", false);
+ CheckGetBoolProperty(true, "true", false);
+}
+
+TEST(properties, GetBoolProperty_false) {
+ CheckGetBoolProperty(false, "0", true);
+ CheckGetBoolProperty(false, "n", true);
+ CheckGetBoolProperty(false, "no", true);
+ CheckGetBoolProperty(false, "off", true);
+ CheckGetBoolProperty(false, "false", true);
+}
+
+TEST(properties, GetBoolProperty_default) {
+ CheckGetBoolProperty(true, "burp", true);
+ CheckGetBoolProperty(false, "burp", false);
+}
+
+template <typename T> void CheckGetIntProperty() {
+ // Positive and negative.
+ android::base::SetProperty("debug.libbase.property_test", "-12");
+ EXPECT_EQ(T(-12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
+ android::base::SetProperty("debug.libbase.property_test", "12");
+ EXPECT_EQ(T(12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
+
+ // Default value.
+ android::base::SetProperty("debug.libbase.property_test", "");
+ EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
+
+ // Bounds checks.
+ android::base::SetProperty("debug.libbase.property_test", "0");
+ EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
+ android::base::SetProperty("debug.libbase.property_test", "1");
+ EXPECT_EQ(T(1), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
+ android::base::SetProperty("debug.libbase.property_test", "2");
+ EXPECT_EQ(T(2), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
+ android::base::SetProperty("debug.libbase.property_test", "3");
+ EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
+}
+
+template <typename T> void CheckGetUintProperty() {
+ // Positive.
+ android::base::SetProperty("debug.libbase.property_test", "12");
+ EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
+
+ // Default value.
+ android::base::SetProperty("debug.libbase.property_test", "");
+ EXPECT_EQ(T(45), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
+
+ // Bounds checks.
+ android::base::SetProperty("debug.libbase.property_test", "12");
+ EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 33, 22));
+ android::base::SetProperty("debug.libbase.property_test", "12");
+ EXPECT_EQ(T(5), android::base::GetUintProperty<T>("debug.libbase.property_test", 5, 10));
+}
+
+TEST(properties, GetIntProperty_int8_t) { CheckGetIntProperty<int8_t>(); }
+TEST(properties, GetIntProperty_int16_t) { CheckGetIntProperty<int16_t>(); }
+TEST(properties, GetIntProperty_int32_t) { CheckGetIntProperty<int32_t>(); }
+TEST(properties, GetIntProperty_int64_t) { CheckGetIntProperty<int64_t>(); }
+
+TEST(properties, GetUintProperty_uint8_t) { CheckGetUintProperty<uint8_t>(); }
+TEST(properties, GetUintProperty_uint16_t) { CheckGetUintProperty<uint16_t>(); }
+TEST(properties, GetUintProperty_uint32_t) { CheckGetUintProperty<uint32_t>(); }
+TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
diff --git a/base/quick_exit.cpp b/base/quick_exit.cpp
new file mode 100644
index 0000000..e4dd62b
--- /dev/null
+++ b/base/quick_exit.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/quick_exit.h"
+
+#if !defined(__linux__)
+
+#include <mutex>
+#include <vector>
+
+namespace android {
+namespace base {
+
+static auto& quick_exit_mutex = *new std::mutex();
+static auto& quick_exit_handlers = *new std::vector<void (*)()>();
+
+void quick_exit(int exit_code) {
+ std::lock_guard<std::mutex> lock(quick_exit_mutex);
+ for (auto it = quick_exit_handlers.rbegin(); it != quick_exit_handlers.rend(); ++it) {
+ (*it)();
+ }
+ _Exit(exit_code);
+}
+
+int at_quick_exit(void (*func)()) {
+ std::lock_guard<std::mutex> lock(quick_exit_mutex);
+ quick_exit_handlers.push_back(func);
+ return 0;
+}
+
+} // namespace base
+} // namespace android
+#endif
diff --git a/base/quick_exit_test.cpp b/base/quick_exit_test.cpp
new file mode 100644
index 0000000..7ca8156
--- /dev/null
+++ b/base/quick_exit_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/quick_exit.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "android-base/test_utils.h"
+
+// These tests are a bit sketchy, since each test run adds global state that affects subsequent
+// tests (including ones not in this file!). Exit with 0 in Exiter and stick the at_quick_exit test
+// at the end to hack around this.
+struct Exiter {
+ ~Exiter() {
+ _Exit(0);
+ }
+};
+
+TEST(quick_exit, smoke) {
+ ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(123), "");
+}
+
+TEST(quick_exit, skip_static_destructors) {
+ static Exiter exiter;
+ ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(123), "");
+}
+
+TEST(quick_exit, at_quick_exit) {
+ static int counter = 4;
+ // "Functions passed to at_quick_exit are called in reverse order of their registration."
+ ASSERT_EQ(0, android::base::at_quick_exit([]() { _exit(counter); }));
+ ASSERT_EQ(0, android::base::at_quick_exit([]() { counter += 2; }));
+ ASSERT_EQ(0, android::base::at_quick_exit([]() { counter *= 10; }));
+ ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(42), "");
+}
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
index d55ff52..78e1e8d 100644
--- a/base/stringprintf.cpp
+++ b/base/stringprintf.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
#include <stdio.h>
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
index 5cc2086..fc009b1 100644
--- a/base/stringprintf_test.cpp
+++ b/base/stringprintf_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
#include <gtest/gtest.h>
diff --git a/base/strings.cpp b/base/strings.cpp
index bac983b..46fe939 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/strings.h"
+#include "android-base/strings.h"
#include <stdlib.h>
#include <string.h>
@@ -83,19 +83,37 @@
// aid compile time and binary size.
template std::string Join(const std::vector<std::string>&, char);
template std::string Join(const std::vector<const char*>&, char);
+template std::string Join(const std::vector<std::string>&, const std::string&);
+template std::string Join(const std::vector<const char*>&, const std::string&);
bool StartsWith(const std::string& s, const char* prefix) {
- return s.compare(0, strlen(prefix), prefix) == 0;
+ return strncmp(s.c_str(), prefix, strlen(prefix)) == 0;
}
-bool EndsWith(const std::string& s, const char* suffix) {
+bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
+ return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
+}
+
+static bool EndsWith(const std::string& s, const char* suffix, bool case_sensitive) {
size_t suffix_length = strlen(suffix);
size_t string_length = s.size();
if (suffix_length > string_length) {
return false;
}
size_t offset = string_length - suffix_length;
- return s.compare(offset, suffix_length, suffix) == 0;
+ return (case_sensitive ? strncmp : strncasecmp)(s.c_str() + offset, suffix, suffix_length) == 0;
+}
+
+bool EndsWith(const std::string& s, const char* suffix) {
+ return EndsWith(s, suffix, true);
+}
+
+bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
+ return EndsWith(s, suffix, false);
+}
+
+bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) {
+ return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
}
} // namespace base
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 5f67575..7a65a00 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/strings.h"
+#include "android-base/strings.h"
#include <gtest/gtest.h>
@@ -134,44 +134,120 @@
"2,1" == android::base::Join(list, ','));
}
-TEST(strings, startswith_empty) {
+TEST(strings, StartsWith_empty) {
ASSERT_FALSE(android::base::StartsWith("", "foo"));
ASSERT_TRUE(android::base::StartsWith("", ""));
}
-TEST(strings, startswith_simple) {
+TEST(strings, StartsWithIgnoreCase_empty) {
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("", "foo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("", ""));
+}
+
+TEST(strings, StartsWith_simple) {
ASSERT_TRUE(android::base::StartsWith("foo", ""));
ASSERT_TRUE(android::base::StartsWith("foo", "f"));
ASSERT_TRUE(android::base::StartsWith("foo", "fo"));
ASSERT_TRUE(android::base::StartsWith("foo", "foo"));
}
-TEST(strings, startswith_prefix_too_long) {
+TEST(strings, StartsWithIgnoreCase_simple) {
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", ""));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "f"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "F"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Fo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Foo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FoO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOO"));
+}
+
+TEST(strings, StartsWith_prefix_too_long) {
ASSERT_FALSE(android::base::StartsWith("foo", "foobar"));
}
-TEST(strings, startswith_contains_prefix) {
+TEST(strings, StartsWithIgnoreCase_prefix_too_long) {
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "foobar"));
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "FOOBAR"));
+}
+
+TEST(strings, StartsWith_contains_prefix) {
ASSERT_FALSE(android::base::StartsWith("foobar", "oba"));
ASSERT_FALSE(android::base::StartsWith("foobar", "bar"));
}
-TEST(strings, endswith_empty) {
+TEST(strings, StartsWithIgnoreCase_contains_prefix) {
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "oba"));
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "OBA"));
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "bar"));
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
+}
+
+TEST(strings, EndsWith_empty) {
ASSERT_FALSE(android::base::EndsWith("", "foo"));
ASSERT_TRUE(android::base::EndsWith("", ""));
}
-TEST(strings, endswith_simple) {
+TEST(strings, EndsWithIgnoreCase_empty) {
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "foo"));
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "FOO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("", ""));
+}
+
+TEST(strings, EndsWith_simple) {
ASSERT_TRUE(android::base::EndsWith("foo", ""));
ASSERT_TRUE(android::base::EndsWith("foo", "o"));
ASSERT_TRUE(android::base::EndsWith("foo", "oo"));
ASSERT_TRUE(android::base::EndsWith("foo", "foo"));
}
-TEST(strings, endswith_prefix_too_long) {
+TEST(strings, EndsWithIgnoreCase_simple) {
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", ""));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "o"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "O"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Oo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "OO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Foo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FoO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOO"));
+}
+
+TEST(strings, EndsWith_prefix_too_long) {
ASSERT_FALSE(android::base::EndsWith("foo", "foobar"));
}
-TEST(strings, endswith_contains_prefix) {
+TEST(strings, EndsWithIgnoreCase_prefix_too_long) {
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "foobar"));
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "FOOBAR"));
+}
+
+TEST(strings, EndsWith_contains_prefix) {
ASSERT_FALSE(android::base::EndsWith("foobar", "oba"));
ASSERT_FALSE(android::base::EndsWith("foobar", "foo"));
}
+
+TEST(strings, EndsWithIgnoreCase_contains_prefix) {
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "OBA"));
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
+}
+
+TEST(strings, EqualsIgnoreCase) {
+ ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
+ ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
+ ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
+ ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
+}
diff --git a/base/test_main.cpp b/base/test_main.cpp
index 546923d..7fa6a84 100644
--- a/base/test_main.cpp
+++ b/base/test_main.cpp
@@ -16,7 +16,7 @@
#include <gtest/gtest.h>
-#include "base/logging.h"
+#include "android-base/logging.h"
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index b0c5a12..3b3d698 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-#include "base/logging.h"
-#include "base/test_utils.h"
-#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
+#include "android-base/logging.h"
+#include "android-base/test_utils.h"
#include <fcntl.h>
#include <stdio.h>
@@ -27,6 +26,9 @@
#if defined(_WIN32)
#include <windows.h>
#include <direct.h>
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
#endif
#include <string>
@@ -37,9 +39,9 @@
return -1;
}
// Use open() to match the close() that TemporaryFile's destructor does.
- // Note that on Windows, this does CR/LF translation and _setmode() should
- // be used to change that if appropriate.
- return open(template_name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
+ // Use O_BINARY to match base file APIs.
+ return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
+ S_IRUSR | S_IWUSR);
}
char* mkdtemp(char* template_name) {
@@ -100,3 +102,32 @@
OS_PATH_SEPARATOR);
return (mkdtemp(path) != nullptr);
}
+
+CapturedStderr::CapturedStderr() : old_stderr_(-1) {
+ init();
+}
+
+CapturedStderr::~CapturedStderr() {
+ reset();
+}
+
+int CapturedStderr::fd() const {
+ return temp_file_.fd;
+}
+
+void CapturedStderr::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));
+#endif
+ old_stderr_ = dup(STDERR_FILENO);
+ CHECK_NE(-1, old_stderr_);
+ CHECK_NE(-1, dup2(fd(), STDERR_FILENO));
+}
+
+void CapturedStderr::reset() {
+ CHECK_NE(-1, dup2(old_stderr_, STDERR_FILENO));
+ CHECK_EQ(0, close(old_stderr_));
+ // Note: cannot restore prior setvbuf() setting.
+}
diff --git a/base/utf8.cpp b/base/utf8.cpp
new file mode 100755
index 0000000..3cca700
--- /dev/null
+++ b/base/utf8.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <windows.h>
+
+#include "android-base/utf8.h"
+
+#include <fcntl.h>
+
+#include <string>
+
+#include "android-base/logging.h"
+
+namespace android {
+namespace base {
+
+// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
+static void SetErrnoFromLastError() {
+ switch (GetLastError()) {
+ case ERROR_NO_UNICODE_TRANSLATION:
+ errno = EILSEQ;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+}
+
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
+ utf8->clear();
+
+ if (size == 0) {
+ return true;
+ }
+
+ // TODO: Consider using std::wstring_convert once libcxx is supported on
+ // Windows.
+
+ // Only Vista or later has this flag that causes WideCharToMultiByte() to
+ // return an error on invalid characters.
+ const DWORD flags =
+#if (WINVER >= 0x0600)
+ WC_ERR_INVALID_CHARS;
+#else
+ 0;
+#endif
+
+ const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+ NULL, 0, NULL, NULL);
+ if (chars_required <= 0) {
+ SetErrnoFromLastError();
+ return false;
+ }
+
+ // This could potentially throw a std::bad_alloc exception.
+ utf8->resize(chars_required);
+
+ const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+ &(*utf8)[0], chars_required, NULL,
+ NULL);
+ if (result != chars_required) {
+ SetErrnoFromLastError();
+ CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
+ << " chars to buffer of " << chars_required << " chars";
+ utf8->clear();
+ return false;
+ }
+
+ return true;
+}
+
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
+ // Compute string length of NULL-terminated string with wcslen().
+ return WideToUTF8(utf16, wcslen(utf16), utf8);
+}
+
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
+ // Use the stored length of the string which allows embedded NULL characters
+ // to be converted.
+ return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
+}
+
+// Internal helper function that takes MultiByteToWideChar() flags.
+static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
+ const DWORD flags) {
+ utf16->clear();
+
+ if (size == 0) {
+ return true;
+ }
+
+ // TODO: Consider using std::wstring_convert once libcxx is supported on
+ // Windows.
+ const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+ NULL, 0);
+ if (chars_required <= 0) {
+ SetErrnoFromLastError();
+ return false;
+ }
+
+ // This could potentially throw a std::bad_alloc exception.
+ utf16->resize(chars_required);
+
+ const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+ &(*utf16)[0], chars_required);
+ if (result != chars_required) {
+ SetErrnoFromLastError();
+ CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
+ << " chars to buffer of " << chars_required << " chars";
+ utf16->clear();
+ return false;
+ }
+
+ return true;
+}
+
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
+ // If strictly interpreting as UTF-8 succeeds, return success.
+ if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
+ return true;
+ }
+
+ const int saved_errno = errno;
+
+ // Fallback to non-strict interpretation, allowing invalid characters and
+ // converting as best as possible, and return false to signify a problem.
+ (void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
+ errno = saved_errno;
+ return false;
+}
+
+bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
+ // Compute string length of NULL-terminated string with strlen().
+ return UTF8ToWide(utf8, strlen(utf8), utf16);
+}
+
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
+ // Use the stored length of the string which allows embedded NULL characters
+ // to be converted.
+ return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
+}
+
+// Versions of standard library APIs that support UTF-8 strings.
+namespace utf8 {
+
+int open(const char* name, int flags, ...) {
+ std::wstring name_utf16;
+ if (!UTF8ToWide(name, &name_utf16)) {
+ return -1;
+ }
+
+ int mode = 0;
+ if ((flags & O_CREAT) != 0) {
+ va_list args;
+ va_start(args, flags);
+ mode = va_arg(args, int);
+ va_end(args);
+ }
+
+ return _wopen(name_utf16.c_str(), flags, mode);
+}
+
+int unlink(const char* name) {
+ std::wstring name_utf16;
+ if (!UTF8ToWide(name, &name_utf16)) {
+ return -1;
+ }
+
+ return _wunlink(name_utf16.c_str());
+}
+
+} // namespace utf8
+} // namespace base
+} // namespace android
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
new file mode 100755
index 0000000..ae8fc8c
--- /dev/null
+++ b/base/utf8_test.cpp
@@ -0,0 +1,412 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "android-base/utf8.h"
+
+#include <gtest/gtest.h>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace base {
+
+TEST(UTFStringConversionsTest, ConvertInvalidUTF8) {
+ std::wstring wide;
+
+ errno = 0;
+
+ // Standalone \xa2 is an invalid UTF-8 sequence, so this should return an
+ // error. Concatenate two C/C++ literal string constants to prevent the
+ // compiler from giving an error about "\xa2af" containing a "hex escape
+ // sequence out of range".
+ EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide));
+
+ EXPECT_EQ(EILSEQ, errno);
+
+ // Even if an invalid character is encountered, UTF8ToWide() should still do
+ // its best to convert the rest of the string. sysdeps_win32.cpp:
+ // _console_write_utf8() depends on this behavior.
+ //
+ // Thus, we verify that the valid characters are converted, but we ignore the
+ // specific replacement character that UTF8ToWide() may replace the invalid
+ // UTF-8 characters with because we want to allow that to change if the
+ // implementation changes.
+ EXPECT_EQ(0U, wide.find(L"before"));
+ const wchar_t after_wide[] = L"after";
+ EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc
+
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The tests below from utf_string_conversions_unittest.cc check for this
+// preprocessor symbol, so define it, as it is appropriate for Windows.
+#define WCHAR_T_IS_UTF16
+static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes");
+
+// The tests below from utf_string_conversions_unittest.cc call versions of
+// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are
+// stub implementations with that signature. These are just for testing and
+// should not be moved to base because they assert/expect no errors which is
+// probably not a good idea (or at least it is something that should be left
+// up to the caller, not a base library).
+
+static std::wstring UTF8ToWide(const std::string& utf8) {
+ std::wstring utf16;
+ EXPECT_TRUE(UTF8ToWide(utf8, &utf16));
+ return utf16;
+}
+
+static std::string WideToUTF8(const std::wstring& utf16) {
+ std::string utf8;
+ EXPECT_TRUE(WideToUTF8(utf16, &utf8));
+ return utf8;
+}
+
+namespace {
+
+const wchar_t* const kConvertRoundtripCases[] = {
+ L"Google Video",
+ // "网页 图片 资讯更多 »"
+ L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+ // "Παγκόσμιος Ιστός"
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+ // "Поиск страниц на русском"
+ L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+ L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+ L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+ // "전체서비스"
+ L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+
+ // Test characters that take more than 16 bits. This will depend on whether
+ // wchar_t is 16 or 32 bits.
+#if defined(WCHAR_T_IS_UTF16)
+ L"\xd800\xdf00",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+#elif defined(WCHAR_T_IS_UTF32)
+ L"\x10300",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\x11d40\x11d41\x11d42\x11d43\x11d44",
+#endif
+};
+
+} // namespace
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
+ // we round-trip all the wide strings through UTF-8 to make sure everything
+ // agrees on the conversion. This uses the stream operators to test them
+ // simultaneously.
+ for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::ostringstream utf8;
+ utf8 << WideToUTF8(kConvertRoundtripCases[i]);
+ std::wostringstream wide;
+ wide << UTF8ToWide(utf8.str());
+
+ EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
+ }
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
+ // An empty std::wstring should be converted to an empty std::string,
+ // and vice versa.
+ std::wstring wempty;
+ std::string empty;
+ EXPECT_EQ(empty, WideToUTF8(wempty));
+ EXPECT_EQ(wempty, UTF8ToWide(empty));
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
+ struct UTF8ToWideCase {
+ const char* utf8;
+ const wchar_t* wide;
+ bool success;
+ } convert_cases[] = {
+ // Regular UTF-8 input.
+ {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
+ // Non-character is passed through.
+ {"\xef\xbf\xbfHello", L"\xffffHello", true},
+ // Truncated UTF-8 sequence.
+ {"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+ // Truncated off the end.
+ {"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
+ // Non-shortest-form UTF-8.
+ {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+ // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
+ // Note that for whatever reason, this test fails on Windows XP.
+ {"\xed\xb0\x80", L"\xfffd", false},
+ // Non-BMP characters. The second is a non-character regarded as valid.
+ // The result will either be in UTF-16 or UTF-32.
+#if defined(WCHAR_T_IS_UTF16)
+ {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
+ {"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
+#elif defined(WCHAR_T_IS_UTF32)
+ {"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
+ {"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
+#endif
+ };
+
+ for (size_t i = 0; i < arraysize(convert_cases); i++) {
+ std::wstring converted;
+ errno = 0;
+ const bool success = UTF8ToWide(convert_cases[i].utf8,
+ strlen(convert_cases[i].utf8),
+ &converted);
+ EXPECT_EQ(convert_cases[i].success, success);
+ // The original test always compared expected and converted, but don't do
+ // that because our implementation of UTF8ToWide() does not guarantee to
+ // produce the same output in error situations.
+ if (success) {
+ std::wstring expected(convert_cases[i].wide);
+ EXPECT_EQ(expected, converted);
+ } else {
+ EXPECT_EQ(EILSEQ, errno);
+ }
+ }
+
+ // Manually test an embedded NULL.
+ std::wstring converted;
+ EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
+ ASSERT_EQ(3U, converted.length());
+ EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
+ EXPECT_EQ('Z', converted[1]);
+ EXPECT_EQ('\t', converted[2]);
+
+ // Make sure that conversion replaces, not appends.
+ EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
+ ASSERT_EQ(1U, converted.length());
+ EXPECT_EQ('B', converted[0]);
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+// This test is only valid when wchar_t == UTF-16.
+TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
+ struct WideToUTF8Case {
+ const wchar_t* utf16;
+ const char* utf8;
+ bool success;
+ } convert_cases[] = {
+ // Regular UTF-16 input.
+ {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+ // Test a non-BMP character.
+ {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
+ // Non-characters are passed through.
+ {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+ {L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
+ // The first character is a truncated UTF-16 character.
+ // Note that for whatever reason, this test fails on Windows XP.
+ {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd",
+#if (WINVER >= 0x0600)
+ // Only Vista and later has a new API/flag that correctly returns false.
+ false
+#else
+ true
+#endif
+ },
+ // Truncated at the end.
+ // Note that for whatever reason, this test fails on Windows XP.
+ {L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd",
+#if (WINVER >= 0x0600)
+ // Only Vista and later has a new API/flag that correctly returns false.
+ false
+#else
+ true
+#endif
+ },
+ };
+
+ for (size_t i = 0; i < arraysize(convert_cases); i++) {
+ std::string converted;
+ errno = 0;
+ const bool success = WideToUTF8(convert_cases[i].utf16,
+ wcslen(convert_cases[i].utf16),
+ &converted);
+ EXPECT_EQ(convert_cases[i].success, success);
+ // The original test always compared expected and converted, but don't do
+ // that because our implementation of WideToUTF8() does not guarantee to
+ // produce the same output in error situations.
+ if (success) {
+ std::string expected(convert_cases[i].utf8);
+ EXPECT_EQ(expected, converted);
+ } else {
+ EXPECT_EQ(EILSEQ, errno);
+ }
+ }
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+// This test is only valid when wchar_t == UTF-32.
+TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
+ struct WideToUTF8Case {
+ const wchar_t* utf32;
+ const char* utf8;
+ bool success;
+ } convert_cases[] = {
+ // Regular 16-bit input.
+ {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+ // Test a non-BMP character.
+ {L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
+ // Non-characters are passed through.
+ {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+ {L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
+ // Invalid Unicode code points.
+ {L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
+ // The first character is a truncated UTF-16 character.
+ {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
+ {L"\xdc01Hello", "\xef\xbf\xbdHello", false},
+ };
+
+ for (size_t i = 0; i < arraysize(convert_cases); i++) {
+ std::string converted;
+ EXPECT_EQ(convert_cases[i].success,
+ WideToUTF8(convert_cases[i].utf32,
+ wcslen(convert_cases[i].utf32),
+ &converted));
+ std::string expected(convert_cases[i].utf8);
+ EXPECT_EQ(expected, converted);
+ }
+}
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// The test below uses these types and functions, so just do enough to get the
+// test running.
+typedef wchar_t char16;
+typedef std::wstring string16;
+
+template<typename T>
+static void* WriteInto(T* t, size_t size) {
+ // std::(w)string::resize() already includes space for a NULL terminator.
+ t->resize(size - 1);
+ return &(*t)[0];
+}
+
+// A stub implementation that calls a helper from above, just to get the test
+// below working. This is just for testing and should not be moved to base
+// because this ignores errors which is probably not a good idea, plus it takes
+// a string16 type which we don't really have.
+static std::string UTF16ToUTF8(const string16& utf16) {
+ return WideToUTF8(utf16);
+}
+
+TEST(UTFStringConversionsTest, ConvertMultiString) {
+ static char16 multi16[] = {
+ 'f', 'o', 'o', '\0',
+ 'b', 'a', 'r', '\0',
+ 'b', 'a', 'z', '\0',
+ '\0'
+ };
+ static char multi[] = {
+ 'f', 'o', 'o', '\0',
+ 'b', 'a', 'r', '\0',
+ 'b', 'a', 'z', '\0',
+ '\0'
+ };
+ string16 multistring16;
+ memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
+ sizeof(multi16));
+ EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
+ std::string expected;
+ memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
+ EXPECT_EQ(arraysize(multi) - 1, expected.length());
+ const std::string& converted = UTF16ToUTF8(multistring16);
+ EXPECT_EQ(arraysize(multi) - 1, converted.length());
+ EXPECT_EQ(expected, converted);
+}
+
+// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8()
+// and SysUTF8ToWide(), so these are stub implementations that call the helpers
+// above. These are just for testing and should not be moved to base because
+// they ignore errors which is probably not a good idea.
+
+static std::string SysWideToUTF8(const std::wstring& utf16) {
+ return WideToUTF8(utf16);
+}
+
+static std::wstring SysUTF8ToWide(const std::string& utf8) {
+ return UTF8ToWide(utf8);
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifdef WCHAR_T_IS_UTF32
+static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
+#else
+static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
+#endif
+
+TEST(SysStrings, SysWideToUTF8) {
+ EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
+ EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
+
+ // >16 bits
+ EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
+
+ // Error case. When Windows finds a UTF-16 character going off the end of
+ // a string, it just converts that literal value to UTF-8, even though this
+ // is invalid.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
+ // SysWideToUTF8(L"\x4f60\xd800zyxw"));
+
+ // Test embedded NULLs.
+ std::wstring wide_null(L"a");
+ wide_null.push_back(0);
+ wide_null.push_back('b');
+
+ std::string expected_null("a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
+}
+
+TEST(SysStrings, SysUTF8ToWide) {
+ EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
+ EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
+ // >16 bits
+ EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
+
+ // Error case. When Windows finds an invalid UTF-8 character, it just skips
+ // it. This seems weird because it's inconsistent with the reverse conversion.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
+
+ // Test embedded NULLs.
+ std::string utf8_null("a");
+ utf8_null.push_back(0);
+ utf8_null.push_back('b');
+
+ std::wstring expected_null(L"a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
+}
+
+} // namespace base
+} // namespace android
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
new file mode 100644
index 0000000..d98a9d7
--- /dev/null
+++ b/bootstat/Android.bp
@@ -0,0 +1,92 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+bootstat_lib_src_files = [
+ "boot_event_record_store.cpp",
+ "histogram_logger.cpp",
+ "uptime_parser.cpp",
+]
+
+cc_defaults {
+ name: "bootstat_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+
+ // 524291 corresponds to sysui_histogram, from
+ // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
+ "-DHISTOGRAM_LOG_TAG=524291",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+ whole_static_libs: ["libgtest_prod"],
+ // Clang is required because of C++14
+ clang: true,
+}
+
+// bootstat static library
+// -----------------------------------------------------------------------------
+cc_library_static {
+ name: "libbootstat",
+ defaults: ["bootstat_defaults"],
+ srcs: bootstat_lib_src_files,
+}
+
+// bootstat static library, debug
+// -----------------------------------------------------------------------------
+cc_library_static {
+ name: "libbootstat_debug",
+ defaults: ["bootstat_defaults"],
+ host_supported: true,
+ srcs: bootstat_lib_src_files,
+
+ target: {
+ host: {
+ cflags: ["-UNDEBUG"],
+ },
+ },
+}
+
+// bootstat binary
+// -----------------------------------------------------------------------------
+cc_binary {
+ name: "bootstat",
+ defaults: ["bootstat_defaults"],
+ static_libs: ["libbootstat"],
+ init_rc: ["bootstat.rc"],
+ srcs: ["bootstat.cpp"],
+}
+
+// Native tests
+// -----------------------------------------------------------------------------
+cc_test {
+ name: "bootstat_tests",
+ defaults: ["bootstat_defaults"],
+ host_supported: true,
+ static_libs: [
+ "libbootstat_debug",
+ "libgmock",
+ ],
+ srcs: [
+ "boot_event_record_store_test.cpp",
+ "testrunner.cpp",
+ ],
+}
diff --git a/bootstat/README.md b/bootstat/README.md
new file mode 100644
index 0000000..76ea6c1
--- /dev/null
+++ b/bootstat/README.md
@@ -0,0 +1,50 @@
+# bootstat #
+
+The bootstat command records boot events (e.g., `firmware_loaded`,
+`boot_complete`) and the relative time at which these events occurred. The
+command also aggregates boot event metrics locally and logs the metrics for
+analysis.
+
+ Usage: bootstat [options]
+ options include:
+ -h, --help Show this help
+ -l, --log Log all metrics to logstorage
+ -p, --print Dump the boot event records to the console
+ -r, --record Record the timestamp of a named boot event
+ --record_boot_reason Record the reason why the device booted
+ --record_time_since_factory_reset Record the time since the device was reset
+
+## Relative time ##
+
+The timestamp recorded by bootstat is the uptime of the system, i.e., the
+number of seconds since the system booted.
+
+## Recording boot events ##
+
+To record the relative time of an event during the boot phase, call `bootstat`
+with the `-r` option and the name of the boot event.
+
+ $ bootstat -r boot_complete
+
+The relative time at which the command runs is recorded along with the name of
+the boot event to be persisted.
+
+## Logging boot events ##
+
+To log the persisted boot events, call `bootstat` with the `-l` option.
+
+ $ bootstat -l
+
+bootstat logs all boot events recorded using the `-r` option to the EventLog
+using the Tron histogram. These logs may be uploaded by interested parties
+for aggregation and analysis of boot time across different devices and
+versions.
+
+## Printing boot events ##
+
+To print the set of persisted boot events, call `bootstat` with the `-p` option.
+
+ $ bootstat -p
+ Boot events:
+ ------------
+ boot_complete 71
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
new file mode 100644
index 0000000..f902af3
--- /dev/null
+++ b/bootstat/boot_event_record_store.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "boot_event_record_store.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <utime.h>
+#include <cstdlib>
+#include <string>
+#include <utility>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include "histogram_logger.h"
+#include "uptime_parser.h"
+
+namespace {
+
+const char BOOTSTAT_DATA_DIR[] = "/data/misc/bootstat/";
+
+// Given a boot even record file at |path|, extracts the event's relative time
+// from the record into |uptime|.
+bool ParseRecordEventTime(const std::string& path, int32_t* uptime) {
+ DCHECK_NE(static_cast<int32_t*>(nullptr), uptime);
+
+ struct stat file_stat;
+ if (stat(path.c_str(), &file_stat) == -1) {
+ PLOG(ERROR) << "Failed to read " << path;
+ return false;
+ }
+
+ *uptime = file_stat.st_mtime;
+
+ return true;
+}
+
+} // namespace
+
+BootEventRecordStore::BootEventRecordStore() {
+ SetStorePath(BOOTSTAT_DATA_DIR);
+}
+
+void BootEventRecordStore::AddBootEvent(const std::string& event) {
+ AddBootEventWithValue(event, bootstat::ParseUptime());
+}
+
+// The implementation of AddBootEventValue makes use of the mtime file
+// attribute to store the value associated with a boot event in order to
+// optimize on-disk size requirements and small-file thrashing.
+void BootEventRecordStore::AddBootEventWithValue(
+ const std::string& event, int32_t value) {
+ std::string record_path = GetBootEventPath(event);
+ int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR);
+ if (record_fd == -1) {
+ PLOG(ERROR) << "Failed to create " << record_path;
+ return;
+ }
+
+ // Fill out the stat structure for |record_path| in order to get the atime to
+ // set in the utime() call.
+ struct stat file_stat;
+ if (stat(record_path.c_str(), &file_stat) == -1) {
+ PLOG(ERROR) << "Failed to read " << record_path;
+ close(record_fd);
+ return;
+ }
+
+ // Set the |modtime| of the file to store the value of the boot event while
+ // preserving the |actime| (as read by stat).
+ struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value};
+ if (utime(record_path.c_str(), ×) == -1) {
+ PLOG(ERROR) << "Failed to set mtime for " << record_path;
+ close(record_fd);
+ return;
+ }
+
+ close(record_fd);
+}
+
+bool BootEventRecordStore::GetBootEvent(
+ const std::string& event, BootEventRecord* record) const {
+ CHECK_NE(static_cast<BootEventRecord*>(nullptr), record);
+ CHECK(!event.empty());
+
+ const std::string record_path = GetBootEventPath(event);
+ int32_t uptime;
+ if (!ParseRecordEventTime(record_path, &uptime)) {
+ LOG(ERROR) << "Failed to parse boot time record: " << record_path;
+ return false;
+ }
+
+ *record = std::make_pair(event, uptime);
+ return true;
+}
+
+std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::
+ GetAllBootEvents() const {
+ std::vector<BootEventRecord> events;
+
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir);
+
+ // This case could happen due to external manipulation of the filesystem,
+ // so crash out if the record store doesn't exist.
+ CHECK_NE(static_cast<DIR*>(nullptr), dir.get());
+
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ // Only parse regular files.
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+
+ const std::string event = entry->d_name;
+ BootEventRecord record;
+ if (!GetBootEvent(event, &record)) {
+ LOG(ERROR) << "Failed to parse boot time event: " << event;
+ continue;
+ }
+
+ events.push_back(record);
+ }
+
+ return events;
+}
+
+void BootEventRecordStore::SetStorePath(const std::string& path) {
+ DCHECK_EQ('/', path.back());
+ store_path_ = path;
+}
+
+std::string BootEventRecordStore::GetBootEventPath(
+ const std::string& event) const {
+ DCHECK_EQ('/', store_path_.back());
+ return store_path_ + event;
+}
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
new file mode 100644
index 0000000..a2b8318
--- /dev/null
+++ b/bootstat/boot_event_record_store.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BOOT_EVENT_RECORD_STORE_H_
+#define BOOT_EVENT_RECORD_STORE_H_
+
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+#include <android-base/macros.h>
+#include <gtest/gtest_prod.h>
+
+// BootEventRecordStore manages the persistence of boot events to the record
+// store and the retrieval of all boot event records from the store.
+class BootEventRecordStore {
+ public:
+ // A BootEventRecord consists of the event name and the timestamp the event
+ // occurred.
+ typedef std::pair<std::string, int32_t> BootEventRecord;
+
+ BootEventRecordStore();
+
+ // Persists the boot |event| in the record store.
+ void AddBootEvent(const std::string& event);
+
+ // Persists the boot |event| with the associated |value| in the record store.
+ void AddBootEventWithValue(const std::string& event, int32_t value);
+
+ // Queries the named boot |event|. |record| must be non-null. |record|
+ // contains the boot event data on success. Returns true iff the query is
+ // successful.
+ bool GetBootEvent(const std::string& event, BootEventRecord* record) const;
+
+ // Returns a list of all of the boot events persisted in the record store.
+ std::vector<BootEventRecord> GetAllBootEvents() const;
+
+ private:
+ // The tests call SetStorePath to override the default store location with a
+ // more test-friendly path.
+ FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent);
+ FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
+ FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
+ FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent);
+ FRIEND_TEST(BootEventRecordStoreTest, GetBootEventNoFileContent);
+
+ // Sets the filesystem path of the record store.
+ void SetStorePath(const std::string& path);
+
+ // Constructs the full path of the given boot |event|.
+ std::string GetBootEventPath(const std::string& event) const;
+
+ // The filesystem path of the record store.
+ std::string store_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(BootEventRecordStore);
+};
+
+#endif // BOOT_EVENT_RECORD_STORE_H_
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
new file mode 100644
index 0000000..90f6513
--- /dev/null
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "boot_event_record_store.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <cstdint>
+#include <cstdlib>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/test_utils.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include "uptime_parser.h"
+
+using testing::UnorderedElementsAreArray;
+
+namespace {
+
+// Creates a fake boot event record file at |record_path| containing the boot
+// record |value|. This method is necessary as truncating a
+// BootEventRecordStore-created file would modify the mtime, which would alter
+// the value of the record.
+bool CreateEmptyBootEventRecord(const std::string& record_path, int32_t value) {
+ android::base::unique_fd record_fd(creat(record_path.c_str(), S_IRUSR | S_IWUSR));
+ if (record_fd == -1) {
+ return false;
+ }
+
+ // Set the |mtime| of the file to store the value of the boot event while
+ // preserving the |atime|.
+ struct timeval atime = {/* tv_sec */ 0, /* tv_usec */ 0};
+ struct timeval mtime = {/* tv_sec */ value, /* tv_usec */ 0};
+ const struct timeval times[] = {atime, mtime};
+ if (utimes(record_path.c_str(), times) != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+// Returns true if the time difference between |a| and |b| is no larger
+// than 10 seconds. This allow for a relatively large fuzz when comparing
+// two timestamps taken back-to-back.
+bool FuzzUptimeEquals(int32_t a, int32_t b) {
+ const int32_t FUZZ_SECONDS = 10;
+ return (abs(a - b) <= FUZZ_SECONDS);
+}
+
+// Recursively deletes the directory at |path|.
+void DeleteDirectory(const std::string& path) {
+ typedef std::unique_ptr<DIR, decltype(&closedir)> ScopedDIR;
+ ScopedDIR dir(opendir(path.c_str()), closedir);
+ ASSERT_NE(nullptr, dir.get());
+
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ const std::string entry_name(entry->d_name);
+ if (entry_name == "." || entry_name == "..") {
+ continue;
+ }
+
+ const std::string entry_path = path + "/" + entry_name;
+ if (entry->d_type == DT_DIR) {
+ DeleteDirectory(entry_path);
+ } else {
+ unlink(entry_path.c_str());
+ }
+ }
+
+ rmdir(path.c_str());
+}
+
+class BootEventRecordStoreTest : public ::testing::Test {
+ public:
+ BootEventRecordStoreTest() {
+ store_path_ = std::string(store_dir_.path) + "/";
+ }
+
+ const std::string& GetStorePathForTesting() const {
+ return store_path_;
+ }
+
+ private:
+ void TearDown() {
+ // This removes the record store temporary directory even though
+ // TemporaryDir should already take care of it, but this method cleans up
+ // the test files added to the directory which prevent TemporaryDir from
+ // being able to remove the directory.
+ DeleteDirectory(store_path_);
+ }
+
+ // A scoped temporary directory. Using this abstraction provides creation of
+ // the directory and the path to the directory, which is stored in
+ // |store_path_|.
+ TemporaryDir store_dir_;
+
+ // The path to the temporary directory used by the BootEventRecordStore to
+ // persist records. The directory is created and destroyed for each test.
+ std::string store_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(BootEventRecordStoreTest);
+};
+
+} // namespace
+
+TEST_F(BootEventRecordStoreTest, AddSingleBootEvent) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ time_t uptime = bootstat::ParseUptime();
+ ASSERT_NE(-1, uptime);
+
+ store.AddBootEvent("cenozoic");
+
+ auto events = store.GetAllBootEvents();
+ ASSERT_EQ(1U, events.size());
+ EXPECT_EQ("cenozoic", events[0].first);
+ EXPECT_TRUE(FuzzUptimeEquals(uptime, events[0].second));
+}
+
+TEST_F(BootEventRecordStoreTest, AddMultipleBootEvents) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ time_t uptime = bootstat::ParseUptime();
+ ASSERT_NE(-1, uptime);
+
+ store.AddBootEvent("cretaceous");
+ store.AddBootEvent("jurassic");
+ store.AddBootEvent("triassic");
+
+ const std::string EXPECTED_NAMES[] = {
+ "cretaceous",
+ "jurassic",
+ "triassic",
+ };
+
+ auto events = store.GetAllBootEvents();
+ ASSERT_EQ(3U, events.size());
+
+ std::vector<std::string> names;
+ std::vector<int32_t> timestamps;
+ for (auto i = events.begin(); i != events.end(); ++i) {
+ names.push_back(i->first);
+ timestamps.push_back(i->second);
+ }
+
+ EXPECT_THAT(names, UnorderedElementsAreArray(EXPECTED_NAMES));
+
+ for (auto i = timestamps.cbegin(); i != timestamps.cend(); ++i) {
+ EXPECT_TRUE(FuzzUptimeEquals(uptime, *i));
+ }
+}
+
+TEST_F(BootEventRecordStoreTest, AddBootEventWithValue) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ store.AddBootEventWithValue("permian", 42);
+
+ auto events = store.GetAllBootEvents();
+ ASSERT_EQ(1U, events.size());
+ EXPECT_EQ("permian", events[0].first);
+ EXPECT_EQ(42, events[0].second);
+}
+
+TEST_F(BootEventRecordStoreTest, GetBootEvent) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ // Event does not exist.
+ BootEventRecordStore::BootEventRecord record;
+ bool result = store.GetBootEvent("nonexistent", &record);
+ EXPECT_EQ(false, result);
+
+ // Empty path.
+ EXPECT_DEATH(store.GetBootEvent(std::string(), &record), std::string());
+
+ // Success case.
+ store.AddBootEventWithValue("carboniferous", 314);
+ result = store.GetBootEvent("carboniferous", &record);
+ EXPECT_EQ(true, result);
+ EXPECT_EQ("carboniferous", record.first);
+ EXPECT_EQ(314, record.second);
+
+ // Null |record|.
+ EXPECT_DEATH(store.GetBootEvent("carboniferous", nullptr), std::string());
+}
+
+// Tests that the BootEventRecordStore is capable of handling an older record
+// protocol which does not contain file contents.
+TEST_F(BootEventRecordStoreTest, GetBootEventNoFileContent) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ EXPECT_TRUE(CreateEmptyBootEventRecord(store.GetBootEventPath("devonian"), 2718));
+
+ BootEventRecordStore::BootEventRecord record;
+ bool result = store.GetBootEvent("devonian", &record);
+ EXPECT_EQ(true, result);
+ EXPECT_EQ("devonian", record.first);
+ EXPECT_EQ(2718, record.second);
+}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
new file mode 100644
index 0000000..a626704
--- /dev/null
+++ b/bootstat/bootstat.cpp
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// The bootstat command provides options to persist boot events with the current
+// timestamp, dump the persisted events, and log all events to EventLog to be
+// uploaded to Android log storage via Tron.
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include <cmath>
+#include <cstddef>
+#include <cstdio>
+#include <ctime>
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android/log.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <cutils/properties.h>
+
+#include "boot_event_record_store.h"
+#include "histogram_logger.h"
+#include "uptime_parser.h"
+
+namespace {
+
+// Scans the boot event record store for record files and logs each boot event
+// via EventLog.
+void LogBootEvents() {
+ BootEventRecordStore boot_event_store;
+
+ auto events = boot_event_store.GetAllBootEvents();
+ for (auto i = events.cbegin(); i != events.cend(); ++i) {
+ bootstat::LogHistogram(i->first, i->second);
+ }
+}
+
+// Records the named boot |event| to the record store. If |value| is non-empty
+// and is a proper string representation of an integer value, the converted
+// integer value is associated with the boot event.
+void RecordBootEventFromCommandLine(
+ const std::string& event, const std::string& value_str) {
+ BootEventRecordStore boot_event_store;
+ if (!value_str.empty()) {
+ int32_t value = 0;
+ if (android::base::ParseInt(value_str, &value)) {
+ boot_event_store.AddBootEventWithValue(event, value);
+ }
+ } else {
+ boot_event_store.AddBootEvent(event);
+ }
+}
+
+void PrintBootEvents() {
+ printf("Boot events:\n");
+ printf("------------\n");
+
+ BootEventRecordStore boot_event_store;
+ auto events = boot_event_store.GetAllBootEvents();
+ for (auto i = events.cbegin(); i != events.cend(); ++i) {
+ printf("%s\t%d\n", i->first.c_str(), i->second);
+ }
+}
+
+void ShowHelp(const char *cmd) {
+ fprintf(stderr, "Usage: %s [options]\n", cmd);
+ fprintf(stderr,
+ "options include:\n"
+ " -h, --help Show this help\n"
+ " -l, --log Log all metrics to logstorage\n"
+ " -p, --print Dump the boot event records to the console\n"
+ " -r, --record Record the timestamp of a named boot event\n"
+ " --value Optional value to associate with the boot event\n"
+ " --record_boot_reason Record the reason why the device booted\n"
+ " --record_time_since_factory_reset Record the time since the device was reset\n");
+}
+
+// Constructs a readable, printable string from the givencommand line
+// arguments.
+std::string GetCommandLine(int argc, char **argv) {
+ std::string cmd;
+ for (int i = 0; i < argc; ++i) {
+ cmd += argv[i];
+ cmd += " ";
+ }
+
+ return cmd;
+}
+
+// Convenience wrapper over the property API that returns an
+// std::string.
+std::string GetProperty(const char* key) {
+ std::vector<char> temp(PROPERTY_VALUE_MAX);
+ const int len = property_get(key, &temp[0], nullptr);
+ if (len < 0) {
+ return "";
+ }
+ return std::string(&temp[0], len);
+}
+
+constexpr int32_t kUnknownBootReason = 1;
+
+// A mapping from boot reason string, as read from the ro.boot.bootreason
+// system property, to a unique integer ID. Viewers of log data dashboards for
+// the boot_reason metric may refer to this mapping to discern the histogram
+// values.
+const std::map<std::string, int32_t> kBootReasonMap = {
+ {"unknown", kUnknownBootReason},
+ {"normal", 2},
+ {"recovery", 3},
+ {"reboot", 4},
+ {"PowerKey", 5},
+ {"hard_reset", 6},
+ {"kernel_panic", 7},
+ {"rpm_err", 8},
+ {"hw_reset", 9},
+ {"tz_err", 10},
+ {"adsp_err", 11},
+ {"modem_err", 12},
+ {"mba_err", 13},
+ {"Watchdog", 14},
+ {"Panic", 15},
+ {"power_key", 16},
+ {"power_on", 17},
+ {"Reboot", 18},
+ {"rtc", 19},
+ {"edl", 20},
+ {"oem_pon1", 21},
+ {"oem_powerkey", 22},
+ {"oem_unknown_reset", 23},
+ {"srto: HWWDT reset SC", 24},
+ {"srto: HWWDT reset platform", 25},
+ {"srto: bootloader", 26},
+ {"srto: kernel panic", 27},
+ {"srto: kernel watchdog reset", 28},
+ {"srto: normal", 29},
+ {"srto: reboot", 30},
+ {"srto: reboot-bootloader", 31},
+ {"srto: security watchdog reset", 32},
+ {"srto: wakesrc", 33},
+ {"srto: watchdog", 34},
+ {"srto:1-1", 35},
+ {"srto:omap_hsmm", 36},
+ {"srto:phy0", 37},
+ {"srto:rtc0", 38},
+ {"srto:touchpad", 39},
+ {"watchdog", 40},
+ {"watchdogr", 41},
+ {"wdog_bark", 42},
+ {"wdog_bite", 43},
+ {"wdog_reset", 44},
+};
+
+// Converts a string value representing the reason the system booted to an
+// integer representation. This is necessary for logging the boot_reason metric
+// via Tron, which does not accept non-integer buckets in histograms.
+int32_t BootReasonStrToEnum(const std::string& boot_reason) {
+ auto mapping = kBootReasonMap.find(boot_reason);
+ if (mapping != kBootReasonMap.end()) {
+ return mapping->second;
+ }
+
+ LOG(INFO) << "Unknown boot reason: " << boot_reason;
+ return kUnknownBootReason;
+}
+
+// Returns the appropriate metric key prefix for the boot_complete metric such
+// that boot metrics after a system update are labeled as ota_boot_complete;
+// otherwise, they are labeled as boot_complete. This method encapsulates the
+// bookkeeping required to track when a system update has occurred by storing
+// the UTC timestamp of the system build date and comparing against the current
+// system build date.
+std::string CalculateBootCompletePrefix() {
+ static const std::string kBuildDateKey = "build_date";
+ std::string boot_complete_prefix = "boot_complete";
+
+ std::string build_date_str = GetProperty("ro.build.date.utc");
+ int32_t build_date;
+ if (!android::base::ParseInt(build_date_str, &build_date)) {
+ return std::string();
+ }
+
+ BootEventRecordStore boot_event_store;
+ BootEventRecordStore::BootEventRecord record;
+ if (!boot_event_store.GetBootEvent(kBuildDateKey, &record) ||
+ build_date != record.second) {
+ boot_complete_prefix = "ota_" + boot_complete_prefix;
+ boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+ }
+
+ return boot_complete_prefix;
+}
+
+// Records the value of a given ro.boottime.init property in milliseconds.
+void RecordInitBootTimeProp(
+ BootEventRecordStore* boot_event_store, const char* property) {
+ std::string value = GetProperty(property);
+
+ int32_t time_in_ms;
+ if (android::base::ParseInt(value, &time_in_ms)) {
+ boot_event_store->AddBootEventWithValue(property, time_in_ms);
+ }
+}
+
+// Records several metrics related to the time it takes to boot the device,
+// including disambiguating boot time on encrypted or non-encrypted devices.
+void RecordBootComplete() {
+ BootEventRecordStore boot_event_store;
+ BootEventRecordStore::BootEventRecord record;
+
+ time_t uptime = bootstat::ParseUptime();
+ time_t current_time_utc = time(nullptr);
+
+ if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
+ time_t last_boot_time_utc = record.second;
+ time_t time_since_last_boot = difftime(current_time_utc,
+ last_boot_time_utc);
+ boot_event_store.AddBootEventWithValue("time_since_last_boot",
+ time_since_last_boot);
+ }
+
+ boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc);
+
+ // The boot_complete metric has two variants: boot_complete and
+ // ota_boot_complete. The latter signifies that the device is booting after
+ // a system update.
+ std::string boot_complete_prefix = CalculateBootCompletePrefix();
+ if (boot_complete_prefix.empty()) {
+ // The system is hosed because the build date property could not be read.
+ return;
+ }
+
+ // post_decrypt_time_elapsed is only logged on encrypted devices.
+ if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) {
+ // Log the amount of time elapsed until the device is decrypted, which
+ // includes the variable amount of time the user takes to enter the
+ // decryption password.
+ boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime);
+
+ // Subtract the decryption time to normalize the boot cycle timing.
+ time_t boot_complete = uptime - record.second;
+ boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
+ boot_complete);
+
+
+ } else {
+ boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
+ uptime);
+ }
+
+ // Record the total time from device startup to boot complete, regardless of
+ // encryption state.
+ boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime);
+
+ RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
+ RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
+ RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
+}
+
+// Records the boot_reason metric by querying the ro.boot.bootreason system
+// property.
+void RecordBootReason() {
+ int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
+ BootEventRecordStore boot_event_store;
+ boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
+}
+
+// Records two metrics related to the user resetting a device: the time at
+// which the device is reset, and the time since the user last reset the
+// device. The former is only set once per-factory reset.
+void RecordFactoryReset() {
+ BootEventRecordStore boot_event_store;
+ BootEventRecordStore::BootEventRecord record;
+
+ time_t current_time_utc = time(nullptr);
+
+ if (current_time_utc < 0) {
+ // UMA does not display negative values in buckets, so convert to positive.
+ bootstat::LogHistogram(
+ "factory_reset_current_time_failure", std::abs(current_time_utc));
+
+ // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+ // is losing records somehow.
+ boot_event_store.AddBootEventWithValue(
+ "factory_reset_current_time_failure", std::abs(current_time_utc));
+ return;
+ } else {
+ bootstat::LogHistogram("factory_reset_current_time", current_time_utc);
+
+ // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+ // is losing records somehow.
+ boot_event_store.AddBootEventWithValue(
+ "factory_reset_current_time", current_time_utc);
+ }
+
+ // The factory_reset boot event does not exist after the device is reset, so
+ // use this signal to mark the time of the factory reset.
+ if (!boot_event_store.GetBootEvent("factory_reset", &record)) {
+ boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc);
+
+ // Don't log the time_since_factory_reset until some time has elapsed.
+ // The data is not meaningful yet and skews the histogram buckets.
+ return;
+ }
+
+ // Calculate and record the difference in time between now and the
+ // factory_reset time.
+ time_t factory_reset_utc = record.second;
+ bootstat::LogHistogram("factory_reset_record_value", factory_reset_utc);
+
+ // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+ // is losing records somehow.
+ boot_event_store.AddBootEventWithValue(
+ "factory_reset_record_value", factory_reset_utc);
+
+ time_t time_since_factory_reset = difftime(current_time_utc,
+ factory_reset_utc);
+ boot_event_store.AddBootEventWithValue("time_since_factory_reset",
+ time_since_factory_reset);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ android::base::InitLogging(argv);
+
+ const std::string cmd_line = GetCommandLine(argc, argv);
+ LOG(INFO) << "Service started: " << cmd_line;
+
+ int option_index = 0;
+ static const char value_str[] = "value";
+ static const char boot_complete_str[] = "record_boot_complete";
+ static const char boot_reason_str[] = "record_boot_reason";
+ static const char factory_reset_str[] = "record_time_since_factory_reset";
+ static const struct option long_options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "log", no_argument, NULL, 'l' },
+ { "print", no_argument, NULL, 'p' },
+ { "record", required_argument, NULL, 'r' },
+ { value_str, required_argument, NULL, 0 },
+ { boot_complete_str, no_argument, NULL, 0 },
+ { boot_reason_str, no_argument, NULL, 0 },
+ { factory_reset_str, no_argument, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+ };
+
+ std::string boot_event;
+ std::string value;
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "hlpr:", long_options, &option_index)) != -1) {
+ switch (opt) {
+ // This case handles long options which have no single-character mapping.
+ case 0: {
+ const std::string option_name = long_options[option_index].name;
+ if (option_name == value_str) {
+ // |optarg| is an external variable set by getopt representing
+ // the option argument.
+ value = optarg;
+ } else if (option_name == boot_complete_str) {
+ RecordBootComplete();
+ } else if (option_name == boot_reason_str) {
+ RecordBootReason();
+ } else if (option_name == factory_reset_str) {
+ RecordFactoryReset();
+ } else {
+ LOG(ERROR) << "Invalid option: " << option_name;
+ }
+ break;
+ }
+
+ case 'h': {
+ ShowHelp(argv[0]);
+ break;
+ }
+
+ case 'l': {
+ LogBootEvents();
+ break;
+ }
+
+ case 'p': {
+ PrintBootEvents();
+ break;
+ }
+
+ case 'r': {
+ // |optarg| is an external variable set by getopt representing
+ // the option argument.
+ boot_event = optarg;
+ break;
+ }
+
+ default: {
+ DCHECK_EQ(opt, '?');
+
+ // |optopt| is an external variable set by getopt representing
+ // the value of the invalid option.
+ LOG(ERROR) << "Invalid option: " << optopt;
+ ShowHelp(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ if (!boot_event.empty()) {
+ RecordBootEventFromCommandLine(boot_event, value);
+ }
+
+ return 0;
+}
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
new file mode 100644
index 0000000..b072412
--- /dev/null
+++ b/bootstat/bootstat.rc
@@ -0,0 +1,44 @@
+# This file is the LOCAL_INIT_RC file for the bootstat command.
+
+on post-fs-data
+ mkdir /data/misc/bootstat 0700 root root
+
+# Record the time at which the user has successfully entered the pin to decrypt
+# the device, /data is decrypted, and the system is entering the main boot phase.
+#
+# post-fs-data: /data is writable
+# property:init.svc.bootanim=running: The boot animation is running
+on post-fs-data && property:init.svc.bootanim=running
+ exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+
+# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
+# This signaling is necessary to prevent logging boot metrics after a runtime
+# restart (e.g., adb shell stop && adb shell start). /proc/uptime is not reset
+# during a runtime restart, which leads to false boot time metrics being reported.
+#
+# The 'on boot' event occurs once per hard boot (device power on), which
+# switches the flag on. If the device performs a runtime restart, the flag is
+# switched off and cannot be switched on until the device hard boots again.
+
+# Enable bootstat logging on boot.
+on boot
+ setprop sys.logbootcomplete 1
+
+# Disable further bootstat logging on a runtime restart. A runtime restart is
+# signaled by the zygote stopping.
+on property:init.svc.zygote=stopping
+ setprop sys.logbootcomplete 0
+
+# Record boot complete metrics.
+on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
+ # Record boot_complete and related stats (decryption, etc).
+ exec - root root -- /system/bin/bootstat --record_boot_complete
+
+ # Record the boot reason.
+ exec - root root -- /system/bin/bootstat --record_boot_reason
+
+ # Record time since factory reset.
+ exec - root root -- /system/bin/bootstat --record_time_since_factory_reset
+
+ # Log all boot events.
+ exec - root root -- /system/bin/bootstat -l
diff --git a/bootstat/histogram_logger.cpp b/bootstat/histogram_logger.cpp
new file mode 100644
index 0000000..73f3295
--- /dev/null
+++ b/bootstat/histogram_logger.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "histogram_logger.h"
+
+#include <cstdlib>
+
+#include <android-base/logging.h>
+#include <log/log_event_list.h>
+
+namespace bootstat {
+
+void LogHistogram(const std::string& event, int32_t data) {
+ LOG(INFO) << "Logging histogram: " << event << " " << data;
+ android_log_event_list log(HISTOGRAM_LOG_TAG);
+ log << event << data << LOG_ID_EVENTS;
+}
+
+} // namespace bootstat
diff --git a/bootstat/histogram_logger.h b/bootstat/histogram_logger.h
new file mode 100644
index 0000000..60c7776
--- /dev/null
+++ b/bootstat/histogram_logger.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+#include <string>
+
+namespace bootstat {
+
+// Builds an EventLog buffer named |event| containing |data| and writes
+// the log into the Tron histogram logs.
+void LogHistogram(const std::string& event, int32_t data);
+
+} // namespace bootstat
\ No newline at end of file
diff --git a/bootstat/testrunner.cpp b/bootstat/testrunner.cpp
new file mode 100644
index 0000000..79b61d1
--- /dev/null
+++ b/bootstat/testrunner.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ android::base::InitLogging(argv, android::base::StderrLogger);
+ return RUN_ALL_TESTS();
+}
diff --git a/bootstat/uptime_parser.cpp b/bootstat/uptime_parser.cpp
new file mode 100644
index 0000000..7c2034c
--- /dev/null
+++ b/bootstat/uptime_parser.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uptime_parser.h"
+
+#include <time.h>
+#include <cstdlib>
+#include <string>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace bootstat {
+
+time_t ParseUptime() {
+ std::string uptime_str;
+ if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
+ PLOG(ERROR) << "Failed to read /proc/uptime";
+ return -1;
+ }
+
+ // Cast intentionally rounds down.
+ return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
+}
+
+} // namespace bootstat
\ No newline at end of file
diff --git a/bootstat/uptime_parser.h b/bootstat/uptime_parser.h
new file mode 100644
index 0000000..756ae9b
--- /dev/null
+++ b/bootstat/uptime_parser.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UPTIME_PARSER_H_
+#define UPTIME_PARSER_H_
+
+#include <time.h>
+
+namespace bootstat {
+
+// Returns the number of seconds the system has been on since reboot.
+time_t ParseUptime();
+
+} // namespace bootstat
+
+#endif // UPTIME_PARSER_H_
\ No newline at end of file
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
index 7175749..b89c395 100644
--- a/cpio/mkbootfs.c
+++ b/cpio/mkbootfs.c
@@ -41,6 +41,7 @@
};
static struct fs_config_entry* canned_config = NULL;
+static char *target_out_path = NULL;
/* Each line in the canned file should be a path plus three ints (uid,
* gid, mode). */
@@ -50,6 +51,8 @@
#define CANNED_LINE_LENGTH (1024)
#endif
+#define TRAILER "TRAILER!!!"
+
static int verbose = 0;
static int total_size = 0;
@@ -79,7 +82,8 @@
} else {
// Use the compiled-in fs_config() function.
unsigned st_mode = s->st_mode;
- fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &st_mode, &capabilities);
+ int is_dir = S_ISDIR(s->st_mode) || strcmp(path, TRAILER) == 0;
+ fs_config(path, is_dir, target_out_path, &s->st_uid, &s->st_gid, &st_mode, &capabilities);
s->st_mode = (typeof(s->st_mode)) st_mode;
}
}
@@ -138,7 +142,7 @@
{
struct stat s;
memset(&s, 0, sizeof(s));
- _eject(&s, "TRAILER!!!", 10, 0, 0);
+ _eject(&s, TRAILER, 10, 0, 0);
while(total_size & 0xff) {
total_size++;
@@ -328,6 +332,12 @@
argc--;
argv++;
+ if (argc > 1 && strcmp(argv[0], "-d") == 0) {
+ target_out_path = argv[1];
+ argc -= 2;
+ argv += 2;
+ }
+
if (argc > 1 && strcmp(argv[0], "-f") == 0) {
read_canned_config(argv[1]);
argc -= 2;
diff --git a/crash_reporter/.project_alias b/crash_reporter/.project_alias
deleted file mode 100644
index 0bc3798..0000000
--- a/crash_reporter/.project_alias
+++ /dev/null
@@ -1 +0,0 @@
-crash
diff --git a/crash_reporter/99-crash-reporter.rules b/crash_reporter/99-crash-reporter.rules
deleted file mode 100644
index aea5b1c..0000000
--- a/crash_reporter/99-crash-reporter.rules
+++ /dev/null
@@ -1,6 +0,0 @@
-ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card0", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=KERNEL=card0:SUBSYSTEM=drm:ACTION=change"
-# For detecting cypress trackpad issue. Passing into crash_reporter SUBSYSTEM=i2c-cyapa since crash_reporter does not handle DRIVER string.
-ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="cyapa", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-cyapa:ACTION=change"
-# For detecting Atmel trackpad/touchscreen issue. Passing into crash_reporter SUBSYSTEM=i2c-atmel_mxt_ts since crash_reporter does not handle DRIVER string.
-ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="atmel_mxt_ts", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-atmel_mxt_ts:ACTION=change"
-ACTION=="add", SUBSYSTEM=="devcoredump", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=devcoredump:ACTION=add:KERNEL_NUMBER=%n"
diff --git a/crash_reporter/NOTICE b/crash_reporter/NOTICE
deleted file mode 100644
index b9e779f..0000000
--- a/crash_reporter/NOTICE
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" 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.
diff --git a/crash_reporter/OWNERS b/crash_reporter/OWNERS
deleted file mode 100644
index 96ea5b2..0000000
--- a/crash_reporter/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-set noparent
-vapier@chromium.org
diff --git a/crash_reporter/TEST_WARNING b/crash_reporter/TEST_WARNING
deleted file mode 100644
index 64ad2e9..0000000
--- a/crash_reporter/TEST_WARNING
+++ /dev/null
@@ -1,31 +0,0 @@
-Apr 31 25:25:25 localhost kernel: [117959.226729] [<ffffffff810e16bf>] do_vfs_ioctl+0x469/0x4b3
-Apr 31 25:25:25 localhost kernel: [117959.226738] [<ffffffff810d3117>] ? fsnotify_access+0x58/0x60
-Apr 31 25:25:25 localhost kernel: [117959.226747] [<ffffffff810d3791>] ? vfs_read+0xad/0xd7
-Apr 31 25:25:25 localhost kernel: [117959.226756] [<ffffffff810e175f>] sys_ioctl+0x56/0x7b
-Apr 31 25:25:25 localhost kernel: [117959.226765] [<ffffffff810d37fe>] ? sys_read+0x43/0x73
-Apr 31 25:25:25 localhost kernel: [117959.226774] [<ffffffff8146b7d2>] system_call_fastpath+0x16/0x1b
-Apr 31 25:25:25 localhost kernel: [117959.226782] ---[ end trace f16822cad7406cec ]---
-Apr 31 25:25:25 localhost kernel: [117959.231085] ------------[ cut here ]------------
-Apr 31 25:25:25 localhost kernel: [117959.231100] WARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9()
-Apr 31 25:25:25 localhost kernel: [117959.231113] Hardware name: Link
-Apr 31 25:25:25 localhost kernel: [117959.231117] eDP powered off while attempting aux channel communication.
-Apr 31 25:25:25 localhost kernel: [117959.231240] Pid: 10508, comm: X Tainted: G WC 3.4.0 #1
-Apr 31 25:25:25 localhost kernel: [117959.231247] Call Trace:
-Apr 31 25:25:25 localhost kernel: [117959.231393] [<ffffffff810d3117>] ? fsnotify_access+0x58/0x60
-Apr 31 25:25:25 localhost kernel: [117959.231402] [<ffffffff810d3791>] ? vfs_read+0xad/0xd7
-Apr 31 25:25:25 localhost kernel: [117959.231411] [<ffffffff810e175f>] sys_ioctl+0x56/0x7b
-Apr 31 25:25:25 localhost kernel: [117959.231420] [<ffffffff810d37fe>] ? sys_read+0x43/0x73
-Apr 31 25:25:25 localhost kernel: [117959.231431] [<ffffffff8146b7d2>] system_call_fastpath+0x16/0x1b
-Apr 31 25:25:25 localhost kernel: [117959.231439] ---[ end trace f16822cad7406ced ]---
-Apr 31 25:25:25 localhost kernel: [117959.231450] ------------[ cut here ]------------
-Apr 31 25:25:25 localhost kernel: [117959.231458] BARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9()
-Apr 31 25:25:25 localhost kernel: [117959.231458] ("BARNING" above is intentional)
-Apr 31 25:25:25 localhost kernel: [117959.231471] Hardware name: Link
-Apr 31 25:25:25 localhost kernel: [117959.231475] eDP powered off while attempting aux channel communication.
-Apr 31 25:25:25 localhost kernel: [117959.231482] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat rfcomm i2c_dev ath9k_btcoex snd_hda_codec_hdmi snd_hda_codec_ca0132 mac80211 snd_hda_intel ath9k_common_btcoex snd_hda_codec ath9k_hw_btcoex aesni_intel cryptd snd_hwdep ath snd_pcm aes_x86_64 isl29018(C) memconsole snd_timer snd_page_alloc industrialio(C) cfg80211 rtc_cmos nm10_gpio zram(C) zsmalloc(C) lzo_decompress lzo_compress fuse nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables xt_mark option usb_wwan cdc_ether usbnet ath3k btusb bluetooth uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops joydev
-Apr 31 25:25:25 localhost kernel: [117959.231588] Pid: 10508, comm: X Tainted: G WC 3.4.0 #1
-Apr 31 25:25:25 localhost kernel: [117959.231595] Call Trace:
-Apr 31 25:25:25 localhost kernel: [117959.231601] [<ffffffff8102a931>] warn_slowpath_common+0x83/0x9c
-Apr 31 25:25:25 localhost kernel: [117959.231610] [<ffffffff8102a9ed>] warn_slowpath_fmt+0x46/0x48
-Apr 31 25:25:25 localhost kernel: [117959.231620] [<ffffffff812af495>] intel_dp_check_edp+0x6b/0xb9
-Apr 31 25:25:25 localhost kernel: [117959.231629] [<ffffffff8102a9ed>] ? warn_slowpath_fmt+
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
deleted file mode 100644
index 69fe939..0000000
--- a/crash_reporter/crash_collector.cc
+++ /dev/null
@@ -1,436 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "crash_collector.h"
-
-#include <dirent.h>
-#include <fcntl.h> // For file creation modes.
-#include <inttypes.h>
-#include <linux/limits.h> // PATH_MAX
-#include <pwd.h> // For struct passwd.
-#include <sys/types.h> // for mode_t.
-#include <sys/wait.h> // For waitpid.
-#include <unistd.h> // For execv and fork.
-
-#include <set>
-#include <utility>
-#include <vector>
-
-#include <base/files/file_util.h>
-#include <base/logging.h>
-#include <base/posix/eintr_wrapper.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <chromeos/key_value_store.h>
-#include <chromeos/process.h>
-
-namespace {
-
-const char kCollectChromeFile[] =
- "/mnt/stateful_partition/etc/collect_chrome_crashes";
-const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress";
-const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
-const char kDefaultUserName[] = "chronos";
-const char kLeaveCoreFile[] = "/root/.leave_core";
-const char kLsbRelease[] = "/etc/lsb-release";
-const char kShellPath[] = "/bin/sh";
-const char kSystemCrashPath[] = "/var/spool/crash";
-const char kUploadVarPrefix[] = "upload_var_";
-const char kUploadFilePrefix[] = "upload_file_";
-
-// Key of the lsb-release entry containing the OS version.
-const char kLsbVersionKey[] = "CHROMEOS_RELEASE_VERSION";
-
-// Normally this path is not used. Unfortunately, there are a few edge cases
-// where we need this. Any process that runs as kDefaultUserName that crashes
-// is consider a "user crash". That includes the initial Chrome browser that
-// runs the login screen. If that blows up, there is no logged in user yet,
-// so there is no per-user dir for us to stash things in. Instead we fallback
-// to this path as it is at least encrypted on a per-system basis.
-//
-// This also comes up when running autotests. The GUI is sitting at the login
-// screen while tests are sshing in, changing users, and triggering crashes as
-// the user (purposefully).
-const char kFallbackUserCrashPath[] = "/home/chronos/crash";
-
-// Directory mode of the user crash spool directory.
-const mode_t kUserCrashPathMode = 0755;
-
-// Directory mode of the system crash spool directory.
-const mode_t kSystemCrashPathMode = 01755;
-
-const uid_t kRootOwner = 0;
-const uid_t kRootGroup = 0;
-
-} // namespace
-
-// Maximum crash reports per crash spool directory. Note that this is
-// a separate maximum from the maximum rate at which we upload these
-// diagnostics. The higher this rate is, the more space we allow for
-// core files, minidumps, and kcrash logs, and equivalently the more
-// processor and I/O bandwidth we dedicate to handling these crashes when
-// many occur at once. Also note that if core files are configured to
-// be left on the file system, we stop adding crashes when either the
-// number of core files or minidumps reaches this number.
-const int CrashCollector::kMaxCrashDirectorySize = 32;
-
-using base::FilePath;
-using base::StringPrintf;
-
-CrashCollector::CrashCollector()
- : lsb_release_(kLsbRelease),
- log_config_path_(kDefaultLogConfig) {
-}
-
-CrashCollector::~CrashCollector() {
-}
-
-void CrashCollector::Initialize(
- CrashCollector::CountCrashFunction count_crash_function,
- CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
- CHECK(count_crash_function);
- CHECK(is_feedback_allowed_function);
-
- count_crash_function_ = count_crash_function;
- is_feedback_allowed_function_ = is_feedback_allowed_function;
-}
-
-int CrashCollector::WriteNewFile(const FilePath &filename,
- const char *data,
- int size) {
- int fd = HANDLE_EINTR(open(filename.value().c_str(),
- O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
- if (fd < 0) {
- return -1;
- }
-
- int rv = base::WriteFileDescriptor(fd, data, size) ? size : -1;
- IGNORE_EINTR(close(fd));
- return rv;
-}
-
-std::string CrashCollector::Sanitize(const std::string &name) {
- // Make sure the sanitized name does not include any periods.
- // The logic in crash_sender relies on this.
- std::string result = name;
- for (size_t i = 0; i < name.size(); ++i) {
- if (!isalnum(result[i]) && result[i] != '_')
- result[i] = '_';
- }
- return result;
-}
-
-std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
- time_t timestamp,
- pid_t pid) {
- struct tm tm;
- localtime_r(×tamp, &tm);
- std::string sanitized_exec_name = Sanitize(exec_name);
- return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
- sanitized_exec_name.c_str(),
- tm.tm_year + 1900,
- tm.tm_mon + 1,
- tm.tm_mday,
- tm.tm_hour,
- tm.tm_min,
- tm.tm_sec,
- pid);
-}
-
-FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
- const std::string &basename,
- const std::string &extension) {
- return crash_directory.Append(StringPrintf("%s.%s",
- basename.c_str(),
- extension.c_str()));
-}
-
-FilePath CrashCollector::GetCrashDirectoryInfo(
- uid_t process_euid,
- uid_t default_user_id,
- gid_t default_user_group,
- mode_t *mode,
- uid_t *directory_owner,
- gid_t *directory_group) {
- if (process_euid == default_user_id) {
- *mode = kUserCrashPathMode;
- *directory_owner = default_user_id;
- *directory_group = default_user_group;
- return FilePath(kFallbackUserCrashPath);
- } else {
- *mode = kSystemCrashPathMode;
- *directory_owner = kRootOwner;
- *directory_group = kRootGroup;
- return FilePath(kSystemCrashPath);
- }
-}
-
-bool CrashCollector::GetUserInfoFromName(const std::string &name,
- uid_t *uid,
- gid_t *gid) {
- char storage[256];
- struct passwd passwd_storage;
- struct passwd *passwd_result = nullptr;
-
- if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
- &passwd_result) != 0 || passwd_result == nullptr) {
- LOG(ERROR) << "Cannot find user named " << name;
- return false;
- }
-
- *uid = passwd_result->pw_uid;
- *gid = passwd_result->pw_gid;
- return true;
-}
-
-bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
- FilePath *crash_directory,
- bool *out_of_capacity) {
- uid_t default_user_id;
- gid_t default_user_group;
-
- if (out_of_capacity) *out_of_capacity = false;
-
- // For testing.
- if (!forced_crash_directory_.empty()) {
- *crash_directory = forced_crash_directory_;
- return true;
- }
-
- if (!GetUserInfoFromName(kDefaultUserName,
- &default_user_id,
- &default_user_group)) {
- LOG(ERROR) << "Could not find default user info";
- return false;
- }
- mode_t directory_mode;
- uid_t directory_owner;
- gid_t directory_group;
- *crash_directory =
- GetCrashDirectoryInfo(euid,
- default_user_id,
- default_user_group,
- &directory_mode,
- &directory_owner,
- &directory_group);
-
- if (!base::PathExists(*crash_directory)) {
- // Create the spool directory with the appropriate mode (regardless of
- // umask) and ownership.
- mode_t old_mask = umask(0);
- if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
- chown(crash_directory->value().c_str(),
- directory_owner,
- directory_group) < 0) {
- LOG(ERROR) << "Unable to create appropriate crash directory";
- return false;
- }
- umask(old_mask);
- }
-
- if (!base::PathExists(*crash_directory)) {
- LOG(ERROR) << "Unable to create crash directory "
- << crash_directory->value().c_str();
- return false;
- }
-
- if (!CheckHasCapacity(*crash_directory)) {
- if (out_of_capacity) *out_of_capacity = true;
- return false;
- }
-
- return true;
-}
-
-FilePath CrashCollector::GetProcessPath(pid_t pid) {
- return FilePath(StringPrintf("/proc/%d", pid));
-}
-
-bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
- FilePath *target) {
- ssize_t max_size = 64;
- std::vector<char> buffer;
-
- while (true) {
- buffer.resize(max_size + 1);
- ssize_t size = readlink(symlink.value().c_str(), buffer.data(), max_size);
- if (size < 0) {
- int saved_errno = errno;
- LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
- << saved_errno;
- return false;
- }
-
- buffer[size] = 0;
- if (size == max_size) {
- max_size *= 2;
- if (max_size > PATH_MAX) {
- return false;
- }
- continue;
- }
- break;
- }
-
- *target = FilePath(buffer.data());
- return true;
-}
-
-bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
- std::string *base_name) {
- FilePath target;
- FilePath process_path = GetProcessPath(pid);
- FilePath exe_path = process_path.Append("exe");
- if (!GetSymlinkTarget(exe_path, &target)) {
- LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
- << " DirectoryExists: "
- << base::DirectoryExists(process_path);
- // Try to further diagnose exe readlink failure cause.
- struct stat buf;
- int stat_result = stat(exe_path.value().c_str(), &buf);
- int saved_errno = errno;
- if (stat_result < 0) {
- LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
- << " " << saved_errno;
- } else {
- LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
- << buf.st_mode;
- }
- return false;
- }
- *base_name = target.BaseName().value();
- return true;
-}
-
-// Return true if the given crash directory has not already reached
-// maximum capacity.
-bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
- DIR* dir = opendir(crash_directory.value().c_str());
- if (!dir) {
- return false;
- }
- struct dirent ent_buf;
- struct dirent* ent;
- bool full = false;
- std::set<std::string> basenames;
- while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
- if ((strcmp(ent->d_name, ".") == 0) ||
- (strcmp(ent->d_name, "..") == 0))
- continue;
-
- std::string filename(ent->d_name);
- size_t last_dot = filename.rfind(".");
- std::string basename;
- // If there is a valid looking extension, use the base part of the
- // name. If the only dot is the first byte (aka a dot file), treat
- // it as unique to avoid allowing a directory full of dot files
- // from accumulating.
- if (last_dot != std::string::npos && last_dot != 0)
- basename = filename.substr(0, last_dot);
- else
- basename = filename;
- basenames.insert(basename);
-
- if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
- LOG(WARNING) << "Crash directory " << crash_directory.value()
- << " already full with " << kMaxCrashDirectorySize
- << " pending reports";
- full = true;
- break;
- }
- }
- closedir(dir);
- return !full;
-}
-
-bool CrashCollector::GetLogContents(const FilePath &config_path,
- const std::string &exec_name,
- const FilePath &output_file) {
- chromeos::KeyValueStore store;
- if (!store.Load(config_path)) {
- LOG(INFO) << "Unable to read log configuration file "
- << config_path.value();
- return false;
- }
-
- std::string command;
- if (!store.GetString(exec_name, &command))
- return false;
-
- chromeos::ProcessImpl diag_process;
- diag_process.AddArg(kShellPath);
- diag_process.AddStringOption("-c", command);
- diag_process.RedirectOutput(output_file.value());
-
- const int result = diag_process.Run();
- if (result != 0) {
- LOG(INFO) << "Log command \"" << command << "\" exited with " << result;
- return false;
- }
- return true;
-}
-
-void CrashCollector::AddCrashMetaData(const std::string &key,
- const std::string &value) {
- extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
-}
-
-void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
- const std::string &path) {
- if (!path.empty())
- AddCrashMetaData(kUploadFilePrefix + key, path);
-}
-
-void CrashCollector::AddCrashMetaUploadData(const std::string &key,
- const std::string &value) {
- if (!value.empty())
- AddCrashMetaData(kUploadVarPrefix + key, value);
-}
-
-void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
- const std::string &exec_name,
- const std::string &payload_path) {
- chromeos::KeyValueStore store;
- if (!store.Load(FilePath(lsb_release_))) {
- LOG(ERROR) << "Problem parsing " << lsb_release_;
- // Even though there was some failure, take as much as we could read.
- }
-
- std::string version("unknown");
- if (!store.GetString(kLsbVersionKey, &version)) {
- LOG(ERROR) << "Unable to read " << kLsbVersionKey << " from "
- << lsb_release_;
- }
- int64_t payload_size = -1;
- base::GetFileSize(FilePath(payload_path), &payload_size);
- std::string meta_data = StringPrintf("%sexec_name=%s\n"
- "ver=%s\n"
- "payload=%s\n"
- "payload_size=%" PRId64 "\n"
- "done=1\n",
- extra_metadata_.c_str(),
- exec_name.c_str(),
- version.c_str(),
- payload_path.c_str(),
- payload_size);
- // We must use WriteNewFile instead of base::WriteFile as we
- // do not want to write with root access to a symlink that an attacker
- // might have created.
- if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
- LOG(ERROR) << "Unable to write " << meta_path.value();
- }
-}
-
-bool CrashCollector::IsCrashTestInProgress() {
- return base::PathExists(FilePath(kCrashTestInProgressPath));
-}
-
-bool CrashCollector::IsDeveloperImage() {
- // If we're testing crash reporter itself, we don't want to special-case
- // for developer images.
- if (IsCrashTestInProgress())
- return false;
- return base::PathExists(FilePath(kLeaveCoreFile));
-}
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
deleted file mode 100644
index 0d335cd..0000000
--- a/crash_reporter/crash_collector.h
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRASH_REPORTER_CRASH_COLLECTOR_H_
-#define CRASH_REPORTER_CRASH_COLLECTOR_H_
-
-#include <sys/stat.h>
-
-#include <map>
-#include <string>
-
-#include <base/files/file_path.h>
-#include <base/macros.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-// User crash collector.
-class CrashCollector {
- public:
- typedef void (*CountCrashFunction)();
- typedef bool (*IsFeedbackAllowedFunction)();
-
- CrashCollector();
-
- virtual ~CrashCollector();
-
- // Initialize the crash collector for detection of crashes, given a
- // crash counting function, and metrics collection enabled oracle.
- void Initialize(CountCrashFunction count_crash,
- IsFeedbackAllowedFunction is_metrics_allowed);
-
- protected:
- friend class CrashCollectorTest;
- FRIEND_TEST(ChromeCollectorTest, HandleCrash);
- FRIEND_TEST(CrashCollectorTest, CheckHasCapacityCorrectBasename);
- FRIEND_TEST(CrashCollectorTest, CheckHasCapacityStrangeNames);
- FRIEND_TEST(CrashCollectorTest, CheckHasCapacityUsual);
- FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo);
- FRIEND_TEST(CrashCollectorTest, GetCrashPath);
- FRIEND_TEST(CrashCollectorTest, GetLogContents);
- FRIEND_TEST(CrashCollectorTest, ForkExecAndPipe);
- FRIEND_TEST(CrashCollectorTest, FormatDumpBasename);
- FRIEND_TEST(CrashCollectorTest, Initialize);
- FRIEND_TEST(CrashCollectorTest, MetaData);
- FRIEND_TEST(CrashCollectorTest, Sanitize);
- FRIEND_TEST(CrashCollectorTest, WriteNewFile);
- FRIEND_TEST(ForkExecAndPipeTest, Basic);
- FRIEND_TEST(ForkExecAndPipeTest, NonZeroReturnValue);
- FRIEND_TEST(ForkExecAndPipeTest, BadOutputFile);
- FRIEND_TEST(ForkExecAndPipeTest, ExistingOutputFile);
- FRIEND_TEST(ForkExecAndPipeTest, BadExecutable);
- FRIEND_TEST(ForkExecAndPipeTest, StderrCaptured);
- FRIEND_TEST(ForkExecAndPipeTest, NULLParam);
- FRIEND_TEST(ForkExecAndPipeTest, NoParams);
- FRIEND_TEST(ForkExecAndPipeTest, SegFaultHandling);
-
- // Set maximum enqueued crashes in a crash directory.
- static const int kMaxCrashDirectorySize;
-
- // Writes |data| of |size| to |filename|, which must be a new file.
- // If the file already exists or writing fails, return a negative value.
- // Otherwise returns the number of bytes written.
- int WriteNewFile(const base::FilePath &filename, const char *data, int size);
-
- // Return a filename that has only [a-z0-1_] characters by mapping
- // all others into '_'.
- std::string Sanitize(const std::string &name);
-
- // For testing, set the directory always returned by
- // GetCreatedCrashDirectoryByEuid.
- void ForceCrashDirectory(const base::FilePath &forced_directory) {
- forced_crash_directory_ = forced_directory;
- }
-
- base::FilePath GetCrashDirectoryInfo(uid_t process_euid,
- uid_t default_user_id,
- gid_t default_user_group,
- mode_t *mode,
- uid_t *directory_owner,
- gid_t *directory_group);
- bool GetUserInfoFromName(const std::string &name,
- uid_t *uid,
- gid_t *gid);
-
- // Determines the crash directory for given euid, and creates the
- // directory if necessary with appropriate permissions. If
- // |out_of_capacity| is not nullptr, it is set to indicate if the call
- // failed due to not having capacity in the crash directory. Returns
- // true whether or not directory needed to be created, false on any
- // failure. If the crash directory is at capacity, returns false.
- bool GetCreatedCrashDirectoryByEuid(uid_t euid,
- base::FilePath *crash_file_path,
- bool *out_of_capacity);
-
- // Format crash name based on components.
- std::string FormatDumpBasename(const std::string &exec_name,
- time_t timestamp,
- pid_t pid);
-
- // Create a file path to a file in |crash_directory| with the given
- // |basename| and |extension|.
- base::FilePath GetCrashPath(const base::FilePath &crash_directory,
- const std::string &basename,
- const std::string &extension);
-
- base::FilePath GetProcessPath(pid_t pid);
- bool GetSymlinkTarget(const base::FilePath &symlink,
- base::FilePath *target);
- bool GetExecutableBaseNameFromPid(pid_t pid,
- std::string *base_name);
-
- // Check given crash directory still has remaining capacity for another
- // crash.
- bool CheckHasCapacity(const base::FilePath &crash_directory);
-
- // Write a log applicable to |exec_name| to |output_file| based on the
- // log configuration file at |config_path|.
- bool GetLogContents(const base::FilePath &config_path,
- const std::string &exec_name,
- const base::FilePath &output_file);
-
- // Add non-standard meta data to the crash metadata file. Call
- // before calling WriteCrashMetaData. Key must not contain "=" or
- // "\n" characters. Value must not contain "\n" characters.
- void AddCrashMetaData(const std::string &key, const std::string &value);
-
- // Add a file to be uploaded to the crash reporter server. The file must
- // persist until the crash report is sent; ideally it should live in the same
- // place as the .meta file, so it can be cleaned up automatically.
- void AddCrashMetaUploadFile(const std::string &key, const std::string &path);
-
- // Add non-standard meta data to the crash metadata file.
- // Data added though this call will be uploaded to the crash reporter server,
- // appearing as a form field.
- void AddCrashMetaUploadData(const std::string &key, const std::string &value);
-
- // Write a file of metadata about crash.
- void WriteCrashMetaData(const base::FilePath &meta_path,
- const std::string &exec_name,
- const std::string &payload_path);
-
- // Returns true if the a crash test is currently running.
- bool IsCrashTestInProgress();
- // Returns true if we should consider ourselves to be running on a
- // developer image.
- bool IsDeveloperImage();
-
- CountCrashFunction count_crash_function_;
- IsFeedbackAllowedFunction is_feedback_allowed_function_;
- std::string extra_metadata_;
- base::FilePath forced_crash_directory_;
- std::string lsb_release_;
- base::FilePath log_config_path_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CrashCollector);
-};
-
-#endif // CRASH_REPORTER_CRASH_COLLECTOR_H_
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
deleted file mode 100644
index 13fb76a..0000000
--- a/crash_reporter/crash_collector_test.cc
+++ /dev/null
@@ -1,287 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "crash_collector_test.h"
-
-#include <unistd.h>
-#include <utility>
-
-#include <base/files/file_util.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
-#include <gtest/gtest.h>
-
-#include "crash_collector.h"
-
-using base::FilePath;
-using base::StringPrintf;
-using chromeos::FindLog;
-using ::testing::Invoke;
-using ::testing::Return;
-
-namespace {
-
-void CountCrash() {
- ADD_FAILURE();
-}
-
-bool IsMetrics() {
- ADD_FAILURE();
- return false;
-}
-
-} // namespace
-
-class CrashCollectorTest : public ::testing::Test {
- public:
- void SetUp() {
- EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return());
-
- collector_.Initialize(CountCrash, IsMetrics);
- test_dir_ = FilePath("test");
- base::CreateDirectory(test_dir_);
- chromeos::ClearLog();
- }
-
- void TearDown() {
- base::DeleteFile(test_dir_, true);
- }
-
- bool CheckHasCapacity();
-
- protected:
- CrashCollectorMock collector_;
- FilePath test_dir_;
-};
-
-TEST_F(CrashCollectorTest, Initialize) {
- ASSERT_TRUE(CountCrash == collector_.count_crash_function_);
- ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_);
-}
-
-TEST_F(CrashCollectorTest, WriteNewFile) {
- FilePath test_file = test_dir_.Append("test_new");
- const char kBuffer[] = "buffer";
- EXPECT_EQ(strlen(kBuffer),
- collector_.WriteNewFile(test_file,
- kBuffer,
- strlen(kBuffer)));
- EXPECT_LT(collector_.WriteNewFile(test_file,
- kBuffer,
- strlen(kBuffer)), 0);
-}
-
-TEST_F(CrashCollectorTest, Sanitize) {
- EXPECT_EQ("chrome", collector_.Sanitize("chrome"));
- EXPECT_EQ("CHROME", collector_.Sanitize("CHROME"));
- EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2"));
- EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)"));
- EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar"));
- EXPECT_EQ("", collector_.Sanitize(""));
- EXPECT_EQ("_", collector_.Sanitize(" "));
-}
-
-TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) {
- FilePath path;
- const int kRootUid = 0;
- const int kRootGid = 0;
- const int kNtpUid = 5;
- const int kChronosUid = 1000;
- const int kChronosGid = 1001;
- const mode_t kExpectedSystemMode = 01755;
- const mode_t kExpectedUserMode = 0755;
-
- mode_t directory_mode;
- uid_t directory_owner;
- gid_t directory_group;
-
- path = collector_.GetCrashDirectoryInfo(kRootUid,
- kChronosUid,
- kChronosGid,
- &directory_mode,
- &directory_owner,
- &directory_group);
- EXPECT_EQ("/var/spool/crash", path.value());
- EXPECT_EQ(kExpectedSystemMode, directory_mode);
- EXPECT_EQ(kRootUid, directory_owner);
- EXPECT_EQ(kRootGid, directory_group);
-
- path = collector_.GetCrashDirectoryInfo(kNtpUid,
- kChronosUid,
- kChronosGid,
- &directory_mode,
- &directory_owner,
- &directory_group);
- EXPECT_EQ("/var/spool/crash", path.value());
- EXPECT_EQ(kExpectedSystemMode, directory_mode);
- EXPECT_EQ(kRootUid, directory_owner);
- EXPECT_EQ(kRootGid, directory_group);
-
- path = collector_.GetCrashDirectoryInfo(kChronosUid,
- kChronosUid,
- kChronosGid,
- &directory_mode,
- &directory_owner,
- &directory_group);
- EXPECT_EQ("/var/spool/crash", path.value());
- EXPECT_EQ(kExpectedUserMode, directory_mode);
- EXPECT_EQ(kChronosUid, directory_owner);
- EXPECT_EQ(kChronosGid, directory_group);
-}
-
-TEST_F(CrashCollectorTest, FormatDumpBasename) {
- struct tm tm = {0};
- tm.tm_sec = 15;
- tm.tm_min = 50;
- tm.tm_hour = 13;
- tm.tm_mday = 23;
- tm.tm_mon = 4;
- tm.tm_year = 110;
- tm.tm_isdst = -1;
- std::string basename =
- collector_.FormatDumpBasename("foo", mktime(&tm), 100);
- ASSERT_EQ("foo.20100523.135015.100", basename);
-}
-
-TEST_F(CrashCollectorTest, GetCrashPath) {
- EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core",
- collector_.GetCrashPath(FilePath("/var/spool/crash"),
- "myprog.20100101.1200.1234",
- "core").value());
- EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp",
- collector_.GetCrashPath(FilePath("/home/chronos/user/crash"),
- "chrome.20100101.1200.1234",
- "dmp").value());
-}
-
-
-bool CrashCollectorTest::CheckHasCapacity() {
- static const char kFullMessage[] = "Crash directory test already full";
- bool has_capacity = collector_.CheckHasCapacity(test_dir_);
- bool has_message = FindLog(kFullMessage);
- EXPECT_EQ(has_message, !has_capacity);
- return has_capacity;
-}
-
-TEST_F(CrashCollectorTest, CheckHasCapacityUsual) {
- // Test kMaxCrashDirectorySize - 1 non-meta files can be added.
- for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), "", 0);
- EXPECT_TRUE(CheckHasCapacity());
- }
-
- // Test an additional kMaxCrashDirectorySize - 1 meta files fit.
- for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), "", 0);
- EXPECT_TRUE(CheckHasCapacity());
- }
-
- // Test an additional kMaxCrashDirectorySize meta files don't fit.
- for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), "", 0);
- EXPECT_FALSE(CheckHasCapacity());
- }
-}
-
-TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) {
- // Test kMaxCrashDirectorySize - 1 files can be added.
- for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), "", 0);
- EXPECT_TRUE(CheckHasCapacity());
- }
- base::WriteFile(test_dir_.Append("file.last.core"), "", 0);
- EXPECT_FALSE(CheckHasCapacity());
-}
-
-TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) {
- // Test many files with different extensions and same base fit.
- for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0);
- EXPECT_TRUE(CheckHasCapacity());
- }
- // Test dot files are treated as individual files.
- for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0);
- EXPECT_TRUE(CheckHasCapacity());
- }
- base::WriteFile(test_dir_.Append("normal.meta"), "", 0);
- EXPECT_FALSE(CheckHasCapacity());
-}
-
-TEST_F(CrashCollectorTest, MetaData) {
- const char kMetaFileBasename[] = "generated.meta";
- FilePath meta_file = test_dir_.Append(kMetaFileBasename);
- FilePath lsb_release = test_dir_.Append("lsb-release");
- FilePath payload_file = test_dir_.Append("payload-file");
- std::string contents;
- collector_.lsb_release_ = lsb_release.value();
- const char kLsbContents[] =
- "CHROMEOS_RELEASE_BOARD=lumpy\n"
- "CHROMEOS_RELEASE_VERSION=6727.0.2015_01_26_0853\n"
- "CHROMEOS_RELEASE_NAME=Chromium OS\n";
- ASSERT_TRUE(base::WriteFile(lsb_release, kLsbContents, strlen(kLsbContents)));
- const char kPayload[] = "foo";
- ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
- collector_.AddCrashMetaData("foo", "bar");
- collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
- EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
- const char kExpectedMeta[] =
- "foo=bar\n"
- "exec_name=kernel\n"
- "ver=6727.0.2015_01_26_0853\n"
- "payload=test/payload-file\n"
- "payload_size=3\n"
- "done=1\n";
- EXPECT_EQ(kExpectedMeta, contents);
-
- // Test target of symlink is not overwritten.
- payload_file = test_dir_.Append("payload2-file");
- ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
- FilePath meta_symlink_path = test_dir_.Append("symlink.meta");
- ASSERT_EQ(0,
- symlink(kMetaFileBasename,
- meta_symlink_path.value().c_str()));
- ASSERT_TRUE(base::PathExists(meta_symlink_path));
- chromeos::ClearLog();
- collector_.WriteCrashMetaData(meta_symlink_path,
- "kernel",
- payload_file.value());
- // Target metadata contents should have stayed the same.
- contents.clear();
- EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
- EXPECT_EQ(kExpectedMeta, contents);
- EXPECT_TRUE(FindLog("Unable to write"));
-
- // Test target of dangling symlink is not created.
- base::DeleteFile(meta_file, false);
- ASSERT_FALSE(base::PathExists(meta_file));
- chromeos::ClearLog();
- collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
- payload_file.value());
- EXPECT_FALSE(base::PathExists(meta_file));
- EXPECT_TRUE(FindLog("Unable to write"));
-}
-
-TEST_F(CrashCollectorTest, GetLogContents) {
- FilePath config_file = test_dir_.Append("crash_config");
- FilePath output_file = test_dir_.Append("crash_log");
- const char kConfigContents[] =
- "foobar=echo hello there | \\\n sed -e \"s/there/world/\"";
- ASSERT_TRUE(
- base::WriteFile(config_file, kConfigContents, strlen(kConfigContents)));
- base::DeleteFile(FilePath(output_file), false);
- EXPECT_FALSE(collector_.GetLogContents(config_file,
- "barfoo",
- output_file));
- EXPECT_FALSE(base::PathExists(output_file));
- base::DeleteFile(FilePath(output_file), false);
- EXPECT_TRUE(collector_.GetLogContents(config_file,
- "foobar",
- output_file));
- ASSERT_TRUE(base::PathExists(output_file));
- std::string contents;
- EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
- EXPECT_EQ("hello world\n", contents);
-}
diff --git a/crash_reporter/crash_collector_test.h b/crash_reporter/crash_collector_test.h
deleted file mode 100644
index c875d44..0000000
--- a/crash_reporter/crash_collector_test.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
-#define CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
-
-#include "crash_collector.h"
-
-#include <map>
-#include <string>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-class CrashCollectorMock : public CrashCollector {
- public:
- MOCK_METHOD0(SetUpDBus, void());
- MOCK_METHOD1(GetActiveUserSessions,
- bool(std::map<std::string, std::string> *sessions));
-};
-
-#endif // CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
deleted file mode 100644
index 0e45992..0000000
--- a/crash_reporter/crash_reporter.cc
+++ /dev/null
@@ -1,297 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <fcntl.h> // for open
-
-#include <string>
-#include <vector>
-
-#include <base/files/file_util.h>
-#include <base/logging.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <chromeos/flag_helper.h>
-#include <chromeos/syslog_logging.h>
-#include <metrics/metrics_library.h>
-
-#include "kernel_collector.h"
-#include "kernel_warning_collector.h"
-#include "udev_collector.h"
-#include "unclean_shutdown_collector.h"
-#include "user_collector.h"
-
-static const char kCrashCounterHistogram[] = "Logging.CrashCounter";
-static const char kUserCrashSignal[] =
- "org.chromium.CrashReporter.UserCrash";
-static const char kKernelCrashDetected[] = "/var/run/kernel-crash-detected";
-static const char kUncleanShutdownDetected[] =
- "/var/run/unclean-shutdown-detected";
-
-// Enumeration of kinds of crashes to be used in the CrashCounter histogram.
-enum CrashKinds {
- kCrashKindUncleanShutdown = 1,
- kCrashKindUser = 2,
- kCrashKindKernel = 3,
- kCrashKindUdev = 4,
- kCrashKindKernelWarning = 5,
- kCrashKindMax
-};
-
-static MetricsLibrary s_metrics_lib;
-
-using base::FilePath;
-using base::StringPrintf;
-
-static bool IsFeedbackAllowed() {
- return s_metrics_lib.AreMetricsEnabled();
-}
-
-static bool TouchFile(const FilePath &file_path) {
- return base::WriteFile(file_path, "", 0) == 0;
-}
-
-static void SendCrashMetrics(CrashKinds type, const char* name) {
- // TODO(kmixter): We can remove this histogram as part of
- // crosbug.com/11163.
- s_metrics_lib.SendEnumToUMA(kCrashCounterHistogram, type, kCrashKindMax);
- s_metrics_lib.SendCrashToUMA(name);
-}
-
-static void CountKernelCrash() {
- SendCrashMetrics(kCrashKindKernel, "kernel");
-}
-
-static void CountUdevCrash() {
- SendCrashMetrics(kCrashKindUdev, "udevcrash");
-}
-
-static void CountUncleanShutdown() {
- SendCrashMetrics(kCrashKindUncleanShutdown, "uncleanshutdown");
-}
-
-static void CountUserCrash() {
- SendCrashMetrics(kCrashKindUser, "user");
- std::string command = StringPrintf(
- "/usr/bin/dbus-send --type=signal --system / \"%s\" &",
- kUserCrashSignal);
- // Announce through D-Bus whenever a user crash happens. This is
- // used by the metrics daemon to log active use time between
- // crashes.
- //
- // This could be done more efficiently by explicit fork/exec or
- // using a dbus library directly. However, this should run
- // relatively rarely and longer term we may need to implement a
- // better way to do this that doesn't rely on D-Bus.
- //
- // We run in the background in case dbus daemon itself is crashed
- // and not responding. This allows us to not block and potentially
- // deadlock on a dbus-daemon crash. If dbus-daemon crashes without
- // restarting, each crash will fork off a lot of dbus-send
- // processes. Such a system is in a unusable state and will need
- // to be restarted anyway.
-
- int status = system(command.c_str());
- LOG_IF(WARNING, status != 0) << "dbus-send running failed";
-}
-
-
-static int Initialize(KernelCollector *kernel_collector,
- UserCollector *user_collector,
- UncleanShutdownCollector *unclean_shutdown_collector,
- const bool unclean_check,
- const bool clean_shutdown) {
- CHECK(!clean_shutdown) << "Incompatible options";
-
- bool was_kernel_crash = false;
- bool was_unclean_shutdown = false;
- kernel_collector->Enable();
- if (kernel_collector->is_enabled()) {
- was_kernel_crash = kernel_collector->Collect();
- }
-
- if (unclean_check) {
- was_unclean_shutdown = unclean_shutdown_collector->Collect();
- }
-
- // Touch a file to notify the metrics daemon that a kernel
- // crash has been detected so that it can log the time since
- // the last kernel crash.
- if (IsFeedbackAllowed()) {
- if (was_kernel_crash) {
- TouchFile(FilePath(kKernelCrashDetected));
- } else if (was_unclean_shutdown) {
- // We only count an unclean shutdown if it did not come with
- // an associated kernel crash.
- TouchFile(FilePath(kUncleanShutdownDetected));
- }
- }
-
- // Must enable the unclean shutdown collector *after* collecting.
- unclean_shutdown_collector->Enable();
- user_collector->Enable();
-
- return 0;
-}
-
-static int HandleUserCrash(UserCollector *user_collector,
- const std::string& user, const bool crash_test) {
- // Handle a specific user space crash.
- CHECK(!user.empty()) << "--user= must be set";
-
- // Make it possible to test what happens when we crash while
- // handling a crash.
- if (crash_test) {
- *(volatile char *)0 = 0;
- return 0;
- }
-
- // Accumulate logs to help in diagnosing failures during user collection.
- chromeos::LogToString(true);
- // Handle the crash, get the name of the process from procfs.
- bool handled = user_collector->HandleCrash(user, nullptr);
- chromeos::LogToString(false);
- if (!handled)
- return 1;
- return 0;
-}
-
-static int HandleUdevCrash(UdevCollector *udev_collector,
- const std::string& udev_event) {
- // Handle a crash indicated by a udev event.
- CHECK(!udev_event.empty()) << "--udev= must be set";
-
- // Accumulate logs to help in diagnosing failures during user collection.
- chromeos::LogToString(true);
- bool handled = udev_collector->HandleCrash(udev_event);
- chromeos::LogToString(false);
- if (!handled)
- return 1;
- return 0;
-}
-
-static int HandleKernelWarning(KernelWarningCollector
- *kernel_warning_collector) {
- // Accumulate logs to help in diagnosing failures during collection.
- chromeos::LogToString(true);
- bool handled = kernel_warning_collector->Collect();
- chromeos::LogToString(false);
- if (!handled)
- return 1;
- return 0;
-}
-
-// Interactive/diagnostics mode for generating kernel crash signatures.
-static int GenerateKernelSignature(KernelCollector *kernel_collector,
- const std::string& kernel_signature_file) {
- std::string kcrash_contents;
- std::string signature;
- if (!base::ReadFileToString(FilePath(kernel_signature_file),
- &kcrash_contents)) {
- fprintf(stderr, "Could not read file.\n");
- return 1;
- }
- if (!kernel_collector->ComputeKernelStackSignature(
- kcrash_contents,
- &signature,
- true)) {
- fprintf(stderr, "Signature could not be generated.\n");
- return 1;
- }
- printf("Kernel crash signature is \"%s\".\n", signature.c_str());
- return 0;
-}
-
-// Ensure stdout, stdin, and stderr are open file descriptors. If
-// they are not, any code which writes to stderr/stdout may write out
-// to files opened during execution. In particular, when
-// crash_reporter is run by the kernel coredump pipe handler (via
-// kthread_create/kernel_execve), it will not have file table entries
-// 1 and 2 (stdout and stderr) populated. We populate them here.
-static void OpenStandardFileDescriptors() {
- int new_fd = -1;
- // We open /dev/null to fill in any of the standard [0, 2] file
- // descriptors. We leave these open for the duration of the
- // process. This works because open returns the lowest numbered
- // invalid fd.
- do {
- new_fd = open("/dev/null", 0);
- CHECK_GE(new_fd, 0) << "Unable to open /dev/null";
- } while (new_fd >= 0 && new_fd <= 2);
- close(new_fd);
-}
-
-int main(int argc, char *argv[]) {
- DEFINE_bool(init, false, "Initialize crash logging");
- DEFINE_bool(clean_shutdown, false, "Signal clean shutdown");
- DEFINE_string(generate_kernel_signature, "",
- "Generate signature from given kcrash file");
- DEFINE_bool(crash_test, false, "Crash test");
- DEFINE_string(user, "", "User crash info (pid:signal:exec_name)");
- DEFINE_bool(unclean_check, true, "Check for unclean shutdown");
- DEFINE_string(udev, "", "Udev event description (type:device:subsystem)");
- DEFINE_bool(kernel_warning, false, "Report collected kernel warning");
- DEFINE_string(pid, "", "PID of crashing process");
- DEFINE_string(uid, "", "UID of crashing process");
- DEFINE_string(exe, "", "Executable name of crashing process");
- DEFINE_bool(core2md_failure, false, "Core2md failure test");
- DEFINE_bool(directory_failure, false, "Spool directory failure test");
- DEFINE_string(filter_in, "",
- "Ignore all crashes but this for testing");
-
- OpenStandardFileDescriptors();
- FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0]));
- s_metrics_lib.Init();
- chromeos::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
- chromeos::OpenLog(my_path.BaseName().value().c_str(), true);
- chromeos::InitLog(chromeos::kLogToSyslog);
-
- KernelCollector kernel_collector;
- kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed);
- UserCollector user_collector;
- user_collector.Initialize(CountUserCrash,
- my_path.value(),
- IsFeedbackAllowed,
- true, // generate_diagnostics
- FLAGS_core2md_failure,
- FLAGS_directory_failure,
- FLAGS_filter_in);
- UncleanShutdownCollector unclean_shutdown_collector;
- unclean_shutdown_collector.Initialize(CountUncleanShutdown,
- IsFeedbackAllowed);
- UdevCollector udev_collector;
- udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
-
- KernelWarningCollector kernel_warning_collector;
- kernel_warning_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
-
- if (FLAGS_init) {
- return Initialize(&kernel_collector,
- &user_collector,
- &unclean_shutdown_collector,
- FLAGS_unclean_check,
- FLAGS_clean_shutdown);
- }
-
- if (FLAGS_clean_shutdown) {
- unclean_shutdown_collector.Disable();
- user_collector.Disable();
- return 0;
- }
-
- if (!FLAGS_generate_kernel_signature.empty()) {
- return GenerateKernelSignature(&kernel_collector,
- FLAGS_generate_kernel_signature);
- }
-
- if (!FLAGS_udev.empty()) {
- return HandleUdevCrash(&udev_collector, FLAGS_udev);
- }
-
- if (FLAGS_kernel_warning) {
- return HandleKernelWarning(&kernel_warning_collector);
- }
-
- return HandleUserCrash(&user_collector, FLAGS_user, FLAGS_crash_test);
-}
diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf
deleted file mode 100644
index f5ca80c..0000000
--- a/crash_reporter/crash_reporter_logs.conf
+++ /dev/null
@@ -1,112 +0,0 @@
-# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can
-# be found in the LICENSE file.
-
-# This file is parsed by chromeos::KeyValueStore. It has the format:
-#
-# <basename>=<shell command>\n
-#
-# Commands may be split across multiple lines using trailing backslashes.
-#
-# When an executable named <basename> crashes, the corresponding command is
-# executed and its standard output and standard error are attached to the crash
-# report.
-#
-# Use caution in modifying this file. Only run common Unix commands here, as
-# these commands will be run when a crash has recently occurred and we should
-# avoid running anything that might cause another crash. Similarly, these
-# commands block notification of the crash to parent processes, so commands
-# should execute quickly.
-
-update_engine=cat $(ls -1tr /var/log/update_engine | tail -5 | \
- sed s.^./var/log/update_engine/.) | tail -c 50000
-
-# The cros_installer output is logged into the update engine log file,
-# so it is handled in the same way as update_engine.
-cros_installer=cat $(ls -1tr /var/log/update_engine | tail -5 | \
- sed s.^./var/log/update_engine/.) | tail -c 50000
-
-# Dump the last 20 lines of the last two files in Chrome's system and user log
-# directories, along with the last 20 messages from the session manager.
-chrome=\
- for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) \
- $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* 2>/dev/null | tail -2); do \
- echo "===$f (tail)==="; \
- tail -20 $f; \
- echo EOF; \
- echo; \
- done; \
- echo "===session_manager (tail)==="; \
- awk '$3 ~ "^session_manager\[" { print }' /var/log/messages | tail -20; \
- echo EOF
-
-# The following rule is used for generating additional diagnostics when
-# collection of user crashes fails. This output should not be too large
-# as it is stored in memory. The output format specified for 'ps' is the
-# same as with the "u" ("user-oriented") option, except it doesn't show
-# the commands' arguments (i.e. "comm" instead of "command").
-crash_reporter-user-collection=\
- echo "===ps output==="; \
- ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | \
- tail -c 25000; \
- echo "===meminfo==="; \
- cat /proc/meminfo
-
-# This rule is similar to the crash_reporter-user-collection rule, except it is
-# run for kernel errors reported through udev events.
-crash_reporter-udev-collection-change-card0-drm=\
- for dri in /sys/kernel/debug/dri/*; do \
- echo "===$dri/i915_error_state==="; \
- cat $dri/i915_error_state; \
- done
-
-# When trackpad driver cyapa detects some abnormal behavior, we collect
-# additional logs from kernel messages.
-crash_reporter-udev-collection-change--i2c-cyapa=\
- /usr/sbin/kernel_log_collector.sh cyapa 30
-# When trackpad/touchscreen driver atmel_mxt_ts detects some abnormal behavior,
-# we collect additional logs from kernel messages.
-crash_reporter-udev-collection-change--i2c-atmel_mxt_ts=\
- /usr/sbin/kernel_log_collector.sh atmel 30
-# When touch device noise are detected, we collect relevant logs.
-# (crosbug.com/p/16788)
-crash_reporter-udev-collection---TouchNoise=cat /var/log/touch_noise.log
-# Periodically collect touch event log for debugging (crosbug.com/p/17244)
-crash_reporter-udev-collection---TouchEvent=cat /var/log/touch_event.log
-
-# Collect the last 50 lines of /var/log/messages and /var/log/net.log for
-# intel wifi driver (iwlwifi) for debugging purpose.
-crash_reporter-udev-collection-devcoredump-iwlwifi=\
- echo "===/var/log/messages==="; \
- tail -n 50 /var/log/messages; \
- echo "===/var/log/net.log==="; \
- tail -n 50 /var/log/net.log; \
- echo EOF
-
-# Dump the last 50 lines of the last two powerd log files -- if the job has
-# already restarted, we want to see the end of the previous instance's logs.
-powerd=\
- for f in $(ls -1tr /var/log/power_manager/powerd.[0-9]* | tail -2); do \
- echo "===$(basename $f) (tail)==="; \
- tail -50 $f; \
- echo EOF; \
- done
-# If power_supply_info aborts (due to e.g. a bad battery), its failure message
-# could end up in various places depending on which process was running it.
-# Attach the end of powerd's log since it might've also logged the underlying
-# problem.
-power_supply_info=\
- echo "===powerd.LATEST (tail)==="; \
- tail -50 /var/log/power_manager/powerd.LATEST; \
- echo EOF
-# powerd_setuid_helper gets run by powerd, so its stdout/stderr will be mixed in
-# with powerd's stdout/stderr.
-powerd_setuid_helper=\
- echo "===powerd.OUT (tail)==="; \
- tail -50 /var/log/powerd.out; \
- echo EOF
-
-# The following rules are only for testing purposes.
-crash_log_test=echo hello world
-crash_log_recursion_test=sleep 1 && \
- /usr/local/autotest/tests/crash_log_recursion_test
diff --git a/crash_reporter/crash_reporter_logs_test.cc b/crash_reporter/crash_reporter_logs_test.cc
deleted file mode 100644
index 9879470..0000000
--- a/crash_reporter/crash_reporter_logs_test.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include <base/files/file_path.h>
-#include <chromeos/key_value_store.h>
-#include <gtest/gtest.h>
-
-namespace {
-
-// Name of the checked-in configuration file containing log-collection commands.
-const char kConfigFile[] = "crash_reporter_logs.conf";
-
-// Executable name for Chrome. kConfigFile is expected to contain this entry.
-const char kChromeExecName[] = "chrome";
-
-} // namespace
-
-// Tests that the config file is parsable and that Chrome is listed.
-TEST(CrashReporterLogsTest, ReadConfig) {
- chromeos::KeyValueStore store;
- ASSERT_TRUE(store.Load(base::FilePath(kConfigFile)));
- std::string command;
- EXPECT_TRUE(store.GetString(kChromeExecName, &command));
- EXPECT_FALSE(command.empty());
-}
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
deleted file mode 100755
index 641ae2d..0000000
--- a/crash_reporter/crash_sender
+++ /dev/null
@@ -1,713 +0,0 @@
-#!/bin/sh
-
-# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-set -e
-
-# Default product ID in crash report (used if GOOGLE_CRASH_* is undefined).
-CHROMEOS_PRODUCT=ChromeOS
-
-# File whose existence implies crash reports may be sent, and whose
-# contents includes our machine's anonymized guid.
-CONSENT_ID="/home/chronos/Consent To Send Stats"
-
-# Crash sender lock in case the sender is already running.
-CRASH_SENDER_LOCK="/var/lock/crash_sender"
-
-# Path to file that indicates a crash test is currently running.
-CRASH_TEST_IN_PROGRESS_FILE="/tmp/crash-test-in-progress"
-
-# Path to find which is required for computing the crash rate.
-FIND="/usr/bin/find"
-
-# Set this to 1 in the environment to allow uploading crash reports
-# for unofficial versions.
-FORCE_OFFICIAL=${FORCE_OFFICIAL:-0}
-
-# Path to hardware class description.
-HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID"
-
-# Path to file that indicates this is a developer image.
-LEAVE_CORE_FILE="/root/.leave_core"
-
-# Path to list_proxies.
-LIST_PROXIES="/usr/bin/list_proxies"
-
-# Maximum crashes to send per day.
-MAX_CRASH_RATE=${MAX_CRASH_RATE:-32}
-
-# Path to metrics_client.
-METRICS_CLIENT="/usr/bin/metrics_client"
-
-# File whose existence mocks crash sending. If empty we pretend the
-# crash sending was successful, otherwise unsuccessful.
-MOCK_CRASH_SENDING="/tmp/mock-crash-sending"
-
-# Set this to 1 in the environment to pretend to have booted in developer
-# mode. This is used by autotests.
-MOCK_DEVELOPER_MODE=${MOCK_DEVELOPER_MODE:-0}
-
-# Ignore PAUSE_CRASH_SENDING file if set.
-OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0}
-
-# File whose existence causes crash sending to be delayed (for testing).
-# Must be stateful to enable testing kernel crashes.
-PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused"
-
-# URL to send official build crash reports to.
-REPORT_UPLOAD_PROD_URL="https://clients2.google.com/cr/report"
-
-# Path to a directory of restricted certificates which includes
-# a certificate for ${REPORT_UPLOAD_PROD_URL}.
-RESTRICTED_CERTIFICATES_PATH="/usr/share/chromeos-ca-certificates"
-
-# File whose existence implies we're running and not to start again.
-RUN_FILE="/var/run/crash_sender.pid"
-
-# Maximum time to sleep between sends.
-SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600}
-
-# Set this to 1 to allow uploading of device coredumps.
-DEVCOREDUMP_UPLOAD_FLAG_FILE=\
-"/var/lib/crash_reporter/device_coredump_upload_allowed"
-
-# The syslog tag for all logging we emit.
-TAG="$(basename $0)[$$]"
-
-# Directory to store timestamp files indicating the uploads in the past 24
-# hours.
-TIMESTAMPS_DIR="/var/lib/crash_sender"
-
-# Temp directory for this process.
-TMP_DIR=""
-
-# Chrome's crash report log file.
-CHROME_CRASH_LOG="/var/log/chrome/Crash Reports/uploads.log"
-
-lecho() {
- logger -t "${TAG}" "$@"
-}
-
-# Returns true if mock is enabled.
-is_mock() {
- [ -f "${MOCK_CRASH_SENDING}" ] && return 0
- return 1
-}
-
-is_mock_successful() {
- local mock_in=$(cat "${MOCK_CRASH_SENDING}")
- [ "${mock_in}" = "" ] && return 0 # empty file means success
- return 1
-}
-
-cleanup() {
- if [ -n "${TMP_DIR}" ]; then
- rm -rf "${TMP_DIR}"
- fi
- rm -f "${RUN_FILE}"
- crash_done
-}
-
-crash_done() {
- if is_mock; then
- # For testing purposes, emit a message to log so that we
- # know when the test has received all the messages from this run.
- lecho "crash_sender done."
- fi
-}
-
-is_official_image() {
- [ ${FORCE_OFFICIAL} -ne 0 ] && return 0
- grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official
-}
-
-# Returns 0 if the a crash test is currently running. NOTE: Mirrors
-# crash_collector.cc:CrashCollector::IsCrashTestInProgress().
-is_crash_test_in_progress() {
- [ -f "${CRASH_TEST_IN_PROGRESS_FILE}" ] && return 0
- return 1
-}
-
-# Returns 0 if we should consider ourselves to be running on a developer
-# image. NOTE: Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage().
-is_developer_image() {
- # If we're testing crash reporter itself, we don't want to special-case
- # for developer images.
- is_crash_test_in_progress && return 1
- [ -f "${LEAVE_CORE_FILE}" ] && return 0
- return 1
-}
-
-# Returns 0 if we should consider ourselves to be running on a test image.
-is_test_image() {
- # If we're testing crash reporter itself, we don't want to special-case
- # for test images.
- is_crash_test_in_progress && return 1
- case $(get_channel) in
- test*) return 0;;
- esac
- return 1
-}
-
-# Returns 0 if the machine booted up in developer mode.
-is_developer_mode() {
- [ ${MOCK_DEVELOPER_MODE} -ne 0 ] && return 0
- # If we're testing crash reporter itself, we don't want to special-case
- # for developer mode.
- is_crash_test_in_progress && return 1
- crossystem "devsw_boot?1" # exit status will be accurate
-}
-
-# Return 0 if the uploading of device coredumps is allowed.
-is_device_coredump_upload_allowed() {
- [ -f "${DEVCOREDUMP_UPLOAD_FLAG_FILE}" ] && return 0
- return 1
-}
-
-# Generate a uniform random number in 0..max-1.
-generate_uniform_random() {
- local max=$1
- local random="$(od -An -N4 -tu /dev/urandom)"
- echo $((random % max))
-}
-
-# Check if sending a crash now does not exceed the maximum 24hr rate and
-# commit to doing so, if not.
-check_rate() {
- mkdir -p ${TIMESTAMPS_DIR}
- # Only consider minidumps written in the past 24 hours by removing all older.
- ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) \
- -exec rm -- '{}' ';'
- local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w)
- lecho "Current send rate: ${sends_in_24hrs}sends/24hrs"
- if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then
- lecho "Cannot send more crashes:"
- lecho " current ${sends_in_24hrs}send/24hrs >= " \
- "max ${MAX_CRASH_RATE}send/24hrs"
- return 1
- fi
- mktemp "${TIMESTAMPS_DIR}"/XXXX > /dev/null
- return 0
-}
-
-# Gets the base part of a crash report file, such as name.01234.5678.9012 from
-# name.01234.5678.9012.meta or name.01234.5678.9012.log.tar.xz. We make sure
-# "name" is sanitized in CrashCollector::Sanitize to not include any periods.
-get_base() {
- echo "$1" | cut -d. -f-4
-}
-
-get_extension() {
- local extension="${1##*.}"
- local filename="${1%.*}"
- # For gzipped file, we ignore .gz and get the real extension
- if [ "${extension}" = "gz" ]; then
- echo "${filename##*.}"
- else
- echo "${extension}"
- fi
-}
-
-# Return which kind of report the given metadata file relates to
-get_kind() {
- local payload="$(get_key_value "$1" "payload")"
- if [ ! -r "${payload}" ]; then
- lecho "Missing payload: ${payload}"
- echo "undefined"
- return
- fi
- local kind="$(get_extension "${payload}")"
- if [ "${kind}" = "dmp" ]; then
- echo "minidump"
- return
- fi
- echo "${kind}"
-}
-
-get_key_value() {
- local file="$1" key="$2" value
-
- if [ -f "${file}" ]; then
- # Return the first entry. There shouldn't be more than one anyways.
- # Substr at length($1) + 2 skips past the key and following = sign (awk
- # uses 1-based indexes), but preserves embedded = characters.
- value=$(sed -n "/^${key}[[:space:]]*=/{s:^[^=]*=::p;q}" "${file}")
- fi
-
- echo "${value:-undefined}"
-}
-
-get_keys() {
- local file="$1" regex="$2"
-
- awk -F'[[:space:]=]' -vregex="${regex}" \
- 'match($1, regex) { print $1 }' "${file}"
-}
-
-# Return the board name.
-get_board() {
- get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD"
-}
-
-# Return the channel name (sans "-channel" suffix).
-get_channel() {
- get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_TRACK" |
- sed 's:-channel$::'
-}
-
-# Return the hardware class or "undefined".
-get_hardware_class() {
- if [ -r "${HWCLASS_PATH}" ]; then
- cat "${HWCLASS_PATH}"
- elif crossystem hwid > /dev/null 2>&1; then
- echo "$(crossystem hwid)"
- else
- echo "undefined"
- fi
-}
-
-send_crash() {
- local meta_path="$1"
- local report_payload="$(get_key_value "${meta_path}" "payload")"
- local kind="$(get_kind "${meta_path}")"
- local exec_name="$(get_key_value "${meta_path}" "exec_name")"
- local url="${REPORT_UPLOAD_PROD_URL}"
- local chromeos_version="$(get_key_value "${meta_path}" "ver")"
- local board="$(get_board)"
- local hwclass="$(get_hardware_class)"
- local write_payload_size="$(get_key_value "${meta_path}" "payload_size")"
- local log="$(get_key_value "${meta_path}" "log")"
- local sig="$(get_key_value "${meta_path}" "sig")"
- local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)"
- local product="$(get_key_value "${meta_path}" "upload_var_prod")"
- local version="$(get_key_value "${meta_path}" "upload_var_ver")"
- local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")"
- local guid
-
- set -- \
- -F "write_payload_size=${write_payload_size}" \
- -F "send_payload_size=${send_payload_size}"
- if [ "${sig}" != "undefined" ]; then
- set -- "$@" \
- -F "sig=${sig}" \
- -F "sig2=${sig}"
- fi
- if [ -r "${report_payload}" ]; then
- set -- "$@" \
- -F "upload_file_${kind}=@${report_payload}"
- fi
- if [ "${log}" != "undefined" -a -r "${log}" ]; then
- set -- "$@" \
- -F "log=@${log}"
- fi
-
- if [ "${upload_prefix}" = "undefined" ]; then
- upload_prefix=""
- fi
-
- # Grab any variable that begins with upload_.
- local v
- for k in $(get_keys "${meta_path}" "^upload_"); do
- v="$(get_key_value "${meta_path}" "${k}")"
- case ${k} in
- # Product & version are handled separately.
- upload_var_prod) ;;
- upload_var_ver) ;;
- upload_var_*)
- set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}"
- ;;
- upload_file_*)
- if [ -r "${v}" ]; then
- set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}"
- fi
- ;;
- esac
- done
-
- # When uploading Chrome reports we need to report the right product and
- # version. If the meta file does not specify it, use GOOGLE_CRASH_ID
- # as the product and GOOGLE_CRASH_VERSION_ID as the version.
- if [ "${product}" = "undefined" ]; then
- product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')"
- fi
- if [ "${version}" = "undefined" ]; then
- version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')"
- fi
-
- # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in
- # /etc/os-release.
- if [ "${product}" = "undefined" ]; then
- product="$(get_key_value /etc/os-release 'ID')"
- fi
- if [ "${version}" = "undefined" ]; then
- version="$(get_key_value /etc/os-release 'VERSION_ID')"
- fi
-
- # If ID or VERSION_ID is undefined, we use the default product name
- # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release.
- if [ "${product}" = "undefined" ]; then
- product="${CHROMEOS_PRODUCT}"
- fi
- if [ "${version}" = "undefined" ]; then
- version="${chromeos_version}"
- fi
-
- local image_type
- if is_test_image; then
- image_type="test"
- elif is_developer_image; then
- image_type="dev"
- elif [ ${FORCE_OFFICIAL} -ne 0 ]; then
- image_type="force-official"
- elif is_mock && ! is_mock_successful; then
- image_type="mock-fail"
- fi
-
- local boot_mode
- if ! crossystem "cros_debug" > /dev/null 2>&1; then
- # Sanity-check failed that makes sure crossystem exists.
- lecho "Cannot determine boot mode due to error running crossystem command"
- boot_mode="missing-crossystem"
- elif is_developer_mode; then
- boot_mode="dev"
- fi
-
- # Need to strip dashes ourselves as Chrome preserves it in the file
- # nowadays. This is also what the Chrome breakpad client does.
- guid=$(tr -d '-' < "${CONSENT_ID}")
-
- local error_type="$(get_key_value "${meta_path}" "error_type")"
- [ "${error_type}" = "undefined" ] && error_type=
-
- lecho "Sending crash:"
- if [ "${product}" != "${CHROMEOS_PRODUCT}" ]; then
- lecho " Sending crash report on behalf of ${product}"
- fi
- lecho " Metadata: ${meta_path} (${kind})"
- lecho " Payload: ${report_payload}"
- lecho " Version: ${version}"
- [ -n "${image_type}" ] && lecho " Image type: ${image_type}"
- [ -n "${boot_mode}" ] && lecho " Boot mode: ${boot_mode}"
- if is_mock; then
- lecho " Product: ${product}"
- lecho " URL: ${url}"
- lecho " Board: ${board}"
- lecho " HWClass: ${hwclass}"
- lecho " write_payload_size: ${write_payload_size}"
- lecho " send_payload_size: ${send_payload_size}"
- if [ "${log}" != "undefined" ]; then
- lecho " log: @${log}"
- fi
- if [ "${sig}" != "undefined" ]; then
- lecho " sig: ${sig}"
- fi
- fi
- lecho " Exec name: ${exec_name}"
- [ -n "${error_type}" ] && lecho " Error type: ${error_type}"
- if is_mock; then
- if ! is_mock_successful; then
- lecho "Mocking unsuccessful send"
- return 1
- fi
- lecho "Mocking successful send"
- return 0
- fi
-
- # Read in the first proxy, if any, for a given URL. NOTE: The
- # double-quotes are necessary due to a bug in dash with the "local"
- # builtin command and values that have spaces in them (see
- # "https://bugs.launchpad.net/ubuntu/+source/dash/+bug/139097").
- if [ -f "${LIST_PROXIES}" ]; then
- local proxy ret
- proxy=$("${LIST_PROXIES}" --quiet "${url}")
- ret=$?
- if [ ${ret} -ne 0 ]; then
- proxy=''
- lecho -psyslog.warn \
- "Listing proxies failed with exit code ${ret}"
- else
- proxy=$(echo "${proxy}" | head -1)
- fi
- fi
- # if a direct connection should be used, unset the proxy variable.
- [ "${proxy}" = "direct://" ] && proxy=
- local report_id="${TMP_DIR}/report_id"
- local curl_stderr="${TMP_DIR}/curl_stderr"
-
- set +e
- curl "${url}" -v ${proxy:+--proxy "$proxy"} \
- --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
- -F "prod=${product}" \
- -F "ver=${version}" \
- -F "board=${board}" \
- -F "hwclass=${hwclass}" \
- -F "exec_name=${exec_name}" \
- ${image_type:+-F "image_type=${image_type}"} \
- ${boot_mode:+-F "boot_mode=${boot_mode}"} \
- ${error_type:+-F "error_type=${error_type}"} \
- -F "guid=${guid}" \
- -o "${report_id}" \
- "$@" \
- 2>"${curl_stderr}"
- curl_result=$?
- set -e
-
- if [ ${curl_result} -eq 0 ]; then
- local id="$(cat "${report_id}")"
- local product_name
- local timestamp="$(date +%s)"
- case ${product} in
- Chrome_ChromeOS)
- if is_official_image; then
- product_name="Chrome"
- else
- product_name="Chromium"
- fi
- ;;
- *)
- if is_official_image; then
- product_name="ChromeOS"
- else
- product_name="ChromiumOS"
- fi
- ;;
- esac
- printf '%s,%s,%s\n' \
- "${timestamp}" "${id}" "${product_name}" >> "${CHROME_CRASH_LOG}"
- lecho "Crash report receipt ID ${id}"
- else
- lecho "Crash sending failed with exit code ${curl_result}: " \
- "$(cat "${curl_stderr}")"
- fi
-
- rm -f "${report_id}"
-
- return ${curl_result}
-}
-
-# *.meta files always end with done=1 so we can tell if they are complete.
-is_complete_metadata() {
- grep -q "done=1" "$1"
-}
-
-# Remove the given report path.
-remove_report() {
- local base="${1%.*}"
- rm -f -- "${base}".*
-}
-
-# Send all crashes from the given directory. This applies even when we're on a
-# 3G connection (see crosbug.com/3304 for discussion).
-send_crashes() {
- local dir="$1"
-
- if [ ! -d "${dir}" ]; then
- return
- fi
-
- # Consider any old files which still have no corresponding meta file
- # as orphaned, and remove them.
- for old_file in $(${FIND} "${dir}" -mindepth 1 \
- -mmin +$((24 * 60)) -type f); do
- if [ ! -e "$(get_base "${old_file}").meta" ]; then
- lecho "Removing old orphaned file: ${old_file}."
- rm -f -- "${old_file}"
- fi
- done
-
- # Look through all metadata (*.meta) files, oldest first. That way, the rate
- # limit does not stall old crashes if there's a high amount of new crashes
- # coming in.
- # For each crash report, first evaluate conditions that might lead to its
- # removal to honor user choice and to free disk space as soon as possible,
- # then decide whether it should be sent right now or kept for later sending.
- for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do
- lecho "Considering metadata ${meta_path}."
-
- local kind=$(get_kind "${meta_path}")
- if [ "${kind}" != "minidump" ] && \
- [ "${kind}" != "kcrash" ] && \
- [ "${kind}" != "log" ] &&
- [ "${kind}" != "devcore" ]; then
- lecho "Unknown report kind ${kind}. Removing report."
- remove_report "${meta_path}"
- continue
- fi
-
- if ! is_complete_metadata "${meta_path}"; then
- # This report is incomplete, so if it's old, just remove it.
- local old_meta=$(${FIND} "${dir}" -mindepth 1 -name \
- $(basename "${meta_path}") -mmin +$((24 * 60)) -type f)
- if [ -n "${old_meta}" ]; then
- lecho "Removing old incomplete metadata."
- remove_report "${meta_path}"
- else
- lecho "Ignoring recent incomplete metadata."
- fi
- continue
- fi
-
- # Ignore device coredump if device coredump uploading is not allowed.
- if [ "${kind}" = "devcore" ] && ! is_device_coredump_upload_allowed; then
- lecho "Ignoring device coredump. Device coredump upload not allowed."
- continue
- fi
-
- if ! is_mock && ! is_official_image; then
- lecho "Not an official OS version. Removing crash."
- remove_report "${meta_path}"
- continue
- fi
-
- # Don't send crash reports from previous sessions while we're in guest mode
- # to avoid the impression that crash reporting was enabled, which it isn't.
- # (Don't exit right now because subsequent reports may be candidates for
- # deletion.)
- if ${METRICS_CLIENT} -g; then
- lecho "Guest mode has been entered. Delaying crash sending until exited."
- continue
- fi
-
- # Remove existing crashes in case user consent has not (yet) been given or
- # has been revoked. This must come after the guest mode check because
- # ${METRICS_CLIENT} always returns "not consented" in guest mode.
- if ! ${METRICS_CLIENT} -c; then
- lecho "Crash reporting is disabled. Removing crash."
- remove_report "${meta_path}"
- continue
- fi
-
- # Skip report if the upload rate is exceeded. (Don't exit right now because
- # subsequent reports may be candidates for deletion.)
- if ! check_rate; then
- lecho "Sending ${meta_path} would exceed rate. Leaving for later."
- continue
- fi
-
- # The .meta file should be written *after* all to-be-uploaded files that it
- # references. Nevertheless, as a safeguard, a hold-off time of thirty
- # seconds after writing the .meta file is ensured. Also, sending of crash
- # reports is spread out randomly by up to SECONDS_SEND_SPREAD. Thus, for
- # the sleep call the greater of the two delays is used.
- local now=$(date +%s)
- local holdoff_time=$(($(stat --format=%Y "${meta_path}") + 30 - ${now}))
- local spread_time=$(generate_uniform_random "${SECONDS_SEND_SPREAD}")
- local sleep_time
- if [ ${spread_time} -gt ${holdoff_time} ]; then
- sleep_time="${spread_time}"
- else
- sleep_time="${holdoff_time}"
- fi
- lecho "Scheduled to send in ${sleep_time}s."
- if ! is_mock; then
- if ! sleep "${sleep_time}"; then
- lecho "Sleep failed"
- return 1
- fi
- fi
-
- # Try to upload.
- if ! send_crash "${meta_path}"; then
- lecho "Problem sending ${meta_path}, not removing."
- continue
- fi
-
- # Send was successful, now remove.
- lecho "Successfully sent crash ${meta_path} and removing."
- remove_report "${meta_path}"
- done
-}
-
-usage() {
- cat <<EOF
-Usage: crash_sender [options]
-
-Options:
- -e <var>=<val> Set env |var| to |val| (only some vars)
-EOF
- exit ${1:-1}
-}
-
-parseargs() {
- # Parse the command line arguments.
- while [ $# -gt 0 ]; do
- case $1 in
- -e)
- shift
- case $1 in
- FORCE_OFFICIAL=*|\
- MAX_CRASH_RATE=*|\
- MOCK_DEVELOPER_MODE=*|\
- OVERRIDE_PAUSE_SENDING=*|\
- SECONDS_SEND_SPREAD=*)
- export "$1"
- ;;
- *)
- lecho "Unknown var passed to -e: $1"
- exit 1
- ;;
- esac
- ;;
- -h)
- usage 0
- ;;
- *)
- lecho "Unknown options: $*"
- exit 1
- ;;
- esac
- shift
- done
-}
-
-main() {
- trap cleanup EXIT INT TERM
-
- parseargs "$@"
-
- if [ -e "${PAUSE_CRASH_SENDING}" ] && \
- [ ${OVERRIDE_PAUSE_SENDING} -eq 0 ]; then
- lecho "Exiting early due to ${PAUSE_CRASH_SENDING}."
- exit 1
- fi
-
- if is_test_image; then
- lecho "Exiting early due to test image."
- exit 1
- fi
-
- # We don't perform checks on this because we have a master lock with the
- # CRASH_SENDER_LOCK file. This pid file is for the system to keep track
- # (like with autotests) that we're still running.
- echo $$ > "${RUN_FILE}"
-
- for dependency in "${FIND}" "${METRICS_CLIENT}" \
- "${RESTRICTED_CERTIFICATES_PATH}"; do
- if [ ! -x "${dependency}" ]; then
- lecho "Fatal: Crash sending disabled: ${dependency} not found."
- exit 1
- fi
- done
-
- TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXXXX)"
-
- # Send system-wide crashes
- send_crashes "/var/spool/crash"
-
- # Send user-specific crashes
- local d
- for d in /home/chronos/crash /home/chronos/u-*/crash; do
- send_crashes "${d}"
- done
-}
-
-(
-if ! flock -n 9; then
- lecho "Already running; quitting."
- crash_done
- exit 1
-fi
-main "$@"
-) 9>"${CRASH_SENDER_LOCK}"
diff --git a/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml
deleted file mode 100644
index 64b8b84..0000000
--- a/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<node name="/org/chromium/LibCrosService"
- xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
- <interface name="org.chromium.LibCrosServiceInterface">
- <method name="ResolveNetworkProxy">
- <arg name="source_url" type="s" direction="in"/>
- <arg name="signal_interface" type="s" direction="in"/>
- <arg name="signal_name" type="s" direction="in"/>
- <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
- </method>
- </interface>
- <interface name="org.chromium.CrashReporterLibcrosProxyResolvedInterface">
- <signal name="ProxyResolved">
- <arg name="source_url" type="s" direction="out"/>
- <arg name="proxy_info" type="s" direction="out"/>
- <arg name="error_message" type="s" direction="out"/>
- </signal>
- </interface>
-</node>
diff --git a/crash_reporter/init/crash-reporter.conf b/crash_reporter/init/crash-reporter.conf
deleted file mode 100644
index 19f2cdb..0000000
--- a/crash_reporter/init/crash-reporter.conf
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-description "Initialize crash reporting services"
-author "chromium-os-dev@chromium.org"
-
-# This job merely initializes its service and then terminates; the
-# actual checking and reporting of crash dumps is triggered by an
-# hourly cron job.
-start on starting system-services
-
-pre-start script
- mkdir -p /var/spool
-
- # Only allow device coredumps on a "developer system".
- if ! is_developer_end_user; then
- # consumer end-user - disable device coredumps, if driver exists.
- echo 1 > /sys/class/devcoredump/disabled || true
- fi
-end script
-
-# crash_reporter uses argv[0] as part of the command line for
-# /proc/sys/kernel/core_pattern. That command line is invoked by
-# the kernel, and can't rely on PATH, so argv[0] must be a full
-# path; we invoke it as such here.
-exec /sbin/crash_reporter --init
diff --git a/crash_reporter/init/crash-sender.conf b/crash_reporter/init/crash-sender.conf
deleted file mode 100644
index 892186f..0000000
--- a/crash_reporter/init/crash-sender.conf
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-description "Run the crash sender periodically"
-author "chromium-os-dev@chromium.org"
-
-start on starting system-services
-stop on stopping system-services
-
-exec periodic_scheduler 3600 14400 crash_sender /sbin/crash_sender
diff --git a/crash_reporter/init/warn-collector.conf b/crash_reporter/init/warn-collector.conf
deleted file mode 100644
index 3be80da..0000000
--- a/crash_reporter/init/warn-collector.conf
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-description "Runs a daemon which collects and reports kernel warnings"
-author "chromium-os-dev@chromium.org"
-
-start on started system-services
-stop on stopping system-services
-respawn
-
-exec warn_collector
diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc
deleted file mode 100644
index b3cbede..0000000
--- a/crash_reporter/kernel_collector.cc
+++ /dev/null
@@ -1,591 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "kernel_collector.h"
-
-#include <map>
-#include <sys/stat.h>
-
-#include <base/files/file_util.h>
-#include <base/logging.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-
-using base::FilePath;
-using base::StringPrintf;
-
-namespace {
-
-const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature";
-const char kDumpParentPath[] = "/dev";
-const char kDumpPath[] = "/dev/pstore";
-const char kDumpFormat[] = "dmesg-ramoops-%zu";
-const char kKernelExecName[] = "kernel";
-// Maximum number of records to examine in the kDumpPath.
-const size_t kMaxDumpRecords = 100;
-const pid_t kKernelPid = 0;
-const char kKernelSignatureKey[] = "sig";
-// Byte length of maximum human readable portion of a kernel crash signature.
-const int kMaxHumanStringLength = 40;
-const uid_t kRootUid = 0;
-// Time in seconds from the final kernel log message for a call stack
-// to count towards the signature of the kcrash.
-const int kSignatureTimestampWindow = 2;
-// Kernel log timestamp regular expression.
-const char kTimestampRegex[] = "^<.*>\\[\\s*(\\d+\\.\\d+)\\]";
-
-//
-// These regular expressions enable to us capture the PC in a backtrace.
-// The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem
-// feature.
-//
-// For ARM we see:
-// "<5>[ 39.458982] PC is at write_breakme+0xd0/0x1b4"
-// For MIPS we see:
-// "<5>[ 3378.552000] epc : 804010f0 lkdtm_do_action+0x68/0x3f8"
-// For x86:
-// "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108
-// SS:ESP 0068:e9dd3efc"
-//
-const char* const kPCRegex[] = {
- 0,
- " PC is at ([^\\+ ]+).*",
- " epc\\s+:\\s+\\S+\\s+([^\\+ ]+).*", // MIPS has an exception program counter
- " EIP: \\[<.*>\\] ([^\\+ ]+).*", // X86 uses EIP for the program counter
- " RIP \\[<.*>\\] ([^\\+ ]+).*", // X86_64 uses RIP for the program counter
-};
-
-COMPILE_ASSERT(arraysize(kPCRegex) == KernelCollector::kArchCount,
- missing_arch_pc_regexp);
-
-} // namespace
-
-KernelCollector::KernelCollector()
- : is_enabled_(false),
- ramoops_dump_path_(kDumpPath),
- records_(0),
- // We expect crash dumps in the format of architecture we are built for.
- arch_(GetCompilerArch()) {
-}
-
-KernelCollector::~KernelCollector() {
-}
-
-void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) {
- ramoops_dump_path_ = file_path;
-}
-
-bool KernelCollector::ReadRecordToString(std::string *contents,
- size_t current_record,
- bool *record_found) {
- // A record is a ramoops dump. It has an associated size of "record_size".
- std::string record;
- std::string captured;
-
- // Ramoops appends a header to a crash which contains ==== followed by a
- // timestamp. Ignore the header.
- pcrecpp::RE record_re(
- "====\\d+\\.\\d+\n(.*)",
- pcrecpp::RE_Options().set_multiline(true).set_dotall(true));
-
- pcrecpp::RE sanity_check_re("\n<\\d+>\\[\\s*(\\d+\\.\\d+)\\]");
-
- FilePath ramoops_record;
- GetRamoopsRecordPath(&ramoops_record, current_record);
- if (!base::ReadFileToString(ramoops_record, &record)) {
- LOG(ERROR) << "Unable to open " << ramoops_record.value();
- return false;
- }
-
- *record_found = false;
- if (record_re.FullMatch(record, &captured)) {
- // Found a ramoops header, so strip the header and append the rest.
- contents->append(captured);
- *record_found = true;
- } else if (sanity_check_re.PartialMatch(record.substr(0, 1024))) {
- // pstore compression has been added since kernel 3.12. In order to
- // decompress dmesg correctly, ramoops driver has to strip the header
- // before handing over the record to the pstore driver, so we don't
- // need to do it here anymore. However, the sanity check is needed because
- // sometimes a pstore record is just a chunk of uninitialized memory which
- // is not the result of a kernel crash. See crbug.com/443764
- contents->append(record);
- *record_found = true;
- } else {
- LOG(WARNING) << "Found invalid record at " << ramoops_record.value();
- }
-
- // Remove the record from pstore after it's found.
- if (*record_found)
- base::DeleteFile(ramoops_record, false);
-
- return true;
-}
-
-void KernelCollector::GetRamoopsRecordPath(FilePath *path,
- size_t record) {
- // Disable error "format not a string literal, argument types not checked"
- // because this is valid, but GNU apparently doesn't bother checking a const
- // format string.
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wformat-nonliteral"
- *path = ramoops_dump_path_.Append(StringPrintf(kDumpFormat, record));
- #pragma GCC diagnostic pop
-}
-
-bool KernelCollector::LoadParameters() {
- // Discover how many ramoops records are being exported by the driver.
- size_t count;
-
- for (count = 0; count < kMaxDumpRecords; ++count) {
- FilePath ramoops_record;
- GetRamoopsRecordPath(&ramoops_record, count);
-
- if (!base::PathExists(ramoops_record))
- break;
- }
-
- records_ = count;
- return (records_ > 0);
-}
-
-bool KernelCollector::LoadPreservedDump(std::string *contents) {
- // Load dumps from the preserved memory and save them in contents.
- // Since the system is set to restart on oops we won't actually ever have
- // multiple records (only 0 or 1), but check in case we don't restart on
- // oops in the future.
- bool any_records_found = false;
- bool record_found = false;
- // clear contents since ReadFileToString actually appends to the string.
- contents->clear();
-
- for (size_t i = 0; i < records_; ++i) {
- if (!ReadRecordToString(contents, i, &record_found)) {
- break;
- }
- if (record_found) {
- any_records_found = true;
- }
- }
-
- if (!any_records_found) {
- LOG(ERROR) << "No valid records found in " << ramoops_dump_path_.value();
- return false;
- }
-
- return true;
-}
-
-void KernelCollector::StripSensitiveData(std::string *kernel_dump) {
- // Strip any data that the user might not want sent up to the crash servers.
- // We'll read in from kernel_dump and also place our output there.
- //
- // At the moment, the only sensitive data we strip is MAC addresses.
-
- // Get rid of things that look like MAC addresses, since they could possibly
- // give information about where someone has been. This is strings that look
- // like this: 11:22:33:44:55:66
- // Complications:
- // - Within a given kernel_dump, want to be able to tell when the same MAC
- // was used more than once. Thus, we'll consistently replace the first
- // MAC found with 00:00:00:00:00:01, the second with ...:02, etc.
- // - ACPI commands look like MAC addresses. We'll specifically avoid getting
- // rid of those.
- std::ostringstream result;
- std::string pre_mac_str;
- std::string mac_str;
- std::map<std::string, std::string> mac_map;
- pcrecpp::StringPiece input(*kernel_dump);
-
- // This RE will find the next MAC address and can return us the data preceding
- // the MAC and the MAC itself.
- pcrecpp::RE mac_re("(.*?)("
- "[0-9a-fA-F][0-9a-fA-F]:"
- "[0-9a-fA-F][0-9a-fA-F]:"
- "[0-9a-fA-F][0-9a-fA-F]:"
- "[0-9a-fA-F][0-9a-fA-F]:"
- "[0-9a-fA-F][0-9a-fA-F]:"
- "[0-9a-fA-F][0-9a-fA-F])",
- pcrecpp::RE_Options()
- .set_multiline(true)
- .set_dotall(true));
-
- // This RE will identify when the 'pre_mac_str' shows that the MAC address
- // was really an ACPI cmd. The full string looks like this:
- // ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES) filtered out
- pcrecpp::RE acpi_re("ACPI cmd ef/$",
- pcrecpp::RE_Options()
- .set_multiline(true)
- .set_dotall(true));
-
- // Keep consuming, building up a result string as we go.
- while (mac_re.Consume(&input, &pre_mac_str, &mac_str)) {
- if (acpi_re.PartialMatch(pre_mac_str)) {
- // We really saw an ACPI command; add to result w/ no stripping.
- result << pre_mac_str << mac_str;
- } else {
- // Found a MAC address; look up in our hash for the mapping.
- std::string replacement_mac = mac_map[mac_str];
- if (replacement_mac == "") {
- // It wasn't present, so build up a replacement string.
- int mac_id = mac_map.size();
-
- // Handle up to 2^32 unique MAC address; overkill, but doesn't hurt.
- replacement_mac = StringPrintf("00:00:%02x:%02x:%02x:%02x",
- (mac_id & 0xff000000) >> 24,
- (mac_id & 0x00ff0000) >> 16,
- (mac_id & 0x0000ff00) >> 8,
- (mac_id & 0x000000ff));
- mac_map[mac_str] = replacement_mac;
- }
-
- // Dump the string before the MAC and the fake MAC address into result.
- result << pre_mac_str << replacement_mac;
- }
- }
-
- // One last bit of data might still be in the input.
- result << input;
-
- // We'll just assign right back to kernel_dump.
- *kernel_dump = result.str();
-}
-
-bool KernelCollector::DumpDirMounted() {
- struct stat st_parent;
- if (stat(kDumpParentPath, &st_parent)) {
- PLOG(WARNING) << "Could not stat " << kDumpParentPath;
- return false;
- }
-
- struct stat st_dump;
- if (stat(kDumpPath, &st_dump)) {
- PLOG(WARNING) << "Could not stat " << kDumpPath;
- return false;
- }
-
- if (st_parent.st_dev == st_dump.st_dev) {
- LOG(WARNING) << "Dump dir " << kDumpPath << " not mounted";
- return false;
- }
-
- return true;
-}
-
-bool KernelCollector::Enable() {
- if (arch_ == kArchUnknown || arch_ >= kArchCount ||
- kPCRegex[arch_] == nullptr) {
- LOG(WARNING) << "KernelCollector does not understand this architecture";
- return false;
- }
-
- if (!DumpDirMounted()) {
- LOG(WARNING) << "Kernel does not support crash dumping";
- return false;
- }
-
- // To enable crashes, we will eventually need to set
- // the chnv bit in BIOS, but it does not yet work.
- LOG(INFO) << "Enabling kernel crash handling";
- is_enabled_ = true;
- return true;
-}
-
-// Hash a string to a number. We define our own hash function to not
-// be dependent on a C++ library that might change. This function
-// uses basically the same approach as tr1/functional_hash.h but with
-// a larger prime number (16127 vs 131).
-static unsigned HashString(const std::string &input) {
- unsigned hash = 0;
- for (size_t i = 0; i < input.length(); ++i)
- hash = hash * 16127 + input[i];
- return hash;
-}
-
-void KernelCollector::ProcessStackTrace(
- pcrecpp::StringPiece kernel_dump,
- bool print_diagnostics,
- unsigned *hash,
- float *last_stack_timestamp,
- bool *is_watchdog_crash) {
- pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE());
- pcrecpp::RE stack_trace_start_re(std::string(kTimestampRegex) +
- " (Call Trace|Backtrace):$");
-
- // Match lines such as the following and grab out "function_name".
- // The ? may or may not be present.
- //
- // For ARM:
- // <4>[ 3498.731164] [<c0057220>] ? (function_name+0x20/0x2c) from
- // [<c018062c>] (foo_bar+0xdc/0x1bc)
- //
- // For MIPS:
- // <5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8
- //
- // For X86:
- // <4>[ 6066.849504] [<7937bcee>] ? function_name+0x66/0x6c
- //
- pcrecpp::RE stack_entry_re(std::string(kTimestampRegex) +
- "\\s+\\[<[[:xdigit:]]+>\\]" // Matches " [<7937bcee>]"
- "([\\s\\?(]+)" // Matches " ? (" (ARM) or " ? " (X86)
- "([^\\+ )]+)"); // Matches until delimiter reached
- std::string line;
- std::string hashable;
- std::string previous_hashable;
- bool is_watchdog = false;
-
- *hash = 0;
- *last_stack_timestamp = 0;
-
- // Find the last and second-to-last stack traces. The latter is used when
- // the panic is from a watchdog timeout.
- while (line_re.FindAndConsume(&kernel_dump, &line)) {
- std::string certainty;
- std::string function_name;
- if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) {
- if (print_diagnostics) {
- printf("Stack trace starting.%s\n",
- hashable.empty() ? "" : " Saving prior trace.");
- }
- previous_hashable = hashable;
- hashable.clear();
- is_watchdog = false;
- } else if (stack_entry_re.PartialMatch(line,
- last_stack_timestamp,
- &certainty,
- &function_name)) {
- bool is_certain = certainty.find('?') == std::string::npos;
- if (print_diagnostics) {
- printf("@%f: stack entry for %s (%s)\n",
- *last_stack_timestamp,
- function_name.c_str(),
- is_certain ? "certain" : "uncertain");
- }
- // Do not include any uncertain (prefixed by '?') frames in our hash.
- if (!is_certain)
- continue;
- if (!hashable.empty())
- hashable.append("|");
- if (function_name == "watchdog_timer_fn" ||
- function_name == "watchdog") {
- is_watchdog = true;
- }
- hashable.append(function_name);
- }
- }
-
- // If the last stack trace contains a watchdog function we assume the panic
- // is from the watchdog timer, and we hash the previous stack trace rather
- // than the last one, assuming that the previous stack is that of the hung
- // thread.
- //
- // In addition, if the hashable is empty (meaning all frames are uncertain,
- // for whatever reason) also use the previous frame, as it cannot be any
- // worse.
- if (is_watchdog || hashable.empty()) {
- hashable = previous_hashable;
- }
-
- *hash = HashString(hashable);
- *is_watchdog_crash = is_watchdog;
-
- if (print_diagnostics) {
- printf("Hash based on stack trace: \"%s\" at %f.\n",
- hashable.c_str(), *last_stack_timestamp);
- }
-}
-
-// static
-KernelCollector::ArchKind KernelCollector::GetCompilerArch() {
-#if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY)
- return kArchArm;
-#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY)
- return kArchMips;
-#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_64)
- return kArchX86_64;
-#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
- return kArchX86;
-#else
- return kArchUnknown;
-#endif
-}
-
-bool KernelCollector::FindCrashingFunction(
- pcrecpp::StringPiece kernel_dump,
- bool print_diagnostics,
- float stack_trace_timestamp,
- std::string *crashing_function) {
- float timestamp = 0;
-
- // Use the correct regex for this architecture.
- pcrecpp::RE eip_re(std::string(kTimestampRegex) + kPCRegex[arch_],
- pcrecpp::MULTILINE());
-
- while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) {
- if (print_diagnostics) {
- printf("@%f: found crashing function %s\n",
- timestamp,
- crashing_function->c_str());
- }
- }
- if (timestamp == 0) {
- if (print_diagnostics) {
- printf("Found no crashing function.\n");
- }
- return false;
- }
- if (stack_trace_timestamp != 0 &&
- abs(static_cast<int>(stack_trace_timestamp - timestamp))
- > kSignatureTimestampWindow) {
- if (print_diagnostics) {
- printf("Found crashing function but not within window.\n");
- }
- return false;
- }
- if (print_diagnostics) {
- printf("Found crashing function %s\n", crashing_function->c_str());
- }
- return true;
-}
-
-bool KernelCollector::FindPanicMessage(pcrecpp::StringPiece kernel_dump,
- bool print_diagnostics,
- std::string *panic_message) {
- // Match lines such as the following and grab out "Fatal exception"
- // <0>[ 342.841135] Kernel panic - not syncing: Fatal exception
- pcrecpp::RE kernel_panic_re(std::string(kTimestampRegex) +
- " Kernel panic[^\\:]*\\:\\s*(.*)",
- pcrecpp::MULTILINE());
- float timestamp = 0;
- while (kernel_panic_re.FindAndConsume(&kernel_dump,
- ×tamp,
- panic_message)) {
- if (print_diagnostics) {
- printf("@%f: panic message %s\n",
- timestamp,
- panic_message->c_str());
- }
- }
- if (timestamp == 0) {
- if (print_diagnostics) {
- printf("Found no panic message.\n");
- }
- return false;
- }
- return true;
-}
-
-bool KernelCollector::ComputeKernelStackSignature(
- const std::string &kernel_dump,
- std::string *kernel_signature,
- bool print_diagnostics) {
- unsigned stack_hash = 0;
- float last_stack_timestamp = 0;
- std::string human_string;
- bool is_watchdog_crash;
-
- ProcessStackTrace(kernel_dump,
- print_diagnostics,
- &stack_hash,
- &last_stack_timestamp,
- &is_watchdog_crash);
-
- if (!FindCrashingFunction(kernel_dump,
- print_diagnostics,
- last_stack_timestamp,
- &human_string)) {
- if (!FindPanicMessage(kernel_dump, print_diagnostics, &human_string)) {
- if (print_diagnostics) {
- printf("Found no human readable string, using empty string.\n");
- }
- human_string.clear();
- }
- }
-
- if (human_string.empty() && stack_hash == 0) {
- if (print_diagnostics) {
- printf("Found neither a stack nor a human readable string, failing.\n");
- }
- return false;
- }
-
- human_string = human_string.substr(0, kMaxHumanStringLength);
- *kernel_signature = StringPrintf("%s-%s%s-%08X",
- kKernelExecName,
- (is_watchdog_crash ? "(HANG)-" : ""),
- human_string.c_str(),
- stack_hash);
- return true;
-}
-
-bool KernelCollector::Collect() {
- std::string kernel_dump;
- FilePath root_crash_directory;
-
- if (!LoadParameters()) {
- return false;
- }
- if (!LoadPreservedDump(&kernel_dump)) {
- return false;
- }
- StripSensitiveData(&kernel_dump);
- if (kernel_dump.empty()) {
- return false;
- }
- std::string signature;
- if (!ComputeKernelStackSignature(kernel_dump, &signature, false)) {
- signature = kDefaultKernelStackSignature;
- }
-
- std::string reason = "handling";
- bool feedback = true;
- if (IsDeveloperImage()) {
- reason = "developer build - always dumping";
- feedback = true;
- } else if (!is_feedback_allowed_function_()) {
- reason = "ignoring - no consent";
- feedback = false;
- }
-
- LOG(INFO) << "Received prior crash notification from "
- << "kernel (signature " << signature << ") (" << reason << ")";
-
- if (feedback) {
- count_crash_function_();
-
- if (!GetCreatedCrashDirectoryByEuid(kRootUid,
- &root_crash_directory,
- nullptr)) {
- return true;
- }
-
- std::string dump_basename =
- FormatDumpBasename(kKernelExecName, time(nullptr), kKernelPid);
- FilePath kernel_crash_path = root_crash_directory.Append(
- StringPrintf("%s.kcrash", dump_basename.c_str()));
-
- // We must use WriteNewFile instead of base::WriteFile as we
- // do not want to write with root access to a symlink that an attacker
- // might have created.
- if (WriteNewFile(kernel_crash_path,
- kernel_dump.data(),
- kernel_dump.length()) !=
- static_cast<int>(kernel_dump.length())) {
- LOG(INFO) << "Failed to write kernel dump to "
- << kernel_crash_path.value().c_str();
- return true;
- }
-
- AddCrashMetaData(kKernelSignatureKey, signature);
- WriteCrashMetaData(
- root_crash_directory.Append(
- StringPrintf("%s.meta", dump_basename.c_str())),
- kKernelExecName,
- kernel_crash_path.value());
-
- LOG(INFO) << "Stored kcrash to " << kernel_crash_path.value();
- }
-
- return true;
-}
diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h
deleted file mode 100644
index 5fc4ac2..0000000
--- a/crash_reporter/kernel_collector.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_H_
-#define CRASH_REPORTER_KERNEL_COLLECTOR_H_
-
-#include <pcrecpp.h>
-
-#include <string>
-
-#include <base/files/file_path.h>
-#include <base/macros.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-#include "crash_collector.h"
-
-// Kernel crash collector.
-class KernelCollector : public CrashCollector {
- public:
- // Enumeration to specify architecture type.
- enum ArchKind {
- kArchUnknown,
- kArchArm,
- kArchMips,
- kArchX86,
- kArchX86_64,
-
- kArchCount // Number of architectures.
- };
-
- KernelCollector();
-
- ~KernelCollector() override;
-
- void OverridePreservedDumpPath(const base::FilePath &file_path);
-
- // Enable collection.
- bool Enable();
-
- // Returns true if the kernel collection currently enabled.
- bool is_enabled() const { return is_enabled_; }
-
- // Collect any preserved kernel crash dump. Returns true if there was
- // a dump (even if there were problems storing the dump), false otherwise.
- bool Collect();
-
- // Compute a stack signature string from a kernel dump.
- bool ComputeKernelStackSignature(const std::string &kernel_dump,
- std::string *kernel_signature,
- bool print_diagnostics);
-
- // Set the architecture of the crash dumps we are looking at.
- void set_arch(ArchKind arch) { arch_ = arch; }
- ArchKind arch() const { return arch_; }
-
- private:
- friend class KernelCollectorTest;
- FRIEND_TEST(KernelCollectorTest, LoadPreservedDump);
- FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBasic);
- FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBulk);
- FRIEND_TEST(KernelCollectorTest, StripSensitiveDataSample);
- FRIEND_TEST(KernelCollectorTest, CollectOK);
-
- virtual bool DumpDirMounted();
-
- bool LoadPreservedDump(std::string *contents);
- void StripSensitiveData(std::string *kernel_dump);
-
- void GetRamoopsRecordPath(base::FilePath *path, size_t record);
- bool LoadParameters();
- bool HasMoreRecords();
-
- // Read a record to string, modified from file_utils since that didn't
- // provide a way to restrict the read length.
- // Return value indicates (only) error state:
- // * false when we get an error (can't read from dump location).
- // * true if no error occured.
- // Not finding a valid record is not an error state and is signaled by the
- // record_found output parameter.
- bool ReadRecordToString(std::string *contents,
- size_t current_record,
- bool *record_found);
-
- void ProcessStackTrace(pcrecpp::StringPiece kernel_dump,
- bool print_diagnostics,
- unsigned *hash,
- float *last_stack_timestamp,
- bool *is_watchdog_crash);
- bool FindCrashingFunction(pcrecpp::StringPiece kernel_dump,
- bool print_diagnostics,
- float stack_trace_timestamp,
- std::string *crashing_function);
- bool FindPanicMessage(pcrecpp::StringPiece kernel_dump,
- bool print_diagnostics,
- std::string *panic_message);
-
- // Returns the architecture kind for which we are built.
- static ArchKind GetCompilerArch();
-
- bool is_enabled_;
- base::FilePath ramoops_dump_path_;
- size_t records_;
-
- // The architecture of kernel dump strings we are working with.
- ArchKind arch_;
-
- DISALLOW_COPY_AND_ASSIGN(KernelCollector);
-};
-
-#endif // CRASH_REPORTER_KERNEL_COLLECTOR_H_
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
deleted file mode 100644
index a534803..0000000
--- a/crash_reporter/kernel_collector_test.cc
+++ /dev/null
@@ -1,674 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "kernel_collector_test.h"
-
-#include <unistd.h>
-
-#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
-#include <gtest/gtest.h>
-
-using base::FilePath;
-using base::StringPrintf;
-using chromeos::FindLog;
-using chromeos::GetLog;
-
-namespace {
-
-int s_crashes = 0;
-bool s_metrics = false;
-
-void CountCrash() {
- ++s_crashes;
-}
-
-bool IsMetrics() {
- return s_metrics;
-}
-
-} // namespace
-
-class KernelCollectorTest : public ::testing::Test {
- protected:
- void WriteStringToFile(const FilePath &file_path,
- const char *data) {
- ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
- }
-
- void SetUpSuccessfulCollect();
- void ComputeKernelStackSignatureCommon();
-
- const FilePath &kcrash_file() const { return test_kcrash_; }
- const FilePath &test_crash_directory() const { return test_crash_directory_; }
-
- KernelCollectorMock collector_;
-
- private:
- void SetUp() override {
- s_crashes = 0;
- s_metrics = true;
-
- EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
-
- collector_.Initialize(CountCrash, IsMetrics);
- ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
- test_kcrash_ = scoped_temp_dir_.path().Append("kcrash");
- ASSERT_TRUE(base::CreateDirectory(test_kcrash_));
- collector_.OverridePreservedDumpPath(test_kcrash_);
-
- test_kcrash_ = test_kcrash_.Append("dmesg-ramoops-0");
- ASSERT_FALSE(base::PathExists(test_kcrash_));
-
- test_crash_directory_ = scoped_temp_dir_.path().Append("crash_directory");
- ASSERT_TRUE(base::CreateDirectory(test_crash_directory_));
- chromeos::ClearLog();
- }
-
- FilePath test_kcrash_;
- FilePath test_crash_directory_;
- base::ScopedTempDir scoped_temp_dir_;
-};
-
-TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) {
- // Make sure the normal build architecture is detected
- EXPECT_NE(KernelCollector::kArchUnknown, collector_.arch());
-}
-
-TEST_F(KernelCollectorTest, LoadPreservedDump) {
- ASSERT_FALSE(base::PathExists(kcrash_file()));
- std::string dump;
- dump.clear();
-
- WriteStringToFile(kcrash_file(),
- "CrashRecordWithoutRamoopsHeader\n<6>[ 0.078852]");
- ASSERT_TRUE(collector_.LoadParameters());
- ASSERT_TRUE(collector_.LoadPreservedDump(&dump));
- ASSERT_EQ("CrashRecordWithoutRamoopsHeader\n<6>[ 0.078852]", dump);
-
- WriteStringToFile(kcrash_file(), "====1.1\nsomething");
- ASSERT_TRUE(collector_.LoadParameters());
- ASSERT_TRUE(collector_.LoadPreservedDump(&dump));
- ASSERT_EQ("something", dump);
-
- WriteStringToFile(kcrash_file(), "\x01\x02\xfe\xff random blob");
- ASSERT_TRUE(collector_.LoadParameters());
- ASSERT_FALSE(collector_.LoadPreservedDump(&dump));
- ASSERT_EQ("", dump);
-}
-
-TEST_F(KernelCollectorTest, EnableMissingKernel) {
- ASSERT_FALSE(collector_.Enable());
- ASSERT_FALSE(collector_.is_enabled());
- ASSERT_TRUE(FindLog(
- "Kernel does not support crash dumping"));
- ASSERT_EQ(s_crashes, 0);
-}
-
-TEST_F(KernelCollectorTest, EnableOK) {
- WriteStringToFile(kcrash_file(), "");
- EXPECT_CALL(collector_, DumpDirMounted()).WillOnce(::testing::Return(true));
- ASSERT_TRUE(collector_.Enable());
- ASSERT_TRUE(collector_.is_enabled());
- ASSERT_TRUE(FindLog("Enabling kernel crash handling"));
- ASSERT_EQ(s_crashes, 0);
-}
-
-TEST_F(KernelCollectorTest, StripSensitiveDataBasic) {
- // Basic tests of StripSensitiveData...
-
- // Make sure we work OK with a string w/ no MAC addresses.
- const std::string kCrashWithNoMacsOrig =
- "<7>[111566.131728] PM: Entering mem sleep\n";
- std::string crash_with_no_macs(kCrashWithNoMacsOrig);
- collector_.StripSensitiveData(&crash_with_no_macs);
- EXPECT_EQ(kCrashWithNoMacsOrig, crash_with_no_macs);
-
- // Make sure that we handle the case where there's nothing before/after the
- // MAC address.
- const std::string kJustAMacOrig =
- "11:22:33:44:55:66";
- const std::string kJustAMacStripped =
- "00:00:00:00:00:01";
- std::string just_a_mac(kJustAMacOrig);
- collector_.StripSensitiveData(&just_a_mac);
- EXPECT_EQ(kJustAMacStripped, just_a_mac);
-
- // Test MAC addresses crammed together to make sure it gets both of them.
- //
- // I'm not sure that the code does ideal on these two test cases (they don't
- // look like two MAC addresses to me), but since we don't see them I think
- // it's OK to behave as shown here.
- const std::string kCrammedMacs1Orig =
- "11:22:33:44:55:66:11:22:33:44:55:66";
- const std::string kCrammedMacs1Stripped =
- "00:00:00:00:00:01:00:00:00:00:00:01";
- std::string crammed_macs_1(kCrammedMacs1Orig);
- collector_.StripSensitiveData(&crammed_macs_1);
- EXPECT_EQ(kCrammedMacs1Stripped, crammed_macs_1);
-
- const std::string kCrammedMacs2Orig =
- "11:22:33:44:55:6611:22:33:44:55:66";
- const std::string kCrammedMacs2Stripped =
- "00:00:00:00:00:0100:00:00:00:00:01";
- std::string crammed_macs_2(kCrammedMacs2Orig);
- collector_.StripSensitiveData(&crammed_macs_2);
- EXPECT_EQ(kCrammedMacs2Stripped, crammed_macs_2);
-
- // Test case-sensitiveness (we shouldn't be case-senstive).
- const std::string kCapsMacOrig =
- "AA:BB:CC:DD:EE:FF";
- const std::string kCapsMacStripped =
- "00:00:00:00:00:01";
- std::string caps_mac(kCapsMacOrig);
- collector_.StripSensitiveData(&caps_mac);
- EXPECT_EQ(kCapsMacStripped, caps_mac);
-
- const std::string kLowerMacOrig =
- "aa:bb:cc:dd:ee:ff";
- const std::string kLowerMacStripped =
- "00:00:00:00:00:01";
- std::string lower_mac(kLowerMacOrig);
- collector_.StripSensitiveData(&lower_mac);
- EXPECT_EQ(kLowerMacStripped, lower_mac);
-}
-
-TEST_F(KernelCollectorTest, StripSensitiveDataBulk) {
- // Test calling StripSensitiveData w/ lots of MAC addresses in the "log".
-
- // Test that stripping code handles more than 256 unique MAC addresses, since
- // that overflows past the last byte...
- // We'll write up some code that generates 258 unique MAC addresses. Sorta
- // cheating since the code is very similar to the current code in
- // StripSensitiveData(), but would catch if someone changed that later.
- std::string lotsa_macs_orig;
- std::string lotsa_macs_stripped;
- int i;
- for (i = 0; i < 258; i++) {
- lotsa_macs_orig += StringPrintf(" 11:11:11:11:%02X:%02x",
- (i & 0xff00) >> 8, i & 0x00ff);
- lotsa_macs_stripped += StringPrintf(" 00:00:00:00:%02X:%02x",
- ((i+1) & 0xff00) >> 8, (i+1) & 0x00ff);
- }
- std::string lotsa_macs(lotsa_macs_orig);
- collector_.StripSensitiveData(&lotsa_macs);
- EXPECT_EQ(lotsa_macs_stripped, lotsa_macs);
-}
-
-TEST_F(KernelCollectorTest, StripSensitiveDataSample) {
- // Test calling StripSensitiveData w/ some actual lines from a real crash;
- // included two MAC addresses (though replaced them with some bogusness).
- const std::string kCrashWithMacsOrig =
- "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
- " filtered out\n"
- "<7>[108539.540144] wlan0: authenticate with 11:22:33:44:55:66 (try 1)\n"
- "<7>[108539.554973] wlan0: associate with 11:22:33:44:55:66 (try 1)\n"
- "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
- " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
- "<7>[110964.314648] wlan0: deauthenticated from 11:22:33:44:55:66"
- " (Reason: 6)\n"
- "<7>[110964.325057] phy0: Removed STA 11:22:33:44:55:66\n"
- "<7>[110964.325115] phy0: Destroyed STA 11:22:33:44:55:66\n"
- "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
- " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
- "<7>[111566.131728] PM: Entering mem sleep\n";
- const std::string kCrashWithMacsStripped =
- "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
- " filtered out\n"
- "<7>[108539.540144] wlan0: authenticate with 00:00:00:00:00:01 (try 1)\n"
- "<7>[108539.554973] wlan0: associate with 00:00:00:00:00:01 (try 1)\n"
- "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
- " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
- "<7>[110964.314648] wlan0: deauthenticated from 00:00:00:00:00:01"
- " (Reason: 6)\n"
- "<7>[110964.325057] phy0: Removed STA 00:00:00:00:00:01\n"
- "<7>[110964.325115] phy0: Destroyed STA 00:00:00:00:00:01\n"
- "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
- " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
- "<7>[111566.131728] PM: Entering mem sleep\n";
- std::string crash_with_macs(kCrashWithMacsOrig);
- collector_.StripSensitiveData(&crash_with_macs);
- EXPECT_EQ(kCrashWithMacsStripped, crash_with_macs);
-}
-
-TEST_F(KernelCollectorTest, CollectPreservedFileMissing) {
- ASSERT_FALSE(collector_.Collect());
- ASSERT_FALSE(FindLog("Stored kcrash to "));
- ASSERT_EQ(0, s_crashes);
-}
-
-TEST_F(KernelCollectorTest, CollectBadDirectory) {
- WriteStringToFile(kcrash_file(), "====1.1\nsomething");
- ASSERT_TRUE(collector_.Collect());
- ASSERT_TRUE(FindLog("Unable to create appropriate crash directory"))
- << "Did not find expected error string in log: {\n"
- << GetLog() << "}";
- ASSERT_EQ(1, s_crashes);
-}
-
-void KernelCollectorTest::SetUpSuccessfulCollect() {
- collector_.ForceCrashDirectory(test_crash_directory());
- WriteStringToFile(kcrash_file(), "====1.1\nsomething");
- ASSERT_EQ(0, s_crashes);
-}
-
-TEST_F(KernelCollectorTest, CollectOptedOut) {
- SetUpSuccessfulCollect();
- s_metrics = false;
- ASSERT_TRUE(collector_.Collect());
- ASSERT_TRUE(FindLog("(ignoring - no consent)"));
- ASSERT_EQ(0, s_crashes);
-}
-
-TEST_F(KernelCollectorTest, CollectOK) {
- SetUpSuccessfulCollect();
- ASSERT_TRUE(collector_.Collect());
- ASSERT_EQ(1, s_crashes);
- ASSERT_TRUE(FindLog("(handling)"));
- static const char kNamePrefix[] = "Stored kcrash to ";
- std::string log = chromeos::GetLog();
- size_t pos = log.find(kNamePrefix);
- ASSERT_NE(std::string::npos, pos)
- << "Did not find string \"" << kNamePrefix << "\" in log: {\n"
- << log << "}";
- pos += strlen(kNamePrefix);
- std::string filename = log.substr(pos, std::string::npos);
- // Take the name up until \n
- size_t end_pos = filename.find_first_of("\n");
- ASSERT_NE(std::string::npos, end_pos);
- filename = filename.substr(0, end_pos);
- ASSERT_EQ(0, filename.find(test_crash_directory().value()));
- ASSERT_TRUE(base::PathExists(FilePath(filename)));
- std::string contents;
- ASSERT_TRUE(base::ReadFileToString(FilePath(filename), &contents));
- ASSERT_EQ("something", contents);
-}
-
-// Perform tests which are common across architectures
-void KernelCollectorTest::ComputeKernelStackSignatureCommon() {
- std::string signature;
-
- const char kStackButNoPC[] =
- "<4>[ 6066.829029] [<790340af>] __do_softirq+0xa6/0x143\n";
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kStackButNoPC, &signature, false));
- EXPECT_EQ("kernel--83615F0A", signature);
-
- const char kMissingEverything[] =
- "<4>[ 6066.829029] [<790340af>] ? __do_softirq+0xa6/0x143\n";
- EXPECT_FALSE(
- collector_.ComputeKernelStackSignature(kMissingEverything,
- &signature,
- false));
-
- // Long message.
- const char kTruncatedMessage[] =
- "<0>[ 87.485611] Kernel panic - not syncing: 01234567890123456789"
- "01234567890123456789X\n";
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kTruncatedMessage,
- &signature,
- false));
- EXPECT_EQ("kernel-0123456789012345678901234567890123456789-00000000",
- signature);
-}
-
-TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) {
- const char kBugToPanic[] =
- "<5>[ 123.412524] Modules linked in:\n"
- "<5>[ 123.412534] CPU: 0 Tainted: G W "
- "(2.6.37-01030-g51cee64 #153)\n"
- "<5>[ 123.412552] PC is at write_breakme+0xd0/0x1b4\n"
- "<5>[ 123.412560] LR is at write_breakme+0xc8/0x1b4\n"
- "<5>[ 123.412569] pc : [<c0058220>] lr : [<c005821c>] "
- "psr: 60000013\n"
- "<5>[ 123.412574] sp : f4e0ded8 ip : c04d104c fp : 000e45e0\n"
- "<5>[ 123.412581] r10: 400ff000 r9 : f4e0c000 r8 : 00000004\n"
- "<5>[ 123.412589] r7 : f4e0df80 r6 : f4820c80 r5 : 00000004 "
- "r4 : f4e0dee8\n"
- "<5>[ 123.412598] r3 : 00000000 r2 : f4e0decc r1 : c05f88a9 "
- "r0 : 00000039\n"
- "<5>[ 123.412608] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA "
- "ARM Segment user\n"
- "<5>[ 123.412617] Control: 10c53c7d Table: 34dcc04a DAC: 00000015\n"
- "<0>[ 123.412626] Process bash (pid: 1014, stack limit = 0xf4e0c2f8)\n"
- "<0>[ 123.412634] Stack: (0xf4e0ded8 to 0xf4e0e000)\n"
- "<0>[ 123.412641] dec0: "
- " f4e0dee8 c0183678\n"
- "<0>[ 123.412654] dee0: 00000000 00000000 00677562 0000081f c06a6a78 "
- "400ff000 f4e0dfb0 00000000\n"
- "<0>[ 123.412666] df00: bec7ab44 000b1719 bec7ab0c c004f498 bec7a314 "
- "c024acc8 00000001 c018359c\n"
- "<0>[ 123.412679] df20: f4e0df34 c04d10fc f5803c80 271beb39 000e45e0 "
- "f5803c80 c018359c c017bfe0\n"
- "<0>[ 123.412691] df40: 00000004 f4820c80 400ff000 f4e0df80 00000004 "
- "f4e0c000 00000000 c01383e4\n"
- "<0>[ 123.412703] df60: f4820c80 400ff000 f4820c80 400ff000 00000000 "
- "00000000 00000004 c0138578\n"
- "<0>[ 123.412715] df80: 00000000 00000000 00000004 00000000 00000004 "
- "402f95d0 00000004 00000004\n"
- "<0>[ 123.412727] dfa0: c0054984 c00547c0 00000004 402f95d0 00000001 "
- "400ff000 00000004 00000000\n"
- "<0>[ 123.412739] dfc0: 00000004 402f95d0 00000004 00000004 400ff000 "
- "000c194c bec7ab58 000e45e0\n"
- "<0>[ 123.412751] dfe0: 00000000 bec7aad8 40232520 40284e9c 60000010 "
- "00000001 00000000 00000000\n"
- "<5>[ 39.496577] Backtrace:\n"
- "<5>[ 123.412782] [<c0058220>] (__bug+0x20/0x2c) from [<c0183678>] "
- "(write_breakme+0xdc/0x1bc)\n"
- "<5>[ 123.412798] [<c0183678>] (write_breakme+0xdc/0x1bc) from "
- "[<c017bfe0>] (proc_reg_write+0x88/0x9c)\n";
- std::string signature;
-
- collector_.set_arch(KernelCollector::kArchArm);
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
- EXPECT_EQ("kernel-write_breakme-97D3E92F", signature);
-
- ComputeKernelStackSignatureCommon();
-}
-
-TEST_F(KernelCollectorTest, ComputeKernelStackSignatureMIPS) {
- const char kBugToPanic[] =
- "<5>[ 3378.472000] lkdtm: Performing direct entry BUG\n"
- "<5>[ 3378.476000] Kernel bug detected[#1]:\n"
- "<5>[ 3378.484000] CPU: 0 PID: 185 Comm: dash Not tainted 3.14.0 #1\n"
- "<5>[ 3378.488000] task: 8fed5220 ti: 8ec4a000 task.ti: 8ec4a000\n"
- "<5>[ 3378.496000] $ 0 : 00000000 804018b8 804010f0 7785b507\n"
- "<5>[ 3378.500000] $ 4 : 8061ab64 81204478 81205b20 00000000\n"
- "<5>[ 3378.508000] $ 8 : 80830000 20746365 72746e65 55422079\n"
- "<5>[ 3378.512000] $12 : 8ec4be94 000000fc 00000000 00000048\n"
- "<5>[ 3378.520000] $16 : 00000004 8ef54000 80710000 00000002\n"
- "<5>[ 3378.528000] $20 : 7765b6d4 00000004 7fffffff 00000002\n"
- "<5>[ 3378.532000] $24 : 00000001 803dc0dc \n"
- "<5>[ 3378.540000] $28 : 8ec4a000 8ec4be20 7775438d 804018b8\n"
- "<5>[ 3378.544000] Hi : 00000000\n"
- "<5>[ 3378.548000] Lo : 49bf8080\n"
- "<5>[ 3378.552000] epc : 804010f0 lkdtm_do_action+0x68/0x3f8\n"
- "<5>[ 3378.560000] Not tainted\n"
- "<5>[ 3378.564000] ra : 804018b8 direct_entry+0x110/0x154\n"
- "<5>[ 3378.568000] Status: 3100dc03 KERNEL EXL IE \n"
- "<5>[ 3378.572000] Cause : 10800024\n"
- "<5>[ 3378.576000] PrId : 0001a120 (MIPS interAptiv (multi))\n"
- "<5>[ 3378.580000] Modules linked in: uinput cfg80211 nf_conntrack_ipv6 "
- "nf_defrag_ipv6 ip6table_filter ip6_tables pcnet32 mii fuse "
- "ppp_async ppp_generic slhc tun\n"
- "<5>[ 3378.600000] Process dash (pid: 185, threadinfo=8ec4a000, "
- "task=8fed5220, tls=77632490)\n"
- "<5>[ 3378.608000] Stack : 00000006 ffffff9c 00000000 00000000 00000000 "
- "00000000 8083454a 00000022\n"
- "<5> 7765baa1 00001fee 80710000 8ef54000 8ec4bf08 00000002 "
- "7765b6d4 00000004\n"
- "<5> 7fffffff 00000002 7775438d 805e5158 7fffffff 00000002 "
- "00000000 7785b507\n"
- "<5> 806a96bc 00000004 8ef54000 8ec4bf08 00000002 804018b8 "
- "80710000 806a98bc\n"
- "<5> 00000002 00000020 00000004 8d515600 77756450 00000004 "
- "8ec4bf08 802377e4\n"
- "<5> ...\n"
- "<5>[ 3378.652000] Call Trace:\n"
- "<5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8\n"
- "<5>[ 3378.660000] [<804018b8>] direct_entry+0x110/0x154\n"
- "<5>[ 3378.664000] [<802377e4>] vfs_write+0xe0/0x1bc\n"
- "<5>[ 3378.672000] [<80237f90>] SyS_write+0x78/0xf8\n"
- "<5>[ 3378.676000] [<80111888>] handle_sys+0x128/0x14c\n"
- "<5>[ 3378.680000] \n"
- "<5>[ 3378.684000] \n"
- "<5>Code: 3c04806b 0c1793aa 248494f0 <000c000d> 3c04806b 248494fc "
- "0c04cc7f 2405017a 08100514 \n"
- "<5>[ 3378.696000] ---[ end trace 75067432f24bbc93 ]---\n";
- std::string signature;
-
- collector_.set_arch(KernelCollector::kArchMips);
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
- EXPECT_EQ("kernel-lkdtm_do_action-5E600A6B", signature);
-
- ComputeKernelStackSignatureCommon();
-}
-
-TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) {
- const char kBugToPanic[] =
- "<4>[ 6066.829029] [<79039d16>] ? run_timer_softirq+0x165/0x1e6\n"
- "<4>[ 6066.829029] [<790340af>] ignore_old_stack+0xa6/0x143\n"
- "<0>[ 6066.829029] EIP: [<b82d7c15>] ieee80211_stop_tx_ba_session+"
- "0xa3/0xb5 [mac80211] SS:ESP 0068:7951febc\n"
- "<0>[ 6066.829029] CR2: 00000000323038a7\n"
- "<4>[ 6066.845422] ---[ end trace 12b058bb46c43500 ]---\n"
- "<0>[ 6066.845747] Kernel panic - not syncing: Fatal exception "
- "in interrupt\n"
- "<0>[ 6066.846902] Call Trace:\n"
- "<4>[ 6066.846902] [<7937a07b>] ? printk+0x14/0x19\n"
- "<4>[ 6066.949779] [<79379fc1>] panic+0x3e/0xe4\n"
- "<4>[ 6066.949971] [<7937c5c5>] oops_end+0x73/0x81\n"
- "<4>[ 6066.950208] [<7901b260>] no_context+0x10d/0x117\n";
- std::string signature;
-
- collector_.set_arch(KernelCollector::kArchX86);
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
- EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-DE253569", signature);
-
- const char kPCButNoStack[] =
- "<0>[ 6066.829029] EIP: [<b82d7c15>] ieee80211_stop_tx_ba_session+";
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kPCButNoStack, &signature, false));
- EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-00000000", signature);
-
- const char kBreakmeBug[] =
- "<4>[ 180.492137] [<790970c6>] ? handle_mm_fault+0x67f/0x96d\n"
- "<4>[ 180.492137] [<790dcdfe>] ? proc_reg_write+0x5f/0x73\n"
- "<4>[ 180.492137] [<790e2224>] ? write_breakme+0x0/0x108\n"
- "<4>[ 180.492137] [<790dcd9f>] ? proc_reg_write+0x0/0x73\n"
- "<4>[ 180.492137] [<790ac0aa>] vfs_write+0x85/0xe4\n"
- "<0>[ 180.492137] Code: c6 44 05 b2 00 89 d8 e8 0c ef 09 00 85 c0 75 "
- "0b c7 00 00 00 00 00 e9 8e 00 00 00 ba e6 75 4b 79 89 d8 e8 f1 ee 09 "
- "00 85 c0 75 04 <0f> 0b eb fe ba 58 47 49 79 89 d8 e8 dd ee 09 00 85 "
- "c0 75 0a 68\n"
- "<0>[ 180.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP "
- "0068:aa3e9efc\n"
- "<4>[ 180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n"
- "<0>[ 180.502026] Kernel panic - not syncing: Fatal exception\n"
- "<4>[ 180.502026] Call Trace:\n"
- "<4>[ 180.502806] [<79379aba>] ? printk+0x14/0x1a\n"
- "<4>[ 180.503033] [<79379a00>] panic+0x3e/0xe4\n"
- "<4>[ 180.503287] [<7937c005>] oops_end+0x73/0x81\n"
- "<4>[ 180.503520] [<790055dd>] die+0x58/0x5e\n"
- "<4>[ 180.503538] [<7937b96c>] do_trap+0x8e/0xa7\n"
- "<4>[ 180.503555] [<79003d70>] ? do_invalid_op+0x0/0x80\n";
-
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kBreakmeBug, &signature, false));
- EXPECT_EQ("kernel-write_breakme-122AB3CD", signature);
-
- const char kPCLineTooOld[] =
- "<4>[ 174.492137] [<790970c6>] ignored_function+0x67f/0x96d\n"
- "<4>[ 175.492137] [<790970c6>] ignored_function2+0x67f/0x96d\n"
- "<0>[ 174.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP "
- "0068:aa3e9efc\n"
- "<4>[ 180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n"
- "<4>[ 180.502026] Call Trace:\n"
- "<0>[ 180.502026] Kernel panic - not syncing: Fatal exception\n"
- "<4>[ 180.502806] [<79379aba>] printk+0x14/0x1a\n";
-
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kPCLineTooOld, &signature, false));
- EXPECT_EQ("kernel-Fatal exception-ED4C84FE", signature);
-
- // Panic without EIP line.
- const char kExamplePanicOnly[] =
- "<0>[ 87.485611] Kernel panic - not syncing: Testing panic\n"
- "<4>[ 87.485630] Pid: 2825, comm: bash Tainted: G "
- "C 2.6.32.23+drm33.10 #1\n"
- "<4>[ 87.485639] Call Trace:\n"
- "<4>[ 87.485660] [<8133f71d>] ? printk+0x14/0x17\n"
- "<4>[ 87.485674] [<8133f663>] panic+0x3e/0xe4\n"
- "<4>[ 87.485689] [<810d062e>] write_breakme+0xaa/0x124\n";
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kExamplePanicOnly,
- &signature,
- false));
- EXPECT_EQ("kernel-Testing panic-E0FC3552", signature);
-
- // Panic from hung task.
- const char kHungTaskBreakMe[] =
- "<3>[ 720.459157] INFO: task bash:2287 blocked blah blah\n"
- "<5>[ 720.459282] Call Trace:\n"
- "<5>[ 720.459307] [<810a457b>] ? __dentry_open+0x186/0x23e\n"
- "<5>[ 720.459323] [<810b9c71>] ? mntput_no_expire+0x29/0xe2\n"
- "<5>[ 720.459336] [<810b9d48>] ? mntput+0x1e/0x20\n"
- "<5>[ 720.459350] [<810ad135>] ? path_put+0x1a/0x1d\n"
- "<5>[ 720.459366] [<8137cacc>] schedule+0x4d/0x4f\n"
- "<5>[ 720.459379] [<8137ccfb>] schedule_timeout+0x26/0xaf\n"
- "<5>[ 720.459394] [<8102127e>] ? should_resched+0xd/0x27\n"
- "<5>[ 720.459409] [<81174d1f>] ? _copy_from_user+0x3c/0x50\n"
- "<5>[ 720.459423] [<8137cd9e>] "
- "schedule_timeout_uninterruptible+0x1a/0x1c\n"
- "<5>[ 720.459438] [<810dee63>] write_breakme+0xb3/0x178\n"
- "<5>[ 720.459453] [<810dedb0>] ? meminfo_proc_show+0x2f2/0x2f2\n"
- "<5>[ 720.459467] [<810d94ae>] proc_reg_write+0x6d/0x87\n"
- "<5>[ 720.459481] [<810d9441>] ? proc_reg_poll+0x76/0x76\n"
- "<5>[ 720.459493] [<810a5e9e>] vfs_write+0x79/0xa5\n"
- "<5>[ 720.459505] [<810a6011>] sys_write+0x40/0x65\n"
- "<5>[ 720.459519] [<8137e677>] sysenter_do_call+0x12/0x26\n"
- "<0>[ 720.459530] Kernel panic - not syncing: hung_task: blocked tasks\n"
- "<5>[ 720.459768] Pid: 31, comm: khungtaskd Tainted: "
- "G C 3.0.8 #1\n"
- "<5>[ 720.459998] Call Trace:\n"
- "<5>[ 720.460140] [<81378a35>] panic+0x53/0x14a\n"
- "<5>[ 720.460312] [<8105f875>] watchdog+0x15b/0x1a0\n"
- "<5>[ 720.460495] [<8105f71a>] ? hung_task_panic+0x16/0x16\n"
- "<5>[ 720.460693] [<81043af3>] kthread+0x67/0x6c\n"
- "<5>[ 720.460862] [<81043a8c>] ? __init_kthread_worker+0x2d/0x2d\n"
- "<5>[ 720.461106] [<8137eb9e>] kernel_thread_helper+0x6/0x10\n";
-
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kHungTaskBreakMe,
- &signature,
- false));
-
- EXPECT_EQ("kernel-(HANG)-hung_task: blocked tasks-600B37EA", signature);
-
- // Panic with all question marks in the last stack trace.
- const char kUncertainStackTrace[] =
- "<0>[56279.689669] ------------[ cut here ]------------\n"
- "<2>[56279.689677] kernel BUG at /build/x86-alex/tmp/portage/"
- "sys-kernel/chromeos-kernel-0.0.1-r516/work/chromeos-kernel-0.0.1/"
- "kernel/timer.c:844!\n"
- "<0>[56279.689683] invalid opcode: 0000 [#1] SMP \n"
- "<0>[56279.689688] last sysfs file: /sys/power/state\n"
- "<5>[56279.689692] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat "
- "gobi usbnet tsl2583(C) industrialio(C) snd_hda_codec_realtek "
- "snd_hda_intel i2c_dev snd_hda_codec snd_hwdep qcserial snd_pcm usb_wwan "
- "i2c_i801 snd_timer nm10_gpio snd_page_alloc rtc_cmos fuse "
- "nf_conntrack_ipv6 nf_defrag_ipv6 uvcvideo videodev ip6table_filter "
- "ath9k ip6_tables ipv6 mac80211 ath9k_common ath9k_hw ath cfg80211 "
- "xt_mark\n"
- "<5>[56279.689731] \n"
- "<5>[56279.689738] Pid: 24607, comm: powerd_suspend Tainted: G "
- "WC 2.6.38.3+ #1 SAMSUNG ELECTRONICS CO., LTD. Alex/G100 \n"
- "<5>[56279.689748] EIP: 0060:[<8103e3ea>] EFLAGS: 00210286 CPU: 3\n"
- "<5>[56279.689758] EIP is at add_timer+0xd/0x1b\n"
- "<5>[56279.689762] EAX: f5e00684 EBX: f5e003c0 ECX: 00000002 EDX: "
- "00200246\n"
- "<5>[56279.689767] ESI: f5e003c0 EDI: d28bc03c EBP: d2be5e40 ESP: "
- "d2be5e40\n"
- "<5>[56279.689772] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068\n"
- "<0>[56279.689778] Process powerd_suspend (pid: 24607, ti=d2be4000 "
- "task=f5dc9b60 task.ti=d2be4000)\n"
- "<0>[56279.689782] Stack:\n"
- "<5>[56279.689785] d2be5e4c f8dccced f4ac02c0 d2be5e70 f8ddc752 "
- "f5e003c0 f4ac0458 f4ac092c\n"
- "<5>[56279.689797] f4ac043c f4ac02c0 f4ac0000 f4ac007c d2be5e7c "
- "f8dd4a33 f4ac0164 d2be5e94\n"
- "<5>[56279.689809] f87e0304 f69ff0cc f4ac0164 f87e02a4 f4ac0164 "
- "d2be5eb0 81248968 00000000\n"
- "<0>[56279.689821] Call Trace:\n"
- "<5>[56279.689840] [<f8dccced>] ieee80211_sta_restart+0x25/0x8c "
- "[mac80211]\n"
- "<5>[56279.689854] [<f8ddc752>] ieee80211_reconfig+0x2e9/0x339 "
- "[mac80211]\n"
- "<5>[56279.689869] [<f8dd4a33>] ieee80211_aes_cmac+0x182d/0x184e "
- "[mac80211]\n"
- "<5>[56279.689883] [<f87e0304>] cfg80211_get_dev_from_info+0x29b/0x2c0 "
- "[cfg80211]\n"
- "<5>[56279.689895] [<f87e02a4>] ? "
- "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n"
- "<5>[56279.689904] [<81248968>] legacy_resume+0x25/0x5d\n"
- "<5>[56279.689910] [<812490ae>] device_resume+0xdd/0x110\n"
- "<5>[56279.689917] [<812491c2>] dpm_resume_end+0xe1/0x271\n"
- "<5>[56279.689925] [<81060481>] suspend_devices_and_enter+0x18b/0x1de\n"
- "<5>[56279.689932] [<810605ba>] enter_state+0xe6/0x132\n"
- "<5>[56279.689939] [<8105fd4b>] state_store+0x91/0x9d\n"
- "<5>[56279.689945] [<8105fcba>] ? state_store+0x0/0x9d\n"
- "<5>[56279.689953] [<81178fb1>] kobj_attr_store+0x16/0x22\n"
- "<5>[56279.689961] [<810eea5e>] sysfs_write_file+0xc1/0xec\n"
- "<5>[56279.689969] [<810af443>] vfs_write+0x8f/0x101\n"
- "<5>[56279.689975] [<810ee99d>] ? sysfs_write_file+0x0/0xec\n"
- "<5>[56279.689982] [<810af556>] sys_write+0x40/0x65\n"
- "<5>[56279.689989] [<81002d57>] sysenter_do_call+0x12/0x26\n"
- "<0>[56279.689993] Code: c1 d3 e2 4a 89 55 f4 f7 d2 21 f2 6a 00 31 c9 89 "
- "d8 e8 6e fd ff ff 5a 8d 65 f8 5b 5e 5d c3 55 89 e5 3e 8d 74 26 00 83 38 "
- "00 74 04 <0f> 0b eb fe 8b 50 08 e8 6f ff ff ff 5d c3 55 89 e5 3e 8d 74 "
- "26 \n"
- "<0>[56279.690009] EIP: [<8103e3ea>] add_timer+0xd/0x1b SS:ESP "
- "0068:d2be5e40\n"
- "<4>[56279.690113] ---[ end trace b71141bb67c6032a ]---\n"
- "<7>[56279.694069] wlan0: deauthenticated from 00:00:00:00:00:01 "
- "(Reason: 6)\n"
- "<0>[56279.703465] Kernel panic - not syncing: Fatal exception\n"
- "<5>[56279.703471] Pid: 24607, comm: powerd_suspend Tainted: G D "
- "WC 2.6.38.3+ #1\n"
- "<5>[56279.703475] Call Trace:\n"
- "<5>[56279.703483] [<8136648c>] ? panic+0x55/0x152\n"
- "<5>[56279.703491] [<810057fa>] ? oops_end+0x73/0x81\n"
- "<5>[56279.703497] [<81005a44>] ? die+0xed/0xf5\n"
- "<5>[56279.703503] [<810033cb>] ? do_trap+0x7a/0x80\n"
- "<5>[56279.703509] [<8100369b>] ? do_invalid_op+0x0/0x80\n"
- "<5>[56279.703515] [<81003711>] ? do_invalid_op+0x76/0x80\n"
- "<5>[56279.703522] [<8103e3ea>] ? add_timer+0xd/0x1b\n"
- "<5>[56279.703529] [<81025e23>] ? check_preempt_curr+0x2e/0x69\n"
- "<5>[56279.703536] [<8102ef28>] ? ttwu_post_activation+0x5a/0x11b\n"
- "<5>[56279.703543] [<8102fa8d>] ? try_to_wake_up+0x213/0x21d\n"
- "<5>[56279.703550] [<81368b7f>] ? error_code+0x67/0x6c\n"
- "<5>[56279.703557] [<8103e3ea>] ? add_timer+0xd/0x1b\n"
- "<5>[56279.703577] [<f8dccced>] ? ieee80211_sta_restart+0x25/0x8c "
- "[mac80211]\n"
- "<5>[56279.703591] [<f8ddc752>] ? ieee80211_reconfig+0x2e9/0x339 "
- "[mac80211]\n"
- "<5>[56279.703605] [<f8dd4a33>] ? ieee80211_aes_cmac+0x182d/0x184e "
- "[mac80211]\n"
- "<5>[56279.703618] [<f87e0304>] ? "
- "cfg80211_get_dev_from_info+0x29b/0x2c0 [cfg80211]\n"
- "<5>[56279.703630] [<f87e02a4>] ? "
- "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n"
- "<5>[56279.703637] [<81248968>] ? legacy_resume+0x25/0x5d\n"
- "<5>[56279.703643] [<812490ae>] ? device_resume+0xdd/0x110\n"
- "<5>[56279.703649] [<812491c2>] ? dpm_resume_end+0xe1/0x271\n"
- "<5>[56279.703657] [<81060481>] ? "
- "suspend_devices_and_enter+0x18b/0x1de\n"
- "<5>[56279.703663] [<810605ba>] ? enter_state+0xe6/0x132\n"
- "<5>[56279.703670] [<8105fd4b>] ? state_store+0x91/0x9d\n"
- "<5>[56279.703676] [<8105fcba>] ? state_store+0x0/0x9d\n"
- "<5>[56279.703683] [<81178fb1>] ? kobj_attr_store+0x16/0x22\n"
- "<5>[56279.703690] [<810eea5e>] ? sysfs_write_file+0xc1/0xec\n"
- "<5>[56279.703697] [<810af443>] ? vfs_write+0x8f/0x101\n"
- "<5>[56279.703703] [<810ee99d>] ? sysfs_write_file+0x0/0xec\n"
- "<5>[56279.703709] [<810af556>] ? sys_write+0x40/0x65\n"
- "<5>[56279.703716] [<81002d57>] ? sysenter_do_call+0x12/0x26\n";
-
- EXPECT_TRUE(
- collector_.ComputeKernelStackSignature(kUncertainStackTrace,
- &signature,
- false));
- // The first trace contains only uncertain entries and its hash is 00000000,
- // so, if we used that, the signature would be kernel-add_timer-00000000.
- // Instead we use the second-to-last trace for the hash.
- EXPECT_EQ("kernel-add_timer-B5178878", signature);
-
- ComputeKernelStackSignatureCommon();
-}
diff --git a/crash_reporter/kernel_collector_test.h b/crash_reporter/kernel_collector_test.h
deleted file mode 100644
index d450134..0000000
--- a/crash_reporter/kernel_collector_test.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
-#define CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
-
-#include "kernel_collector.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-class KernelCollectorMock : public KernelCollector {
- public:
- MOCK_METHOD0(DumpDirMounted, bool());
- MOCK_METHOD0(SetUpDBus, void());
-};
-
-#endif // CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
diff --git a/crash_reporter/kernel_log_collector.sh b/crash_reporter/kernel_log_collector.sh
deleted file mode 100644
index d38479e..0000000
--- a/crash_reporter/kernel_log_collector.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/sh
-
-# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Usage example: "kernel_log_collector.sh XXX YYY"
-# This script searches logs in the /var/log/messages which have the keyword XXX.
-# And only those logs which are within the last YYY seconds of the latest log
-# that has the keyword XXX are printed.
-
-# Kernel log has the possible formats:
-# 2013-06-14T16:31:40.514513-07:00 localhost kernel: [ 2.682472] MSG MSG ...
-# 2013-06-19T20:38:58.661826+00:00 localhost kernel: [ 1.668092] MSG MSG ...
-
-search_key=$1
-time_duration=$2
-msg_pattern="^[0-9-]*T[0-9:.+-]* localhost kernel"
-
-die() {
- echo "kernel_log_collector: $*" >&2
- exit 1
-}
-
-get_timestamp() {
- timestamp="$(echo $1 | cut -d " " -f 1)"
- timestamp="$(date -d "${timestamp}" +%s)" || exit $?
- echo "${timestamp}"
-}
-
-last_line=$(grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | tail -n 1)
-
-if [ -n "${last_line}" ]; then
- if ! allowed_timestamp=$(get_timestamp "${last_line}"); then
- die "coule not get timestamp from: ${last_line}"
- fi
- : $(( allowed_timestamp -= ${time_duration} ))
- grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | while read line; do
- if ! timestamp=$(get_timestamp "${line}"); then
- die "could not get timestamp from: ${line}"
- fi
- if [ ${timestamp} -gt ${allowed_timestamp} ]; then
- echo "${line}"
- fi
- done
-fi
-
-echo "END-OF-LOG"
-
diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc
deleted file mode 100644
index 4cf7640..0000000
--- a/crash_reporter/kernel_warning_collector.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "kernel_warning_collector.h"
-
-#include <base/files/file_util.h>
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-
-namespace {
-const char kExecName[] = "kernel-warning";
-const char kKernelWarningSignatureKey[] = "sig";
-const char kKernelWarningPath[] = "/var/run/kwarn/warning";
-const pid_t kKernelPid = 0;
-const uid_t kRootUid = 0;
-} // namespace
-
-using base::FilePath;
-using base::StringPrintf;
-
-KernelWarningCollector::KernelWarningCollector() {
-}
-
-KernelWarningCollector::~KernelWarningCollector() {
-}
-
-bool KernelWarningCollector::LoadKernelWarning(std::string *content,
- std::string *signature) {
- FilePath kernel_warning_path(kKernelWarningPath);
- if (!base::ReadFileToString(kernel_warning_path, content)) {
- LOG(ERROR) << "Could not open " << kKernelWarningPath;
- return false;
- }
- // The signature is in the first line.
- std::string::size_type end_position = content->find('\n');
- if (end_position == std::string::npos) {
- LOG(ERROR) << "unexpected kernel warning format";
- return false;
- }
- *signature = content->substr(0, end_position);
- return true;
-}
-
-bool KernelWarningCollector::Collect() {
- std::string reason = "normal collection";
- bool feedback = true;
- if (IsDeveloperImage()) {
- reason = "always collect from developer builds";
- feedback = true;
- } else if (!is_feedback_allowed_function_()) {
- reason = "no user consent";
- feedback = false;
- }
-
- LOG(INFO) << "Processing kernel warning: " << reason;
-
- if (!feedback) {
- return true;
- }
-
- std::string kernel_warning;
- std::string warning_signature;
- if (!LoadKernelWarning(&kernel_warning, &warning_signature)) {
- return true;
- }
-
- FilePath root_crash_directory;
- if (!GetCreatedCrashDirectoryByEuid(kRootUid, &root_crash_directory,
- nullptr)) {
- return true;
- }
-
- std::string dump_basename =
- FormatDumpBasename(kExecName, time(nullptr), kKernelPid);
- FilePath kernel_crash_path = root_crash_directory.Append(
- StringPrintf("%s.kcrash", dump_basename.c_str()));
-
- // We must use WriteNewFile instead of base::WriteFile as we
- // do not want to write with root access to a symlink that an attacker
- // might have created.
- if (WriteNewFile(kernel_crash_path,
- kernel_warning.data(),
- kernel_warning.length()) !=
- static_cast<int>(kernel_warning.length())) {
- LOG(INFO) << "Failed to write kernel warning to "
- << kernel_crash_path.value().c_str();
- return true;
- }
-
- AddCrashMetaData(kKernelWarningSignatureKey, warning_signature);
- WriteCrashMetaData(
- root_crash_directory.Append(
- StringPrintf("%s.meta", dump_basename.c_str())),
- kExecName, kernel_crash_path.value());
-
- LOG(INFO) << "Stored kernel warning into " << kernel_crash_path.value();
- return true;
-}
diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h
deleted file mode 100644
index 82c509c..0000000
--- a/crash_reporter/kernel_warning_collector.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
-#define CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
-
-#include <string>
-
-#include <base/macros.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-#include "crash_collector.h"
-
-// Kernel warning collector.
-class KernelWarningCollector : public CrashCollector {
- public:
- KernelWarningCollector();
-
- ~KernelWarningCollector() override;
-
- // Collects warning.
- bool Collect();
-
- private:
- friend class KernelWarningCollectorTest;
- FRIEND_TEST(KernelWarningCollectorTest, CollectOK);
-
- // Reads the full content of the kernel warn dump and its signature.
- bool LoadKernelWarning(std::string *content, std::string *signature);
-
- DISALLOW_COPY_AND_ASSIGN(KernelWarningCollector);
-};
-
-#endif // CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
deleted file mode 100644
index de6ef0a..0000000
--- a/crash_reporter/list_proxies.cc
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <sysexits.h>
-#include <unistd.h> // for isatty()
-
-#include <string>
-#include <vector>
-
-#include <base/cancelable_callback.h>
-#include <base/command_line.h>
-#include <base/files/file_util.h>
-#include <base/memory/weak_ptr.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_tokenizer.h>
-#include <base/strings/string_util.h>
-#include <base/values.h>
-#include <chromeos/daemons/dbus_daemon.h>
-#include <chromeos/syslog_logging.h>
-
-#include "libcrosservice/dbus-proxies.h"
-
-using std::unique_ptr;
-
-namespace {
-
-const char kLibCrosProxyResolvedSignalInterface[] =
- "org.chromium.CrashReporterLibcrosProxyResolvedInterface";
-const char kLibCrosProxyResolvedName[] = "ProxyResolved";
-const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
-const char kNoProxy[] = "direct://";
-
-const int kTimeoutDefaultSeconds = 5;
-
-const char kHelp[] = "help";
-const char kQuiet[] = "quiet";
-const char kTimeout[] = "timeout";
-const char kVerbose[] = "verbose";
-// Help message to show when the --help command line switch is specified.
-const char kHelpMessage[] =
- "Chromium OS Crash helper: proxy lister\n"
- "\n"
- "Available Switches:\n"
- " --quiet Only print the proxies\n"
- " --verbose Print additional messages even when not run from a TTY\n"
- " --timeout=N Set timeout for browser resolving proxies (default is 5)\n"
- " --help Show this help.\n";
-
-// Copied from src/update_engine/chrome_browser_proxy_resolver.cc
-// Parses the browser's answer for resolved proxies. It returns a
-// list of strings, each of which is a resolved proxy.
-std::vector<std::string> ParseProxyString(const std::string& input) {
- std::vector<std::string> ret;
- // Some of this code taken from
- // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
- // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
- base::StringTokenizer entry_tok(input, ";");
- while (entry_tok.GetNext()) {
- std::string token = entry_tok.token();
- base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
-
- // Start by finding the first space (if any).
- std::string::iterator space;
- for (space = token.begin(); space != token.end(); ++space) {
- if (IsAsciiWhitespace(*space)) {
- break;
- }
- }
-
- std::string scheme = std::string(token.begin(), space);
- base::StringToLowerASCII(&scheme);
- // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
- if (scheme == "socks")
- scheme += "4";
- else if (scheme == "proxy")
- scheme = "http";
- else if (scheme != "https" &&
- scheme != "socks4" &&
- scheme != "socks5" &&
- scheme != "direct")
- continue; // Invalid proxy scheme
-
- std::string host_and_port = std::string(space, token.end());
- base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
- if (scheme != "direct" && host_and_port.empty())
- continue; // Must supply host/port when non-direct proxy used.
- ret.push_back(scheme + "://" + host_and_port);
- }
- if (ret.empty() || *ret.rbegin() != kNoProxy)
- ret.push_back(kNoProxy);
- return ret;
-}
-
-// A class for interfacing with Chrome to resolve proxies for a given source
-// url. The class is initialized with the given source url to check, the
-// signal interface and name that Chrome will reply to, and how long to wait
-// for the resolve request to timeout. Once initialized, the Run() function
-// must be called, which blocks on the D-Bus call to Chrome. The call returns
-// after either the timeout or the proxy has been resolved. The resolved
-// proxies can then be accessed through the proxies() function.
-class ProxyResolver : public chromeos::DBusDaemon {
- public:
- ProxyResolver(const std::string& source_url,
- const std::string& signal_interface,
- const std::string& signal_name,
- base::TimeDelta timeout)
- : source_url_(source_url),
- signal_interface_(signal_interface),
- signal_name_(signal_name),
- timeout_(timeout),
- weak_ptr_factory_(this),
- timeout_callback_(base::Bind(&ProxyResolver::HandleBrowserTimeout,
- weak_ptr_factory_.GetWeakPtr())) {}
-
- ~ProxyResolver() override {}
-
- const std::vector<std::string>& proxies() {
- return proxies_;
- }
-
- int Run() override {
- // Add task for if the browser proxy call times out.
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- timeout_callback_.callback(),
- timeout_);
-
- return chromeos::DBusDaemon::Run();
- }
-
- protected:
- // If the browser times out, quit the run loop.
- void HandleBrowserTimeout() {
- LOG(ERROR) << "Timeout while waiting for browser to resolve proxy";
- Quit();
- }
-
- // If the signal handler connects successfully, call the browser's
- // ResolveNetworkProxy D-Bus method. Otherwise, don't do anything and let
- // the timeout task quit the run loop.
- void HandleDBusSignalConnected(const std::string& interface,
- const std::string& signal,
- bool success) {
- if (!success) {
- LOG(ERROR) << "Could not connect to signal " << interface << "."
- << signal;
- timeout_callback_.Cancel();
- Quit();
- return;
- }
-
- chromeos::ErrorPtr error;
- call_proxy_->ResolveNetworkProxy(source_url_,
- signal_interface_,
- signal_name_,
- &error);
-
- if (error) {
- LOG(ERROR) << "Call to ResolveNetworkProxy failed: "
- << error->GetMessage();
- timeout_callback_.Cancel();
- Quit();
- }
- }
-
- // Handle incoming ProxyResolved signal.
- void HandleProxyResolvedSignal(const std::string& source_url,
- const std::string& proxy_info,
- const std::string& error_message) {
- timeout_callback_.Cancel();
- proxies_ = ParseProxyString(proxy_info);
- LOG(INFO) << "Found proxies via browser signal: "
- << JoinString(proxies_, 'x');
-
- Quit();
- }
-
- int OnInit() override {
- int return_code = chromeos::DBusDaemon::OnInit();
- if (return_code != EX_OK)
- return return_code;
-
- // Initialize D-Bus proxies.
- call_proxy_.reset(
- new org::chromium::LibCrosServiceInterfaceProxy(bus_,
- kLibCrosServiceName));
- signal_proxy_.reset(
- new org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy(
- bus_,
- kLibCrosServiceName));
-
- // Set up the D-Bus signal handler.
- // TODO(crbug.com/446115): Update ResolveNetworkProxy call to use an
- // asynchronous return value rather than a return signal.
- signal_proxy_->RegisterProxyResolvedSignalHandler(
- base::Bind(&ProxyResolver::HandleProxyResolvedSignal,
- weak_ptr_factory_.GetWeakPtr()),
- base::Bind(&ProxyResolver::HandleDBusSignalConnected,
- weak_ptr_factory_.GetWeakPtr()));
-
- return EX_OK;
- }
-
- private:
- unique_ptr<org::chromium::LibCrosServiceInterfaceProxy> call_proxy_;
- unique_ptr<org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy>
- signal_proxy_;
-
- const std::string source_url_;
- const std::string signal_interface_;
- const std::string signal_name_;
- base::TimeDelta timeout_;
-
- std::vector<std::string> proxies_;
- base::WeakPtrFactory<ProxyResolver> weak_ptr_factory_;
-
- base::CancelableClosure timeout_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
-};
-
-static bool ShowBrowserProxies(std::string url, base::TimeDelta timeout) {
- // Initialize and run the proxy resolver to watch for signals.
- ProxyResolver resolver(url,
- kLibCrosProxyResolvedSignalInterface,
- kLibCrosProxyResolvedName,
- timeout);
- resolver.Run();
-
- std::vector<std::string> proxies = resolver.proxies();
-
- // If proxies is empty, then the timeout was reached waiting for the proxy
- // resolved signal. If no proxies are defined, proxies will be populated
- // with "direct://".
- if (proxies.empty())
- return false;
-
- for (const auto& proxy : proxies) {
- printf("%s\n", proxy.c_str());
- }
- return true;
-}
-
-} // namespace
-
-int main(int argc, char *argv[]) {
- base::CommandLine::Init(argc, argv);
- base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
-
- if (cl->HasSwitch(kHelp)) {
- LOG(INFO) << kHelpMessage;
- return 0;
- }
-
- bool quiet = cl->HasSwitch(kQuiet);
- bool verbose = cl->HasSwitch(kVerbose);
-
- int timeout = kTimeoutDefaultSeconds;
- std::string str_timeout = cl->GetSwitchValueASCII(kTimeout);
- if (!str_timeout.empty() && !base::StringToInt(str_timeout, &timeout)) {
- LOG(ERROR) << "Invalid timeout value: " << str_timeout;
- return 1;
- }
-
- // Default to logging to syslog.
- int init_flags = chromeos::kLogToSyslog;
- // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose"
- // was passed.
-
- if ((!quiet && isatty(STDERR_FILENO)) || verbose)
- init_flags |= chromeos::kLogToStderr;
- chromeos::InitLog(init_flags);
-
- std::string url;
- base::CommandLine::StringVector urls = cl->GetArgs();
- if (!urls.empty()) {
- url = urls[0];
- LOG(INFO) << "Resolving proxies for URL: " << url;
- } else {
- LOG(INFO) << "Resolving proxies without URL";
- }
-
- if (!ShowBrowserProxies(url, base::TimeDelta::FromSeconds(timeout))) {
- LOG(ERROR) << "Error resolving proxies via the browser";
- LOG(INFO) << "Assuming direct proxy";
- printf("%s\n", kNoProxy);
- }
-
- return 0;
-}
diff --git a/crash_reporter/testrunner.cc b/crash_reporter/testrunner.cc
deleted file mode 100644
index d45bbf8..0000000
--- a/crash_reporter/testrunner.cc
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <chromeos/test_helpers.h>
-#include <gtest/gtest.h>
-
-int main(int argc, char** argv) {
- SetUpTests(&argc, argv, true);
- return RUN_ALL_TESTS();
-}
diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc
deleted file mode 100644
index 85f40db..0000000
--- a/crash_reporter/udev_collector.cc
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "udev_collector.h"
-
-#include <map>
-#include <utility>
-#include <vector>
-
-#include <base/files/file_enumerator.h>
-#include <base/files/file_util.h>
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <chromeos/process.h>
-
-using base::FilePath;
-
-namespace {
-
-const char kCollectUdevSignature[] = "crash_reporter-udev-collection";
-const char kGzipPath[] = "/bin/gzip";
-const char kUdevExecName[] = "udev";
-const char kUdevSignatureKey[] = "sig";
-const char kUdevSubsystemDevCoredump[] = "devcoredump";
-const char kDefaultDevCoredumpDirectory[] = "/sys/class/devcoredump";
-const char kDevCoredumpFilePrefixFormat[] = "devcoredump_%s";
-
-} // namespace
-
-UdevCollector::UdevCollector()
- : dev_coredump_directory_(kDefaultDevCoredumpDirectory) {}
-
-UdevCollector::~UdevCollector() {}
-
-bool UdevCollector::HandleCrash(const std::string &udev_event) {
- if (IsDeveloperImage()) {
- LOG(INFO) << "developer image - collect udev crash info.";
- } else if (is_feedback_allowed_function_()) {
- LOG(INFO) << "Consent given - collect udev crash info.";
- } else {
- LOG(INFO) << "Ignoring - Non-developer image and no consent given.";
- return false;
- }
-
- // Process the udev event string.
- // First get all the key-value pairs.
- std::vector<std::pair<std::string, std::string>> udev_event_keyval;
- base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval);
- std::vector<std::pair<std::string, std::string>>::const_iterator iter;
- std::map<std::string, std::string> udev_event_map;
- for (iter = udev_event_keyval.begin();
- iter != udev_event_keyval.end();
- ++iter) {
- udev_event_map[iter->first] = iter->second;
- }
-
- // Make sure the crash directory exists, or create it if it doesn't.
- FilePath crash_directory;
- if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) {
- LOG(ERROR) << "Could not get crash directory.";
- return false;
- }
-
- if (udev_event_map["SUBSYSTEM"] == kUdevSubsystemDevCoredump) {
- int instance_number;
- if (!base::StringToInt(udev_event_map["KERNEL_NUMBER"], &instance_number)) {
- LOG(ERROR) << "Invalid kernel number: "
- << udev_event_map["KERNEL_NUMBER"];
- return false;
- }
- return ProcessDevCoredump(crash_directory, instance_number);
- }
-
- return ProcessUdevCrashLogs(crash_directory,
- udev_event_map["ACTION"],
- udev_event_map["KERNEL"],
- udev_event_map["SUBSYSTEM"]);
-}
-
-bool UdevCollector::ProcessUdevCrashLogs(const FilePath& crash_directory,
- const std::string& action,
- const std::string& kernel,
- const std::string& subsystem) {
- // Construct the basename string for crash_reporter_logs.conf:
- // "crash_reporter-udev-collection-[action]-[name]-[subsystem]"
- // If a udev field is not provided, "" is used in its place, e.g.:
- // "crash_reporter-udev-collection-[action]--[subsystem]"
- // Hence, "" is used as a wildcard name string.
- // TODO(sque, crosbug.com/32238): Implement wildcard checking.
- std::string basename = action + "-" + kernel + "-" + subsystem;
- std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
- basename;
-
- // Create the destination path.
- std::string log_file_name =
- FormatDumpBasename(basename, time(nullptr), 0);
- FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log");
-
- // Handle the crash.
- bool result = GetLogContents(log_config_path_, udev_log_name, crash_path);
- if (!result) {
- LOG(ERROR) << "Error reading udev log info " << udev_log_name;
- return false;
- }
-
- // Compress the output using gzip.
- chromeos::ProcessImpl gzip_process;
- gzip_process.AddArg(kGzipPath);
- gzip_process.AddArg(crash_path.value());
- int process_result = gzip_process.Run();
- FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz");
- // If the zip file was not created, use the uncompressed file.
- if (process_result != 0 || !base::PathExists(crash_path_zipped))
- LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value();
- else
- crash_path = crash_path_zipped;
-
- std::string exec_name = std::string(kUdevExecName) + "-" + subsystem;
- AddCrashMetaData(kUdevSignatureKey, udev_log_name);
- WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"),
- exec_name, crash_path.value());
- return true;
-}
-
-bool UdevCollector::ProcessDevCoredump(const FilePath& crash_directory,
- int instance_number) {
- FilePath coredump_path =
- FilePath(base::StringPrintf("%s/devcd%d/data",
- dev_coredump_directory_.c_str(),
- instance_number));
- if (!base::PathExists(coredump_path)) {
- LOG(ERROR) << "Device coredump file " << coredump_path.value()
- << " does not exist";
- return false;
- }
-
- // Add coredump file to the crash directory.
- if (!AppendDevCoredump(crash_directory, coredump_path, instance_number)) {
- ClearDevCoredump(coredump_path);
- return false;
- }
-
- // Clear the coredump data to allow generation of future device coredumps
- // without having to wait for the 5-minutes timeout.
- return ClearDevCoredump(coredump_path);
-}
-
-bool UdevCollector::AppendDevCoredump(const FilePath& crash_directory,
- const FilePath& coredump_path,
- int instance_number) {
- // Retrieve the driver name of the failing device.
- std::string driver_name = GetFailingDeviceDriverName(instance_number);
- if (driver_name.empty()) {
- LOG(ERROR) << "Failed to obtain driver name for instance: "
- << instance_number;
- return false;
- }
-
- std::string coredump_prefix =
- base::StringPrintf(kDevCoredumpFilePrefixFormat, driver_name.c_str());
-
- std::string dump_basename = FormatDumpBasename(coredump_prefix,
- time(nullptr),
- instance_number);
- FilePath core_path = GetCrashPath(crash_directory, dump_basename, "devcore");
- FilePath log_path = GetCrashPath(crash_directory, dump_basename, "log");
- FilePath meta_path = GetCrashPath(crash_directory, dump_basename, "meta");
-
- // Collect coredump data.
- if (!base::CopyFile(coredump_path, core_path)) {
- LOG(ERROR) << "Failed to copy device coredumpm file from "
- << coredump_path.value() << " to " << core_path.value();
- return false;
- }
-
- // Collect additional logs if one is specified in the config file.
- std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
- kUdevSubsystemDevCoredump + '-' + driver_name;
- bool result = GetLogContents(log_config_path_, udev_log_name, log_path);
- if (result) {
- AddCrashMetaUploadFile("logs", log_path.value());
- }
-
- WriteCrashMetaData(meta_path, coredump_prefix, core_path.value());
-
- return true;
-}
-
-bool UdevCollector::ClearDevCoredump(const FilePath& coredump_path) {
- if (!base::WriteFile(coredump_path, "0", 1)) {
- LOG(ERROR) << "Failed to delete the coredump data file "
- << coredump_path.value();
- return false;
- }
- return true;
-}
-
-std::string UdevCollector::GetFailingDeviceDriverName(int instance_number) {
- FilePath failing_uevent_path =
- FilePath(base::StringPrintf("%s/devcd%d/failing_device/uevent",
- dev_coredump_directory_.c_str(),
- instance_number));
- if (!base::PathExists(failing_uevent_path)) {
- LOG(ERROR) << "Failing uevent path " << failing_uevent_path.value()
- << " does not exist";
- return "";
- }
-
- std::string uevent_content;
- if (!base::ReadFileToString(failing_uevent_path, &uevent_content)) {
- LOG(ERROR) << "Failed to read uevent file " << failing_uevent_path.value();
- return "";
- }
-
- // Parse uevent file contents as key-value pairs.
- std::vector<std::pair<std::string, std::string>> uevent_keyval;
- base::SplitStringIntoKeyValuePairs(uevent_content, '=', '\n', &uevent_keyval);
- std::vector<std::pair<std::string, std::string>>::const_iterator iter;
- for (iter = uevent_keyval.begin();
- iter != uevent_keyval.end();
- ++iter) {
- if (iter->first == "DRIVER") {
- return iter->second;
- }
- }
-
- return "";
-}
diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h
deleted file mode 100644
index d9b37eb..0000000
--- a/crash_reporter/udev_collector.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRASH_REPORTER_UDEV_COLLECTOR_H_
-#define CRASH_REPORTER_UDEV_COLLECTOR_H_
-
-#include <string>
-
-#include <base/files/file_path.h>
-#include <base/macros.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-#include "crash_collector.h"
-
-// Udev crash collector.
-class UdevCollector : public CrashCollector {
- public:
- UdevCollector();
-
- ~UdevCollector() override;
-
- // The udev event string should be formatted as follows:
- // "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]"
- // The values don't have to be in any particular order. One or more of them
- // could be omitted, in which case it would be treated as a wildcard (*).
- bool HandleCrash(const std::string& udev_event);
-
- protected:
- std::string dev_coredump_directory_;
-
- private:
- friend class UdevCollectorTest;
-
- // Process udev crash logs, collecting log files according to the config
- // file (crash_reporter_logs.conf).
- bool ProcessUdevCrashLogs(const base::FilePath& crash_directory,
- const std::string& action,
- const std::string& kernel,
- const std::string& subsystem);
- // Process device coredump, collecting device coredump file.
- // |instance_number| is the kernel number of the virtual device for the device
- // coredump instance.
- bool ProcessDevCoredump(const base::FilePath& crash_directory,
- int instance_number);
- // Copy device coredump file to crash directory, and perform necessary
- // coredump file management.
- bool AppendDevCoredump(const base::FilePath& crash_directory,
- const base::FilePath& coredump_path,
- int instance_number);
- // Clear the device coredump file by performing a dummy write to it.
- bool ClearDevCoredump(const base::FilePath& coredump_path);
- // Return the driver name of the device that generates the coredump.
- std::string GetFailingDeviceDriverName(int instance_number);
-
- // Mutator for unit testing.
- void set_log_config_path(const std::string& path) {
- log_config_path_ = base::FilePath(path);
- }
-
- DISALLOW_COPY_AND_ASSIGN(UdevCollector);
-};
-
-#endif // CRASH_REPORTER_UDEV_COLLECTOR_H_
diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc
deleted file mode 100644
index 4897b91..0000000
--- a/crash_reporter/udev_collector_test.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <base/files/file_enumerator.h>
-#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
-#include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "udev_collector.h"
-
-using base::FilePath;
-
-namespace {
-
-// Dummy log config file name.
-const char kLogConfigFileName[] = "log_config_file";
-
-// Dummy directory for storing device coredumps.
-const char kDevCoredumpDirectory[] = "devcoredump";
-
-// A bunch of random rules to put into the dummy log config file.
-const char kLogConfigFileContents[] =
- "crash_reporter-udev-collection-change-card0-drm=echo change card0 drm\n"
- "crash_reporter-udev-collection-add-state0-cpu=echo change state0 cpu\n"
- "crash_reporter-udev-collection-devcoredump-iwlwifi=echo devcoredump\n"
- "cros_installer=echo not for udev";
-
-const char kCrashLogFilePattern[] = "*.log.gz";
-const char kDevCoredumpFilePattern[] = "*.devcore";
-
-// Dummy content for device coredump data file.
-const char kDevCoredumpDataContents[] = "coredump";
-
-// Content for failing device's uevent file.
-const char kFailingDeviceUeventContents[] = "DRIVER=iwlwifi\n";
-
-void CountCrash() {}
-
-bool s_consent_given = true;
-
-bool IsMetrics() {
- return s_consent_given;
-}
-
-// Returns the number of files found in the given path that matches the
-// specified file name pattern.
-int GetNumFiles(const FilePath& path, const std::string& file_pattern) {
- base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES,
- file_pattern);
- int num_files = 0;
- for (FilePath file_path = enumerator.Next();
- !file_path.value().empty();
- file_path = enumerator.Next()) {
- num_files++;
- }
- return num_files;
-}
-
-} // namespace
-
-class UdevCollectorMock : public UdevCollector {
- public:
- MOCK_METHOD0(SetUpDBus, void());
-};
-
-class UdevCollectorTest : public ::testing::Test {
- protected:
- base::ScopedTempDir temp_dir_generator_;
-
- void HandleCrash(const std::string &udev_event) {
- collector_.HandleCrash(udev_event);
- }
-
- void GenerateDevCoredump(const std::string& device_name) {
- // Generate coredump data file.
- ASSERT_TRUE(CreateDirectory(
- FilePath(base::StringPrintf("%s/%s",
- collector_.dev_coredump_directory_.c_str(),
- device_name.c_str()))));
- FilePath data_path =
- FilePath(base::StringPrintf("%s/%s/data",
- collector_.dev_coredump_directory_.c_str(),
- device_name.c_str()));
- ASSERT_EQ(strlen(kDevCoredumpDataContents),
- base::WriteFile(data_path,
- kDevCoredumpDataContents,
- strlen(kDevCoredumpDataContents)));
- // Generate uevent file for failing device.
- ASSERT_TRUE(CreateDirectory(
- FilePath(base::StringPrintf("%s/%s/failing_device",
- collector_.dev_coredump_directory_.c_str(),
- device_name.c_str()))));
- FilePath uevent_path =
- FilePath(base::StringPrintf("%s/%s/failing_device/uevent",
- collector_.dev_coredump_directory_.c_str(),
- device_name.c_str()));
- ASSERT_EQ(strlen(kFailingDeviceUeventContents),
- base::WriteFile(uevent_path,
- kFailingDeviceUeventContents,
- strlen(kFailingDeviceUeventContents)));
- }
-
- private:
- void SetUp() override {
- s_consent_given = true;
-
- EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
-
- collector_.Initialize(CountCrash, IsMetrics);
-
- ASSERT_TRUE(temp_dir_generator_.CreateUniqueTempDir());
-
- FilePath log_config_path =
- temp_dir_generator_.path().Append(kLogConfigFileName);
- collector_.log_config_path_ = log_config_path;
- collector_.ForceCrashDirectory(temp_dir_generator_.path());
-
- FilePath dev_coredump_path =
- temp_dir_generator_.path().Append(kDevCoredumpDirectory);
- collector_.dev_coredump_directory_ = dev_coredump_path.value();
-
- // Write to a dummy log config file.
- ASSERT_EQ(strlen(kLogConfigFileContents),
- base::WriteFile(log_config_path,
- kLogConfigFileContents,
- strlen(kLogConfigFileContents)));
-
- chromeos::ClearLog();
- }
-
- UdevCollectorMock collector_;
-};
-
-TEST_F(UdevCollectorTest, TestNoConsent) {
- s_consent_given = false;
- HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
- EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
-}
-
-TEST_F(UdevCollectorTest, TestNoMatch) {
- // No rule should match this.
- HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar");
- EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
-}
-
-TEST_F(UdevCollectorTest, TestMatches) {
- // Try multiple udev events in sequence. The number of log files generated
- // should increase.
- HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
- EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
- HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu");
- EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
-}
-
-TEST_F(UdevCollectorTest, TestDevCoredump) {
- GenerateDevCoredump("devcd0");
- HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
- EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(),
- kDevCoredumpFilePattern));
- GenerateDevCoredump("devcd1");
- HandleCrash("ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump");
- EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(),
- kDevCoredumpFilePattern));
-}
-
-// TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev
-// events.
diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc
deleted file mode 100644
index a6da1bb..0000000
--- a/crash_reporter/unclean_shutdown_collector.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "unclean_shutdown_collector.h"
-
-#include <base/files/file_util.h>
-#include <base/logging.h>
-
-static const char kUncleanShutdownFile[] =
- "/var/lib/crash_reporter/pending_clean_shutdown";
-
-// Files created by power manager used for crash reporting.
-static const char kPowerdTracePath[] = "/var/lib/power_manager";
-// Presence of this file indicates that the system was suspended
-static const char kPowerdSuspended[] = "powerd_suspended";
-
-using base::FilePath;
-
-UncleanShutdownCollector::UncleanShutdownCollector()
- : unclean_shutdown_file_(kUncleanShutdownFile),
- powerd_trace_path_(kPowerdTracePath),
- powerd_suspended_file_(powerd_trace_path_.Append(kPowerdSuspended)) {
-}
-
-UncleanShutdownCollector::~UncleanShutdownCollector() {
-}
-
-bool UncleanShutdownCollector::Enable() {
- FilePath file_path(unclean_shutdown_file_);
- base::CreateDirectory(file_path.DirName());
- if (base::WriteFile(file_path, "", 0) != 0) {
- LOG(ERROR) << "Unable to create shutdown check file";
- return false;
- }
- return true;
-}
-
-bool UncleanShutdownCollector::DeleteUncleanShutdownFiles() {
- if (!base::DeleteFile(FilePath(unclean_shutdown_file_), false)) {
- LOG(ERROR) << "Failed to delete unclean shutdown file "
- << unclean_shutdown_file_;
- return false;
- }
- // Delete power manager state file if it exists.
- base::DeleteFile(powerd_suspended_file_, false);
- return true;
-}
-
-bool UncleanShutdownCollector::Collect() {
- FilePath unclean_file_path(unclean_shutdown_file_);
- if (!base::PathExists(unclean_file_path)) {
- return false;
- }
- LOG(WARNING) << "Last shutdown was not clean";
- if (DeadBatteryCausedUncleanShutdown()) {
- DeleteUncleanShutdownFiles();
- return false;
- }
- DeleteUncleanShutdownFiles();
-
- if (is_feedback_allowed_function_()) {
- count_crash_function_();
- }
- return true;
-}
-
-bool UncleanShutdownCollector::Disable() {
- LOG(INFO) << "Clean shutdown signalled";
- return DeleteUncleanShutdownFiles();
-}
-
-bool UncleanShutdownCollector::DeadBatteryCausedUncleanShutdown() {
- // Check for case of battery running out while suspended.
- if (base::PathExists(powerd_suspended_file_)) {
- LOG(INFO) << "Unclean shutdown occurred while suspended. Not counting "
- << "toward unclean shutdown statistic.";
- return true;
- }
- return false;
-}
diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h
deleted file mode 100644
index 2ce0842..0000000
--- a/crash_reporter/unclean_shutdown_collector.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
-#define CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
-
-#include <string>
-
-#include <base/files/file_path.h>
-#include <base/macros.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-#include "crash_collector.h"
-
-// Unclean shutdown collector.
-class UncleanShutdownCollector : public CrashCollector {
- public:
- UncleanShutdownCollector();
- ~UncleanShutdownCollector() override;
-
- // Enable collection - signal that a boot has started.
- bool Enable();
-
- // Collect if there is was an unclean shutdown. Returns true if
- // there was, false otherwise.
- bool Collect();
-
- // Disable collection - signal that the system has been shutdown cleanly.
- bool Disable();
-
- private:
- friend class UncleanShutdownCollectorTest;
- FRIEND_TEST(UncleanShutdownCollectorTest, EnableCannotWrite);
- FRIEND_TEST(UncleanShutdownCollectorTest, CollectDeadBatterySuspended);
-
- bool DeleteUncleanShutdownFiles();
-
- // Check for unclean shutdown due to battery running out by analyzing powerd
- // trace files.
- bool DeadBatteryCausedUncleanShutdown();
-
- const char *unclean_shutdown_file_;
- base::FilePath powerd_trace_path_;
- base::FilePath powerd_suspended_file_;
-
- DISALLOW_COPY_AND_ASSIGN(UncleanShutdownCollector);
-};
-
-#endif // CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
deleted file mode 100644
index dc420fb..0000000
--- a/crash_reporter/unclean_shutdown_collector_test.cc
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "unclean_shutdown_collector.h"
-
-#include <unistd.h>
-
-#include <base/files/file_util.h>
-#include <base/strings/string_util.h>
-#include <chromeos/syslog_logging.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-using base::FilePath;
-using ::chromeos::FindLog;
-
-namespace {
-
-int s_crashes = 0;
-bool s_metrics = true;
-
-const char kTestDirectory[] = "test";
-const char kTestSuspended[] = "test/suspended";
-const char kTestUnclean[] = "test/unclean";
-
-void CountCrash() {
- ++s_crashes;
-}
-
-bool IsMetrics() {
- return s_metrics;
-}
-
-} // namespace
-
-class UncleanShutdownCollectorMock : public UncleanShutdownCollector {
- public:
- MOCK_METHOD0(SetUpDBus, void());
-};
-
-class UncleanShutdownCollectorTest : public ::testing::Test {
- void SetUp() {
- s_crashes = 0;
-
- EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
-
- collector_.Initialize(CountCrash,
- IsMetrics);
- rmdir(kTestDirectory);
- test_unclean_ = FilePath(kTestUnclean);
- collector_.unclean_shutdown_file_ = kTestUnclean;
- base::DeleteFile(test_unclean_, true);
- // Set up an alternate power manager state file as well
- collector_.powerd_suspended_file_ = FilePath(kTestSuspended);
- chromeos::ClearLog();
- }
-
- protected:
- void WriteStringToFile(const FilePath &file_path,
- const char *data) {
- ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
- }
-
- UncleanShutdownCollectorMock collector_;
- FilePath test_unclean_;
-};
-
-TEST_F(UncleanShutdownCollectorTest, EnableWithoutParent) {
- ASSERT_TRUE(collector_.Enable());
- ASSERT_TRUE(base::PathExists(test_unclean_));
-}
-
-TEST_F(UncleanShutdownCollectorTest, EnableWithParent) {
- mkdir(kTestDirectory, 0777);
- ASSERT_TRUE(collector_.Enable());
- ASSERT_TRUE(base::PathExists(test_unclean_));
-}
-
-TEST_F(UncleanShutdownCollectorTest, EnableCannotWrite) {
- collector_.unclean_shutdown_file_ = "/bad/path";
- ASSERT_FALSE(collector_.Enable());
- ASSERT_TRUE(FindLog("Unable to create shutdown check file"));
-}
-
-TEST_F(UncleanShutdownCollectorTest, CollectTrue) {
- ASSERT_TRUE(collector_.Enable());
- ASSERT_TRUE(base::PathExists(test_unclean_));
- ASSERT_TRUE(collector_.Collect());
- ASSERT_FALSE(base::PathExists(test_unclean_));
- ASSERT_EQ(1, s_crashes);
- ASSERT_TRUE(FindLog("Last shutdown was not clean"));
-}
-
-TEST_F(UncleanShutdownCollectorTest, CollectFalse) {
- ASSERT_FALSE(collector_.Collect());
- ASSERT_EQ(0, s_crashes);
-}
-
-TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) {
- ASSERT_TRUE(collector_.Enable());
- ASSERT_TRUE(base::PathExists(test_unclean_));
- base::WriteFile(collector_.powerd_suspended_file_, "", 0);
- ASSERT_FALSE(collector_.Collect());
- ASSERT_FALSE(base::PathExists(test_unclean_));
- ASSERT_FALSE(base::PathExists(collector_.powerd_suspended_file_));
- ASSERT_EQ(0, s_crashes);
- ASSERT_TRUE(FindLog("Unclean shutdown occurred while suspended."));
-}
-
-TEST_F(UncleanShutdownCollectorTest, Disable) {
- ASSERT_TRUE(collector_.Enable());
- ASSERT_TRUE(base::PathExists(test_unclean_));
- ASSERT_TRUE(collector_.Disable());
- ASSERT_FALSE(base::PathExists(test_unclean_));
- ASSERT_FALSE(collector_.Collect());
-}
-
-TEST_F(UncleanShutdownCollectorTest, DisableWhenNotEnabled) {
- ASSERT_TRUE(collector_.Disable());
-}
-
-TEST_F(UncleanShutdownCollectorTest, CantDisable) {
- mkdir(kTestDirectory, 0700);
- if (mkdir(kTestUnclean, 0700)) {
- ASSERT_EQ(EEXIST, errno)
- << "Error while creating directory '" << kTestUnclean
- << "': " << strerror(errno);
- }
- ASSERT_EQ(0, base::WriteFile(test_unclean_.Append("foo"), "", 0))
- << "Error while creating empty file '"
- << test_unclean_.Append("foo").value() << "': " << strerror(errno);
- ASSERT_FALSE(collector_.Disable());
- rmdir(kTestUnclean);
-}
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
deleted file mode 100644
index 069b581..0000000
--- a/crash_reporter/user_collector.cc
+++ /dev/null
@@ -1,579 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "user_collector.h"
-
-#include <elf.h>
-#include <fcntl.h>
-#include <grp.h> // For struct group.
-#include <pcrecpp.h>
-#include <pwd.h> // For struct passwd.
-#include <stdint.h>
-#include <sys/cdefs.h> // For __WORDSIZE
-#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
-
-#include <string>
-#include <vector>
-
-#include <base/files/file_util.h>
-#include <base/logging.h>
-#include <base/posix/eintr_wrapper.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <chromeos/process.h>
-#include <chromeos/syslog_logging.h>
-
-static const char kCollectionErrorSignature[] =
- "crash_reporter-user-collection";
-// This procfs file is used to cause kernel core file writing to
-// instead pipe the core file into a user space process. See
-// core(5) man page.
-static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
-static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
-// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
-// crashes, but finite to avoid infinitely recursing on crash handling.
-static const char kCorePipeLimit[] = "4";
-static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
-
-static const char kStatePrefix[] = "State:\t";
-
-// Define an otherwise invalid value that represents an unknown UID.
-static const uid_t kUnknownUid = -1;
-
-const char *UserCollector::kUserId = "Uid:\t";
-const char *UserCollector::kGroupId = "Gid:\t";
-
-using base::FilePath;
-using base::StringPrintf;
-
-UserCollector::UserCollector()
- : generate_diagnostics_(false),
- core_pattern_file_(kCorePatternFile),
- core_pipe_limit_file_(kCorePipeLimitFile),
- initialized_(false) {
-}
-
-void UserCollector::Initialize(
- UserCollector::CountCrashFunction count_crash_function,
- const std::string &our_path,
- UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
- bool generate_diagnostics,
- bool core2md_failure,
- bool directory_failure,
- const std::string &filter_in) {
- CrashCollector::Initialize(count_crash_function,
- is_feedback_allowed_function);
- our_path_ = our_path;
- initialized_ = true;
- generate_diagnostics_ = generate_diagnostics;
- core2md_failure_ = core2md_failure;
- directory_failure_ = directory_failure;
- filter_in_ = filter_in;
-}
-
-UserCollector::~UserCollector() {
-}
-
-std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const {
- switch (error_type) {
- case kErrorSystemIssue:
- return "system-issue";
- case kErrorReadCoreData:
- return "read-core-data";
- case kErrorUnusableProcFiles:
- return "unusable-proc-files";
- case kErrorInvalidCoreFile:
- return "invalid-core-file";
- case kErrorUnsupported32BitCoreFile:
- return "unsupported-32bit-core-file";
- case kErrorCore2MinidumpConversion:
- return "core2md-conversion";
- default:
- return "";
- }
-}
-
-// Return the string that should be used for the kernel's core_pattern file.
-// Note that if you change the format of the enabled pattern, you'll probably
-// also need to change the ParseCrashAttributes() function below, the
-// user_collector_test.cc unittest, and the logging_UserCrash.py autotest.
-std::string UserCollector::GetPattern(bool enabled) const {
- if (enabled) {
- // Combine the four crash attributes into one parameter to try to reduce
- // the size of the invocation line for crash_reporter, since the kernel
- // has a fixed-sized (128B) buffer for it (before parameter expansion).
- // Note that the kernel does not support quoted arguments in core_pattern.
- return StringPrintf("|%s --user=%%P:%%s:%%u:%%e", our_path_.c_str());
- } else {
- return "core";
- }
-}
-
-bool UserCollector::SetUpInternal(bool enabled) {
- CHECK(initialized_);
- LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
-
- if (base::WriteFile(FilePath(core_pipe_limit_file_), kCorePipeLimit,
- strlen(kCorePipeLimit)) !=
- static_cast<int>(strlen(kCorePipeLimit))) {
- PLOG(ERROR) << "Unable to write " << core_pipe_limit_file_;
- return false;
- }
- std::string pattern = GetPattern(enabled);
- if (base::WriteFile(FilePath(core_pattern_file_), pattern.c_str(),
- pattern.length()) != static_cast<int>(pattern.length())) {
- PLOG(ERROR) << "Unable to write " << core_pattern_file_;
- return false;
- }
- return true;
-}
-
-bool UserCollector::GetFirstLineWithPrefix(
- const std::vector<std::string> &lines,
- const char *prefix, std::string *line) {
- std::vector<std::string>::const_iterator line_iterator;
- for (line_iterator = lines.begin(); line_iterator != lines.end();
- ++line_iterator) {
- if (line_iterator->find(prefix) == 0) {
- *line = *line_iterator;
- return true;
- }
- }
- return false;
-}
-
-bool UserCollector::GetIdFromStatus(
- const char *prefix, IdKind kind,
- const std::vector<std::string> &status_lines, int *id) {
- // From fs/proc/array.c:task_state(), this file contains:
- // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
- std::string id_line;
- if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
- return false;
- }
- std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
- std::vector<std::string> ids;
- base::SplitString(id_substring, '\t', &ids);
- if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
- return false;
- }
- const char *number = ids[kind].c_str();
- char *end_number = nullptr;
- *id = strtol(number, &end_number, 10);
- if (*end_number != '\0') {
- return false;
- }
- return true;
-}
-
-bool UserCollector::GetStateFromStatus(
- const std::vector<std::string> &status_lines, std::string *state) {
- std::string state_line;
- if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
- return false;
- }
- *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
- return true;
-}
-
-void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
- ErrorType error_type,
- const std::string &exec) {
- FilePath crash_path;
- LOG(INFO) << "Writing conversion problems as separate crash report.";
- if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, nullptr)) {
- LOG(ERROR) << "Could not even get log directory; out of space?";
- return;
- }
- AddCrashMetaData("sig", kCollectionErrorSignature);
- AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
- std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
- std::string error_log = chromeos::GetLog();
- FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
- if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
- diag_log_path)) {
- // We load the contents of diag_log into memory and append it to
- // the error log. We cannot just append to files because we need
- // to always create new files to prevent attack.
- std::string diag_log_contents;
- base::ReadFileToString(diag_log_path, &diag_log_contents);
- error_log.append(diag_log_contents);
- base::DeleteFile(diag_log_path, false);
- }
- FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
- FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
- // We must use WriteNewFile instead of base::WriteFile as we do
- // not want to write with root access to a symlink that an attacker
- // might have created.
- if (WriteNewFile(log_path, error_log.data(), error_log.length()) < 0) {
- LOG(ERROR) << "Error writing new file " << log_path.value();
- return;
- }
- WriteCrashMetaData(meta_path, exec, log_path.value());
-}
-
-bool UserCollector::CopyOffProcFiles(pid_t pid,
- const FilePath &container_dir) {
- if (!base::CreateDirectory(container_dir)) {
- PLOG(ERROR) << "Could not create " << container_dir.value().c_str();
- return false;
- }
- FilePath process_path = GetProcessPath(pid);
- if (!base::PathExists(process_path)) {
- LOG(ERROR) << "Path " << process_path.value() << " does not exist";
- return false;
- }
- static const char *proc_files[] = {
- "auxv",
- "cmdline",
- "environ",
- "maps",
- "status"
- };
- for (unsigned i = 0; i < arraysize(proc_files); ++i) {
- if (!base::CopyFile(process_path.Append(proc_files[i]),
- container_dir.Append(proc_files[i]))) {
- LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
- return false;
- }
- }
- return true;
-}
-
-bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const {
- // Check if the maps file is empty, which could be due to the crashed
- // process being reaped by the kernel before finishing a core dump.
- int64_t file_size = 0;
- if (!base::GetFileSize(container_dir.Append("maps"), &file_size)) {
- LOG(ERROR) << "Could not get the size of maps file";
- return false;
- }
- if (file_size == 0) {
- LOG(ERROR) << "maps file is empty";
- return false;
- }
- return true;
-}
-
-UserCollector::ErrorType UserCollector::ValidateCoreFile(
- const FilePath &core_path) const {
- int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY));
- if (fd < 0) {
- PLOG(ERROR) << "Could not open core file " << core_path.value();
- return kErrorInvalidCoreFile;
- }
-
- char e_ident[EI_NIDENT];
- bool read_ok = base::ReadFromFD(fd, e_ident, sizeof(e_ident));
- IGNORE_EINTR(close(fd));
- if (!read_ok) {
- LOG(ERROR) << "Could not read header of core file";
- return kErrorInvalidCoreFile;
- }
-
- if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
- e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
- LOG(ERROR) << "Invalid core file";
- return kErrorInvalidCoreFile;
- }
-
-#if __WORDSIZE == 64
- // TODO(benchan, mkrebs): Remove this check once core2md can
- // handles both 32-bit and 64-bit ELF on a 64-bit platform.
- if (e_ident[EI_CLASS] == ELFCLASS32) {
- LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is "
- << "currently not supported";
- return kErrorUnsupported32BitCoreFile;
- }
-#endif
-
- return kErrorNone;
-}
-
-bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
- FilePath *crash_file_path,
- bool *out_of_capacity) {
- FilePath process_path = GetProcessPath(pid);
- std::string status;
- if (directory_failure_) {
- LOG(ERROR) << "Purposefully failing to create spool directory";
- return false;
- }
-
- uid_t uid;
- if (base::ReadFileToString(process_path.Append("status"), &status)) {
- std::vector<std::string> status_lines;
- base::SplitString(status, '\n', &status_lines);
-
- std::string process_state;
- if (!GetStateFromStatus(status_lines, &process_state)) {
- LOG(ERROR) << "Could not find process state in status file";
- return false;
- }
- LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
-
- // Get effective UID of crashing process.
- int id;
- if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &id)) {
- LOG(ERROR) << "Could not find euid in status file";
- return false;
- }
- uid = id;
- } else if (supplied_ruid != kUnknownUid) {
- LOG(INFO) << "Using supplied UID " << supplied_ruid
- << " for crashed process [" << pid
- << "] due to error reading status file";
- uid = supplied_ruid;
- } else {
- LOG(ERROR) << "Could not read status file and kernel did not supply UID";
- LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
- << base::DirectoryExists(process_path);
- return false;
- }
-
- if (!GetCreatedCrashDirectoryByEuid(uid, crash_file_path, out_of_capacity)) {
- LOG(ERROR) << "Could not create crash directory";
- return false;
- }
- return true;
-}
-
-bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
- // Copy off all stdin to a core file.
- FilePath stdin_path("/dev/fd/0");
- if (base::CopyFile(stdin_path, core_path)) {
- return true;
- }
-
- PLOG(ERROR) << "Could not write core file";
- // If the file system was full, make sure we remove any remnants.
- base::DeleteFile(core_path, false);
- return false;
-}
-
-bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
- const FilePath &procfs_directory,
- const FilePath &minidump_path,
- const FilePath &temp_directory) {
- FilePath output_path = temp_directory.Append("output");
- chromeos::ProcessImpl core2md;
- core2md.RedirectOutput(output_path.value());
- core2md.AddArg(kCoreToMinidumpConverterPath);
- core2md.AddArg(core_path.value());
- core2md.AddArg(procfs_directory.value());
-
- if (!core2md_failure_) {
- core2md.AddArg(minidump_path.value());
- } else {
- // To test how core2md errors are propagaged, cause an error
- // by forgetting a required argument.
- }
-
- int errorlevel = core2md.Run();
-
- std::string output;
- base::ReadFileToString(output_path, &output);
- if (errorlevel != 0) {
- LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
- << " [result=" << errorlevel << "]: " << output;
- return false;
- }
-
- if (!base::PathExists(minidump_path)) {
- LOG(ERROR) << "Minidump file " << minidump_path.value()
- << " was not created";
- return false;
- }
- return true;
-}
-
-UserCollector::ErrorType UserCollector::ConvertCoreToMinidump(
- pid_t pid,
- const FilePath &container_dir,
- const FilePath &core_path,
- const FilePath &minidump_path) {
- // If proc files are unuable, we continue to read the core file from stdin,
- // but only skip the core-to-minidump conversion, so that we may still use
- // the core file for debugging.
- bool proc_files_usable =
- CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
-
- if (!CopyStdinToCoreFile(core_path)) {
- return kErrorReadCoreData;
- }
-
- if (!proc_files_usable) {
- LOG(INFO) << "Skipped converting core file to minidump due to "
- << "unusable proc files";
- return kErrorUnusableProcFiles;
- }
-
- ErrorType error = ValidateCoreFile(core_path);
- if (error != kErrorNone) {
- return error;
- }
-
- if (!RunCoreToMinidump(core_path,
- container_dir, // procfs directory
- minidump_path,
- container_dir)) { // temporary directory
- return kErrorCore2MinidumpConversion;
- }
-
- LOG(INFO) << "Stored minidump to " << minidump_path.value();
- return kErrorNone;
-}
-
-UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash(
- pid_t pid, const std::string &exec, uid_t supplied_ruid,
- bool *out_of_capacity) {
- FilePath crash_path;
- if (!GetCreatedCrashDirectory(pid, supplied_ruid, &crash_path,
- out_of_capacity)) {
- LOG(ERROR) << "Unable to find/create process-specific crash path";
- return kErrorSystemIssue;
- }
-
- // Directory like /tmp/crash_reporter/1234 which contains the
- // procfs entries and other temporary files used during conversion.
- FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", pid));
- // Delete a pre-existing directory from crash reporter that may have
- // been left around for diagnostics from a failed conversion attempt.
- // If we don't, existing files can cause forking to fail.
- base::DeleteFile(container_dir, true);
- std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
- FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
- FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
- FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
- FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
-
- if (GetLogContents(FilePath(log_config_path_), exec, log_path))
- AddCrashMetaData("log", log_path.value());
-
- ErrorType error_type =
- ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
- if (error_type != kErrorNone) {
- LOG(INFO) << "Leaving core file at " << core_path.value()
- << " due to conversion error";
- return error_type;
- }
-
- // Here we commit to sending this file. We must not return false
- // after this point or we will generate a log report as well as a
- // crash report.
- WriteCrashMetaData(meta_path,
- exec,
- minidump_path.value());
-
- if (!IsDeveloperImage()) {
- base::DeleteFile(core_path, false);
- } else {
- LOG(INFO) << "Leaving core file at " << core_path.value()
- << " due to developer image";
- }
-
- base::DeleteFile(container_dir, true);
- return kErrorNone;
-}
-
-bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
- pid_t *pid, int *signal, uid_t *uid,
- std::string *kernel_supplied_name) {
- pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(.*)");
- if (re.FullMatch(crash_attributes, pid, signal, uid, kernel_supplied_name))
- return true;
-
- LOG(INFO) << "Falling back to parsing crash attributes '"
- << crash_attributes << "' without UID";
- pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
- *uid = kUnknownUid;
- return re_without_uid.FullMatch(crash_attributes, pid, signal,
- kernel_supplied_name);
-}
-
-bool UserCollector::ShouldDump(bool has_owner_consent,
- bool is_developer,
- std::string *reason) {
- reason->clear();
-
- // For developer builds, we always want to keep the crash reports unless
- // we're testing the crash facilities themselves. This overrides
- // feedback. Crash sending still obeys consent.
- if (is_developer) {
- *reason = "developer build - not testing - always dumping";
- return true;
- }
-
- if (!has_owner_consent) {
- *reason = "ignoring - no consent";
- return false;
- }
-
- *reason = "handling";
- return true;
-}
-
-bool UserCollector::HandleCrash(const std::string &crash_attributes,
- const char *force_exec) {
- CHECK(initialized_);
- pid_t pid = 0;
- int signal = 0;
- uid_t supplied_ruid = kUnknownUid;
- std::string kernel_supplied_name;
-
- if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
- &kernel_supplied_name)) {
- LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
- return false;
- }
-
- std::string exec;
- if (force_exec) {
- exec.assign(force_exec);
- } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
- // If we cannot find the exec name, use the kernel supplied name.
- // We don't always use the kernel's since it truncates the name to
- // 16 characters.
- exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
- }
-
- // Allow us to test the crash reporting mechanism successfully even if
- // other parts of the system crash.
- if (!filter_in_.empty() &&
- (filter_in_ == "none" ||
- filter_in_ != exec)) {
- // We use a different format message to make it more obvious in tests
- // which crashes are test generated and which are real.
- LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
- << "filter_in=" << filter_in_ << ".";
- return true;
- }
-
- std::string reason;
- bool dump = ShouldDump(is_feedback_allowed_function_(),
- IsDeveloperImage(),
- &reason);
-
- LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
- << "] sig " << signal << ", user " << supplied_ruid
- << " (" << reason << ")";
-
- if (dump) {
- count_crash_function_();
-
- if (generate_diagnostics_) {
- bool out_of_capacity = false;
- ErrorType error_type =
- ConvertAndEnqueueCrash(pid, exec, supplied_ruid, &out_of_capacity);
- if (error_type != kErrorNone) {
- if (!out_of_capacity)
- EnqueueCollectionErrorLog(pid, error_type, exec);
- return false;
- }
- }
- }
-
- return true;
-}
diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h
deleted file mode 100644
index 7b356ee..0000000
--- a/crash_reporter/user_collector.h
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRASH_REPORTER_USER_COLLECTOR_H_
-#define CRASH_REPORTER_USER_COLLECTOR_H_
-
-#include <string>
-#include <vector>
-
-#include <base/files/file_path.h>
-#include <base/macros.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-#include "crash_collector.h"
-
-class SystemLogging;
-
-// User crash collector.
-class UserCollector : public CrashCollector {
- public:
- UserCollector();
-
- // Initialize the user crash collector for detection of crashes,
- // given a crash counting function, the path to this executable,
- // metrics collection enabled oracle, and system logger facility.
- // Crash detection/reporting is not enabled until Enable is called.
- // |generate_diagnostics| is used to indicate whether or not to try
- // to generate a minidump from crashes.
- void Initialize(CountCrashFunction count_crash,
- const std::string &our_path,
- IsFeedbackAllowedFunction is_metrics_allowed,
- bool generate_diagnostics,
- bool core2md_failure,
- bool directory_failure,
- const std::string &filter_in);
-
- ~UserCollector() override;
-
- // Enable collection.
- bool Enable() { return SetUpInternal(true); }
-
- // Disable collection.
- bool Disable() { return SetUpInternal(false); }
-
- // Handle a specific user crash. Returns true on success.
- bool HandleCrash(const std::string &crash_attributes,
- const char *force_exec);
-
- // Set (override the default) core file pattern.
- void set_core_pattern_file(const std::string &pattern) {
- core_pattern_file_ = pattern;
- }
-
- // Set (override the default) core pipe limit file.
- void set_core_pipe_limit_file(const std::string &path) {
- core_pipe_limit_file_ = path;
- }
-
- private:
- friend class UserCollectorTest;
- FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath);
- FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid);
- FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK);
- FRIEND_TEST(UserCollectorTest, GetExecutableBaseNameFromPid);
- FRIEND_TEST(UserCollectorTest, GetFirstLineWithPrefix);
- FRIEND_TEST(UserCollectorTest, GetIdFromStatus);
- FRIEND_TEST(UserCollectorTest, GetStateFromStatus);
- FRIEND_TEST(UserCollectorTest, GetProcessPath);
- FRIEND_TEST(UserCollectorTest, GetSymlinkTarget);
- FRIEND_TEST(UserCollectorTest, GetUserInfoFromName);
- FRIEND_TEST(UserCollectorTest, ParseCrashAttributes);
- FRIEND_TEST(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage);
- FRIEND_TEST(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent);
- FRIEND_TEST(UserCollectorTest, ShouldDumpUseConsentProductionImage);
- FRIEND_TEST(UserCollectorTest, ValidateProcFiles);
- FRIEND_TEST(UserCollectorTest, ValidateCoreFile);
-
- // Enumeration to pass to GetIdFromStatus. Must match the order
- // that the kernel lists IDs in the status file.
- enum IdKind {
- kIdReal = 0, // uid and gid
- kIdEffective = 1, // euid and egid
- kIdSet = 2, // suid and sgid
- kIdFileSystem = 3, // fsuid and fsgid
- kIdMax
- };
-
- enum ErrorType {
- kErrorNone,
- kErrorSystemIssue,
- kErrorReadCoreData,
- kErrorUnusableProcFiles,
- kErrorInvalidCoreFile,
- kErrorUnsupported32BitCoreFile,
- kErrorCore2MinidumpConversion,
- };
-
- static const int kForkProblem = 255;
-
- // Returns an error type signature for a given |error_type| value,
- // which is reported to the crash server along with the
- // crash_reporter-user-collection signature.
- std::string GetErrorTypeSignature(ErrorType error_type) const;
-
- std::string GetPattern(bool enabled) const;
- bool SetUpInternal(bool enabled);
-
- // Returns, via |line|, the first line in |lines| that starts with |prefix|.
- // Returns true if a line is found, or false otherwise.
- bool GetFirstLineWithPrefix(const std::vector<std::string> &lines,
- const char *prefix, std::string *line);
-
- // Returns the identifier of |kind|, via |id|, found in |status_lines| on
- // the line starting with |prefix|. |status_lines| contains the lines in
- // the status file. Returns true if the identifier can be determined.
- bool GetIdFromStatus(const char *prefix,
- IdKind kind,
- const std::vector<std::string> &status_lines,
- int *id);
-
- // Returns the process state, via |state|, found in |status_lines|, which
- // contains the lines in the status file. Returns true if the process state
- // can be determined.
- bool GetStateFromStatus(const std::vector<std::string> &status_lines,
- std::string *state);
-
- void LogCollectionError(const std::string &error_message);
- void EnqueueCollectionErrorLog(pid_t pid, ErrorType error_type,
- const std::string &exec_name);
-
- bool CopyOffProcFiles(pid_t pid, const base::FilePath &container_dir);
-
- // Validates the proc files at |container_dir| and returns true if they
- // are usable for the core-to-minidump conversion later. For instance, if
- // a process is reaped by the kernel before the copying of its proc files
- // takes place, some proc files like /proc/<pid>/maps may contain nothing
- // and thus become unusable.
- bool ValidateProcFiles(const base::FilePath &container_dir) const;
-
- // Validates the core file at |core_path| and returns kErrorNone if
- // the file contains the ELF magic bytes and an ELF class that matches the
- // platform (i.e. 32-bit ELF on a 32-bit platform or 64-bit ELF on a 64-bit
- // platform), which is due to the limitation in core2md. It returns an error
- // type otherwise.
- ErrorType ValidateCoreFile(const base::FilePath &core_path) const;
-
- // Determines the crash directory for given pid based on pid's owner,
- // and creates the directory if necessary with appropriate permissions.
- // Returns true whether or not directory needed to be created, false on
- // any failure.
- bool GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
- base::FilePath *crash_file_path,
- bool *out_of_capacity);
- bool CopyStdinToCoreFile(const base::FilePath &core_path);
- bool RunCoreToMinidump(const base::FilePath &core_path,
- const base::FilePath &procfs_directory,
- const base::FilePath &minidump_path,
- const base::FilePath &temp_directory);
- ErrorType ConvertCoreToMinidump(pid_t pid,
- const base::FilePath &container_dir,
- const base::FilePath &core_path,
- const base::FilePath &minidump_path);
- ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name,
- uid_t supplied_ruid, bool *out_of_capacity);
- bool ParseCrashAttributes(const std::string &crash_attributes,
- pid_t *pid, int *signal, uid_t *uid,
- std::string *kernel_supplied_name);
-
- bool ShouldDump(bool has_owner_consent,
- bool is_developer,
- std::string *reason);
-
- bool generate_diagnostics_;
- std::string core_pattern_file_;
- std::string core_pipe_limit_file_;
- std::string our_path_;
- bool initialized_;
-
- bool core2md_failure_;
- bool directory_failure_;
- std::string filter_in_;
-
- static const char *kUserId;
- static const char *kGroupId;
-
- DISALLOW_COPY_AND_ASSIGN(UserCollector);
-};
-
-#endif // CRASH_REPORTER_USER_COLLECTOR_H_
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
deleted file mode 100644
index ee3ca12..0000000
--- a/crash_reporter/user_collector_test.cc
+++ /dev/null
@@ -1,492 +0,0 @@
-// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "user_collector.h"
-
-#include <elf.h>
-#include <sys/cdefs.h> // For __WORDSIZE
-#include <unistd.h>
-
-#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
-#include <base/strings/string_split.h>
-#include <chromeos/syslog_logging.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-using base::FilePath;
-using chromeos::FindLog;
-
-namespace {
-
-int s_crashes = 0;
-bool s_metrics = false;
-
-const char kFilePath[] = "/my/path";
-
-// Keep in sync with UserCollector::ShouldDump.
-const char kChromeIgnoreMsg[] =
- "ignoring call by kernel - chrome crash; "
- "waiting for chrome to call us directly";
-
-void CountCrash() {
- ++s_crashes;
-}
-
-bool IsMetrics() {
- return s_metrics;
-}
-
-} // namespace
-
-class UserCollectorMock : public UserCollector {
- public:
- MOCK_METHOD0(SetUpDBus, void());
-};
-
-class UserCollectorTest : public ::testing::Test {
- void SetUp() {
- s_crashes = 0;
-
- EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
-
- collector_.Initialize(CountCrash,
- kFilePath,
- IsMetrics,
- false,
- false,
- false,
- "");
- base::DeleteFile(FilePath("test"), true);
- mkdir("test", 0777);
- collector_.set_core_pattern_file("test/core_pattern");
- collector_.set_core_pipe_limit_file("test/core_pipe_limit");
- pid_ = getpid();
- chromeos::ClearLog();
- }
-
- protected:
- void ExpectFileEquals(const char *golden,
- const FilePath &file_path) {
- std::string contents;
- EXPECT_TRUE(base::ReadFileToString(file_path, &contents));
- EXPECT_EQ(golden, contents);
- }
-
- std::vector<std::string> SplitLines(const std::string &lines) const {
- std::vector<std::string> result;
- base::SplitString(lines, '\n', &result);
- return result;
- }
-
- UserCollectorMock collector_;
- pid_t pid_;
-};
-
-TEST_F(UserCollectorTest, EnableOK) {
- ASSERT_TRUE(collector_.Enable());
- ExpectFileEquals("|/my/path --user=%P:%s:%u:%e",
- FilePath("test/core_pattern"));
- ExpectFileEquals("4", FilePath("test/core_pipe_limit"));
- ASSERT_EQ(s_crashes, 0);
- EXPECT_TRUE(FindLog("Enabling user crash handling"));
-}
-
-TEST_F(UserCollectorTest, EnableNoPatternFileAccess) {
- collector_.set_core_pattern_file("/does_not_exist");
- ASSERT_FALSE(collector_.Enable());
- ASSERT_EQ(s_crashes, 0);
- EXPECT_TRUE(FindLog("Enabling user crash handling"));
- EXPECT_TRUE(FindLog("Unable to write /does_not_exist"));
-}
-
-TEST_F(UserCollectorTest, EnableNoPipeLimitFileAccess) {
- collector_.set_core_pipe_limit_file("/does_not_exist");
- ASSERT_FALSE(collector_.Enable());
- ASSERT_EQ(s_crashes, 0);
- // Core pattern should not be written if we cannot access the pipe limit
- // or otherwise we may set a pattern that results in infinite recursion.
- ASSERT_FALSE(base::PathExists(FilePath("test/core_pattern")));
- EXPECT_TRUE(FindLog("Enabling user crash handling"));
- EXPECT_TRUE(FindLog("Unable to write /does_not_exist"));
-}
-
-TEST_F(UserCollectorTest, DisableOK) {
- ASSERT_TRUE(collector_.Disable());
- ExpectFileEquals("core", FilePath("test/core_pattern"));
- ASSERT_EQ(s_crashes, 0);
- EXPECT_TRUE(FindLog("Disabling user crash handling"));
-}
-
-TEST_F(UserCollectorTest, DisableNoFileAccess) {
- collector_.set_core_pattern_file("/does_not_exist");
- ASSERT_FALSE(collector_.Disable());
- ASSERT_EQ(s_crashes, 0);
- EXPECT_TRUE(FindLog("Disabling user crash handling"));
- EXPECT_TRUE(FindLog("Unable to write /does_not_exist"));
-}
-
-TEST_F(UserCollectorTest, ParseCrashAttributes) {
- pid_t pid;
- int signal;
- uid_t uid;
- std::string exec_name;
- EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:foobar",
- &pid, &signal, &uid, &exec_name));
- EXPECT_EQ(123456, pid);
- EXPECT_EQ(11, signal);
- EXPECT_EQ(1000, uid);
- EXPECT_EQ("foobar", exec_name);
- EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
- &pid, &signal, &uid, &exec_name));
- EXPECT_EQ(4321, pid);
- EXPECT_EQ(6, signal);
- EXPECT_EQ(-1, uid);
- EXPECT_EQ("barfoo", exec_name);
-
- EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
- &pid, &signal, &uid, &exec_name));
-
- EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra",
- &pid, &signal, &uid, &exec_name));
- EXPECT_EQ("exec:extra", exec_name);
-
- EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar",
- &pid, &signal, &uid, &exec_name));
-
- EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar",
- &pid, &signal, &uid, &exec_name));
-
- EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar",
- &pid, &signal, &uid, &exec_name));
-}
-
-TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) {
- std::string reason;
- EXPECT_TRUE(collector_.ShouldDump(false, true, &reason));
- EXPECT_EQ("developer build - not testing - always dumping", reason);
-
- // When running a crash test, behave as normal.
- EXPECT_FALSE(collector_.ShouldDump(false, false, &reason));
- EXPECT_EQ("ignoring - no consent", reason);
-}
-
-TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) {
- std::string result;
- EXPECT_FALSE(collector_.ShouldDump(false, false, &result));
- EXPECT_EQ("ignoring - no consent", result);
-
- EXPECT_TRUE(collector_.ShouldDump(true, false, &result));
- EXPECT_EQ("handling", result);
-}
-
-TEST_F(UserCollectorTest, HandleCrashWithoutConsent) {
- s_metrics = false;
- collector_.HandleCrash("20:10:ignored", "foobar");
- EXPECT_TRUE(FindLog(
- "Received crash notification for foobar[20] sig 10"));
- ASSERT_EQ(s_crashes, 0);
-}
-
-TEST_F(UserCollectorTest, HandleNonChromeCrashWithConsent) {
- s_metrics = true;
- collector_.HandleCrash("5:2:ignored", "chromeos-wm");
- EXPECT_TRUE(FindLog(
- "Received crash notification for chromeos-wm[5] sig 2"));
- ASSERT_EQ(s_crashes, 1);
-}
-
-TEST_F(UserCollectorTest, HandleChromeCrashWithConsent) {
- s_metrics = true;
- collector_.HandleCrash("5:2:ignored", "chrome");
- EXPECT_TRUE(FindLog(
- "Received crash notification for chrome[5] sig 2"));
- EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
- ASSERT_EQ(s_crashes, 0);
-}
-
-TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithConsent) {
- s_metrics = true;
- collector_.HandleCrash("0:2:chrome", nullptr);
- EXPECT_TRUE(FindLog(
- "Received crash notification for supplied_chrome[0] sig 2"));
- EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
- ASSERT_EQ(s_crashes, 0);
-}
-
-TEST_F(UserCollectorTest, GetProcessPath) {
- FilePath path = collector_.GetProcessPath(100);
- ASSERT_EQ("/proc/100", path.value());
-}
-
-TEST_F(UserCollectorTest, GetSymlinkTarget) {
- FilePath result;
- ASSERT_FALSE(collector_.GetSymlinkTarget(FilePath("/does_not_exist"),
- &result));
- ASSERT_TRUE(FindLog(
- "Readlink failed on /does_not_exist with 2"));
- std::string long_link;
- for (int i = 0; i < 50; ++i)
- long_link += "0123456789";
- long_link += "/gold";
-
- for (size_t len = 1; len <= long_link.size(); ++len) {
- std::string this_link;
- static const char kLink[] = "test/this_link";
- this_link.assign(long_link.c_str(), len);
- ASSERT_EQ(len, this_link.size());
- unlink(kLink);
- ASSERT_EQ(0, symlink(this_link.c_str(), kLink));
- ASSERT_TRUE(collector_.GetSymlinkTarget(FilePath(kLink), &result));
- ASSERT_EQ(this_link, result.value());
- }
-}
-
-TEST_F(UserCollectorTest, GetExecutableBaseNameFromPid) {
- std::string base_name;
- EXPECT_FALSE(collector_.GetExecutableBaseNameFromPid(0, &base_name));
- EXPECT_TRUE(FindLog(
- "Readlink failed on /proc/0/exe with 2"));
- EXPECT_TRUE(FindLog(
- "GetSymlinkTarget failed - Path /proc/0 DirectoryExists: 0"));
- EXPECT_TRUE(FindLog("stat /proc/0/exe failed: -1 2"));
-
- chromeos::ClearLog();
- pid_t my_pid = getpid();
- EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name));
- EXPECT_FALSE(FindLog("Readlink failed"));
- EXPECT_EQ("crash_reporter_test", base_name);
-}
-
-TEST_F(UserCollectorTest, GetFirstLineWithPrefix) {
- std::vector<std::string> lines;
- std::string line;
-
- EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line));
- EXPECT_EQ("", line);
-
- lines.push_back("Name:\tls");
- lines.push_back("State:\tR (running)");
- lines.push_back(" Foo:\t1000");
-
- line.clear();
- EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line));
- EXPECT_EQ(lines[0], line);
-
- line.clear();
- EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "State:", &line));
- EXPECT_EQ(lines[1], line);
-
- line.clear();
- EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Foo:", &line));
- EXPECT_EQ("", line);
-
- line.clear();
- EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, " Foo:", &line));
- EXPECT_EQ(lines[2], line);
-
- line.clear();
- EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Bar:", &line));
- EXPECT_EQ("", line);
-}
-
-TEST_F(UserCollectorTest, GetIdFromStatus) {
- int id = 1;
- EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
- UserCollector::kIdEffective,
- SplitLines("nothing here"),
- &id));
- EXPECT_EQ(id, 1);
-
- // Not enough parameters.
- EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
- UserCollector::kIdReal,
- SplitLines("line 1\nUid:\t1\n"),
- &id));
-
- const std::vector<std::string> valid_contents =
- SplitLines("\nUid:\t1\t2\t3\t4\nGid:\t5\t6\t7\t8\n");
- EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
- UserCollector::kIdReal,
- valid_contents,
- &id));
- EXPECT_EQ(1, id);
-
- EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
- UserCollector::kIdEffective,
- valid_contents,
- &id));
- EXPECT_EQ(2, id);
-
- EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
- UserCollector::kIdFileSystem,
- valid_contents,
- &id));
- EXPECT_EQ(4, id);
-
- EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId,
- UserCollector::kIdEffective,
- valid_contents,
- &id));
- EXPECT_EQ(6, id);
-
- EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId,
- UserCollector::kIdSet,
- valid_contents,
- &id));
- EXPECT_EQ(7, id);
-
- EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId,
- UserCollector::IdKind(5),
- valid_contents,
- &id));
- EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId,
- UserCollector::IdKind(-1),
- valid_contents,
- &id));
-
- // Fail if junk after number
- EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
- UserCollector::kIdReal,
- SplitLines("Uid:\t1f\t2\t3\t4\n"),
- &id));
- EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
- UserCollector::kIdReal,
- SplitLines("Uid:\t1\t2\t3\t4\n"),
- &id));
- EXPECT_EQ(1, id);
-
- // Fail if more than 4 numbers.
- EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
- UserCollector::kIdReal,
- SplitLines("Uid:\t1\t2\t3\t4\t5\n"),
- &id));
-}
-
-TEST_F(UserCollectorTest, GetStateFromStatus) {
- std::string state;
- EXPECT_FALSE(collector_.GetStateFromStatus(SplitLines("nothing here"),
- &state));
- EXPECT_EQ("", state);
-
- EXPECT_TRUE(collector_.GetStateFromStatus(SplitLines("State:\tR (running)"),
- &state));
- EXPECT_EQ("R (running)", state);
-
- EXPECT_TRUE(collector_.GetStateFromStatus(
- SplitLines("Name:\tls\nState:\tZ (zombie)\n"), &state));
- EXPECT_EQ("Z (zombie)", state);
-}
-
-TEST_F(UserCollectorTest, GetUserInfoFromName) {
- gid_t gid = 100;
- uid_t uid = 100;
- EXPECT_TRUE(collector_.GetUserInfoFromName("root", &uid, &gid));
- EXPECT_EQ(0, uid);
- EXPECT_EQ(0, gid);
-}
-
-TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) {
- // Try a path that is not writable.
- ASSERT_FALSE(collector_.CopyOffProcFiles(pid_, FilePath("/bad/path")));
- EXPECT_TRUE(FindLog("Could not create /bad/path"));
-}
-
-TEST_F(UserCollectorTest, CopyOffProcFilesBadPid) {
- FilePath container_path("test/container");
- ASSERT_FALSE(collector_.CopyOffProcFiles(0, container_path));
- EXPECT_TRUE(FindLog("Path /proc/0 does not exist"));
-}
-
-TEST_F(UserCollectorTest, CopyOffProcFilesOK) {
- FilePath container_path("test/container");
- ASSERT_TRUE(collector_.CopyOffProcFiles(pid_, container_path));
- EXPECT_FALSE(FindLog("Could not copy"));
- static struct {
- const char *name;
- bool exists;
- } expectations[] = {
- { "auxv", true },
- { "cmdline", true },
- { "environ", true },
- { "maps", true },
- { "mem", false },
- { "mounts", false },
- { "sched", false },
- { "status", true }
- };
- for (unsigned i = 0; i < sizeof(expectations)/sizeof(expectations[0]); ++i) {
- EXPECT_EQ(expectations[i].exists,
- base::PathExists(
- container_path.Append(expectations[i].name)));
- }
-}
-
-TEST_F(UserCollectorTest, ValidateProcFiles) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath container_dir = temp_dir.path();
-
- // maps file not exists (i.e. GetFileSize fails)
- EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
-
- // maps file is empty
- FilePath maps_file = container_dir.Append("maps");
- ASSERT_EQ(0, base::WriteFile(maps_file, nullptr, 0));
- ASSERT_TRUE(base::PathExists(maps_file));
- EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
-
- // maps file is not empty
- const char data[] = "test data";
- ASSERT_EQ(sizeof(data), base::WriteFile(maps_file, data, sizeof(data)));
- ASSERT_TRUE(base::PathExists(maps_file));
- EXPECT_TRUE(collector_.ValidateProcFiles(container_dir));
-}
-
-TEST_F(UserCollectorTest, ValidateCoreFile) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath container_dir = temp_dir.path();
- FilePath core_file = container_dir.Append("core");
-
- // Core file does not exist
- EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
- collector_.ValidateCoreFile(core_file));
- char e_ident[EI_NIDENT];
- e_ident[EI_MAG0] = ELFMAG0;
- e_ident[EI_MAG1] = ELFMAG1;
- e_ident[EI_MAG2] = ELFMAG2;
- e_ident[EI_MAG3] = ELFMAG3;
-#if __WORDSIZE == 32
- e_ident[EI_CLASS] = ELFCLASS32;
-#elif __WORDSIZE == 64
- e_ident[EI_CLASS] = ELFCLASS64;
-#else
-#error Unknown/unsupported value of __WORDSIZE.
-#endif
-
- // Core file has the expected header
- ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
- EXPECT_EQ(UserCollector::kErrorNone,
- collector_.ValidateCoreFile(core_file));
-
-#if __WORDSIZE == 64
- // 32-bit core file on 64-bit platform
- e_ident[EI_CLASS] = ELFCLASS32;
- ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
- EXPECT_EQ(UserCollector::kErrorUnsupported32BitCoreFile,
- collector_.ValidateCoreFile(core_file));
- e_ident[EI_CLASS] = ELFCLASS64;
-#endif
-
- // Invalid core files
- ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident) - 1));
- EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
- collector_.ValidateCoreFile(core_file));
-
- e_ident[EI_MAG0] = 0;
- ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
- EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
- collector_.ValidateCoreFile(core_file));
-}
diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l
deleted file mode 100644
index de746fe..0000000
--- a/crash_reporter/warn_collector.l
+++ /dev/null
@@ -1,324 +0,0 @@
-/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * This flex program reads /var/log/messages as it grows and saves kernel
- * warnings to files. It keeps track of warnings it has seen (based on
- * file/line only, ignoring differences in the stack trace), and reports only
- * the first warning of each kind, but maintains a count of all warnings by
- * using their hashes as buckets in a UMA sparse histogram. It also invokes
- * the crash collector, which collects the warnings and prepares them for later
- * shipment to the crash server.
- */
-
-%option noyywrap
-
-%{
-#include <fcntl.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <sys/inotify.h>
-#include <sys/select.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "metrics/c_metrics_library.h"
-
-int WarnStart(void);
-void WarnEnd(void);
-void WarnInput(char *buf, yy_size_t *result, size_t max_size);
-
-#define YY_INPUT(buf, result, max_size) WarnInput(buf, &result, max_size)
-
-%}
-
-/* Define a few useful regular expressions. */
-
-D [0-9]
-PREFIX .*" kernel: [ "*{D}+"."{D}+"]"
-CUT_HERE {PREFIX}" ------------[ cut here".*
-WARNING {PREFIX}" WARNING: at "
-END_TRACE {PREFIX}" ---[ end trace".*
-
-/* Use exclusive start conditions. */
-%x PRE_WARN WARN
-
-%%
- /* The scanner itself. */
-
-^{CUT_HERE}\n{WARNING} BEGIN(PRE_WARN);
-.|\n /* ignore all other input in state 0 */
-<PRE_WARN>[^ ]+.[^ ]+\n if (WarnStart()) {
- /* yytext is
- "file:line func+offset/offset()\n" */
- BEGIN(WARN); ECHO;
- } else {
- BEGIN(0);
- }
-
- /* Assume the warning ends at the "end trace" line */
-<WARN>^{END_TRACE}\n ECHO; BEGIN(0); WarnEnd();
-<WARN>^.*\n ECHO;
-
-%%
-
-#define HASH_BITMAP_SIZE (1 << 15) /* size in bits */
-#define HASH_BITMAP_MASK (HASH_BITMAP_SIZE - 1)
-
-const char warn_hist_name[] = "Platform.KernelWarningHashes";
-uint32_t hash_bitmap[HASH_BITMAP_SIZE / 32];
-CMetricsLibrary metrics_library;
-
-const char *prog_name; /* the name of this program */
-int yyin_fd; /* instead of FILE *yyin to avoid buffering */
-int i_fd; /* for inotify, to detect file changes */
-int testing; /* 1 if running test */
-int filter; /* 1 when using as filter (for development) */
-int fifo; /* 1 when reading from fifo (for devel) */
-int draining; /* 1 when draining renamed log file */
-
-const char *msg_path = "/var/log/messages";
-const char warn_dump_dir[] = "/var/run/kwarn";
-const char *warn_dump_path = "/var/run/kwarn/warning";
-const char *crash_reporter_command;
-
-__attribute__((__format__(__printf__, 1, 2)))
-static void Die(const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- fprintf(stderr, "%s: ", prog_name);
- vfprintf(stderr, format, ap);
- exit(1);
-}
-
-static void RunCrashReporter(void) {
- int status = system(crash_reporter_command);
- if (status != 0)
- Die("%s exited with status %d\n", crash_reporter_command, status);
-}
-
-static uint32_t StringHash(const char *string) {
- uint32_t hash = 0;
- while (*string != '\0') {
- hash = (hash << 5) + hash + *string++;
- }
- return hash;
-}
-
-/* We expect only a handful of different warnings per boot session, so the
- * probability of a collision is very low, and statistically it won't matter
- * (unless warnings with the same hash also happens in tandem, which is even
- * rarer).
- */
-static int HashSeen(uint32_t hash) {
- int word_index = (hash & HASH_BITMAP_MASK) / 32;
- int bit_index = (hash & HASH_BITMAP_MASK) % 32;
- return hash_bitmap[word_index] & 1 << bit_index;
-}
-
-static void SetHashSeen(uint32_t hash) {
- int word_index = (hash & HASH_BITMAP_MASK) / 32;
- int bit_index = (hash & HASH_BITMAP_MASK) % 32;
- hash_bitmap[word_index] |= 1 << bit_index;
-}
-
-#pragma GCC diagnostic ignored "-Wwrite-strings"
-int WarnStart(void) {
- uint32_t hash;
- char *spacep;
-
- if (filter)
- return 1;
-
- hash = StringHash(yytext);
- if (!(testing || fifo || filter)) {
- CMetricsLibrarySendSparseToUMA(metrics_library, warn_hist_name, (int) hash);
- }
- if (HashSeen(hash))
- return 0;
- SetHashSeen(hash);
-
- yyout = fopen(warn_dump_path, "w");
- if (yyout == NULL)
- Die("fopen %s failed: %s\n", warn_dump_path, strerror(errno));
- spacep = strchr(yytext, ' ');
- if (spacep == NULL || spacep[1] == '\0')
- spacep = "unknown-function";
- fprintf(yyout, "%08x-%s\n", hash, spacep + 1);
- return 1;
-}
-
-void WarnEnd(void) {
- if (filter)
- return;
- fclose(yyout);
- yyout = stdout; /* for debugging */
- RunCrashReporter();
-}
-
-static void WarnOpenInput(const char *path) {
- yyin_fd = open(path, O_RDONLY);
- if (yyin_fd < 0)
- Die("could not open %s: %s\n", path, strerror(errno));
- if (!fifo) {
- /* Go directly to the end of the file. We don't want to parse the same
- * warnings multiple times on reboot/restart. We might miss some
- * warnings, but so be it---it's too hard to keep track reliably of the
- * last parsed position in the syslog.
- */
- if (lseek(yyin_fd, 0, SEEK_END) < 0)
- Die("could not lseek %s: %s\n", path, strerror(errno));
- /* Set up notification of file growth and rename. */
- i_fd = inotify_init();
- if (i_fd < 0)
- Die("inotify_init: %s\n", strerror(errno));
- if (inotify_add_watch(i_fd, path, IN_MODIFY | IN_MOVE_SELF) < 0)
- Die("inotify_add_watch: %s\n", strerror(errno));
- }
-}
-
-/* We replace the default YY_INPUT() for the following reasons:
- *
- * 1. We want to read data as soon as it becomes available, but the default
- * YY_INPUT() uses buffered I/O.
- *
- * 2. We want to block on end of input and wait for the file to grow.
- *
- * 3. We want to detect log rotation, and reopen the input file as needed.
- */
-void WarnInput(char *buf, yy_size_t *result, size_t max_size) {
- while (1) {
- ssize_t ret = read(yyin_fd, buf, max_size);
- if (ret < 0)
- Die("read: %s", strerror(errno));
- *result = ret;
- if (*result > 0 || fifo || filter)
- return;
- if (draining) {
- /* Assume we're done with this log, and move to next
- * log. Rsyslogd may keep writing to the old log file
- * for a while, but we don't care since we don't have
- * to be exact.
- */
- close(yyin_fd);
- if (YYSTATE == WARN) {
- /* Be conservative in case we lose the warn
- * terminator during the switch---or we may
- * collect personally identifiable information.
- */
- WarnEnd();
- }
- BEGIN(0); /* see above comment */
- sleep(1); /* avoid race with log rotator */
- WarnOpenInput(msg_path);
- draining = 0;
- continue;
- }
- /* Nothing left to read, so we must wait. */
- struct inotify_event event;
- while (1) {
- int n = read(i_fd, &event, sizeof(event));
- if (n <= 0) {
- if (errno == EINTR)
- continue;
- else
- Die("inotify: %s\n", strerror(errno));
- } else
- break;
- }
- if (event.mask & IN_MOVE_SELF) {
- /* The file has been renamed. Before switching
- * to the new one, we process any remaining
- * content of this file.
- */
- draining = 1;
- }
- }
-}
-
-int main(int argc, char **argv) {
- int result;
- struct passwd *user;
- prog_name = argv[0];
-
- if (argc == 2 && strcmp(argv[1], "--test") == 0)
- testing = 1;
- else if (argc == 2 && strcmp(argv[1], "--filter") == 0)
- filter = 1;
- else if (argc == 2 && strcmp(argv[1], "--fifo") == 0) {
- fifo = 1;
- } else if (argc != 1) {
- fprintf(stderr,
- "usage: %s [single-flag]\n"
- "flags (for testing only):\n"
- "--fifo\tinput is fifo \"fifo\", output is stdout\n"
- "--filter\tinput is stdin, output is stdout\n"
- "--test\trun self-test\n",
- prog_name);
- exit(1);
- }
-
- metrics_library = CMetricsLibraryNew();
- CMetricsLibraryInit(metrics_library);
-
- crash_reporter_command = testing ?
- "./warn_collector_test_reporter.sh" :
- "/sbin/crash_reporter --kernel_warning";
-
- /* When filtering with --filter (for development) use stdin for input.
- * Otherwise read input from a file or a fifo.
- */
- yyin_fd = fileno(stdin);
- if (testing) {
- msg_path = "messages";
- warn_dump_path = "warning";
- }
- if (fifo) {
- msg_path = "fifo";
- }
- if (!filter) {
- WarnOpenInput(msg_path);
- }
-
- /* Create directory for dump file. Still need to be root here. */
- unlink(warn_dump_path);
- if (!testing && !fifo && !filter) {
- rmdir(warn_dump_dir);
- result = mkdir(warn_dump_dir, 0755);
- if (result < 0)
- Die("could not create %s: %s\n",
- warn_dump_dir, strerror(errno));
- }
-
- if (0) {
- /* TODO(semenzato): put this back in once we decide it's safe
- * to make /var/spool/crash rwxrwxrwx root, or use a different
- * owner and setuid for the crash reporter as well.
- */
-
- /* Get low privilege uid, gid. */
- user = getpwnam("chronos");
- if (user == NULL)
- Die("getpwnam failed\n");
-
- /* Change dump directory ownership. */
- if (chown(warn_dump_dir, user->pw_uid, user->pw_gid) < 0)
- Die("chown: %s\n", strerror(errno));
-
- /* Drop privileges. */
- if (setuid(user->pw_uid) < 0) {
- Die("setuid: %s\n", strerror(errno));
- }
- }
-
- /* Go! */
- return yylex();
-}
-
-/* Flex should really know not to generate these functions.
- */
-void UnusedFunctionWarningSuppressor(void) {
- yyunput(0, 0);
-}
diff --git a/crash_reporter/warn_collector_test.c b/crash_reporter/warn_collector_test.c
deleted file mode 100644
index 7e25d01..0000000
--- a/crash_reporter/warn_collector_test.c
+++ /dev/null
@@ -1,14 +0,0 @@
-/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/*
- * Test driver for the warn_collector daemon.
- */
-#include <stdlib.h>
-
-int main(int ac, char **av) {
- int status = system("exec \"${SRC}\"/warn_collector_test.sh");
- return status < 0 ? EXIT_FAILURE : WEXITSTATUS(status);
-}
diff --git a/crash_reporter/warn_collector_test.sh b/crash_reporter/warn_collector_test.sh
deleted file mode 100755
index d9bb6f9..0000000
--- a/crash_reporter/warn_collector_test.sh
+++ /dev/null
@@ -1,79 +0,0 @@
-#! /bin/bash
-# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Test for warn_collector. Run the warn collector in the background, emulate
-# the kernel by appending lines to the log file "messages", and observe the log
-# of the (fake) crash reporter each time is run by the warn collector daemon.
-
-set -e
-
-fail() {
- printf '[ FAIL ] %b\n' "$*"
- exit 1
-}
-
-if [[ -z ${SYSROOT} ]]; then
- fail "SYSROOT must be set for this test to work"
-fi
-: ${OUT:=${PWD}}
-cd "${OUT}"
-PATH=${OUT}:${PATH}
-TESTLOG="${OUT}/warn-test-log"
-
-echo "Testing: $(which warn_collector)"
-
-cleanup() {
- # Kill daemon (if started) on exit
- kill %
-}
-
-check_log() {
- local n_expected=$1
- if [[ ! -f ${TESTLOG} ]]; then
- fail "${TESTLOG} was not created"
- fi
- if [[ $(wc -l < "${TESTLOG}") -ne ${n_expected} ]]; then
- fail "expected ${n_expected} lines in ${TESTLOG}, found this instead:
-$(<"${TESTLOG}")"
- fi
- if egrep -qv '^[0-9a-f]{8}' "${TESTLOG}"; then
- fail "found bad lines in ${TESTLOG}:
-$(<"${TESTLOG}")"
- fi
-}
-
-rm -f "${TESTLOG}"
-cp "${SRC}/warn_collector_test_reporter.sh" .
-cp "${SRC}/TEST_WARNING" .
-cp TEST_WARNING messages
-
-# Start the collector daemon. With the --test option, the daemon reads input
-# from ./messages, writes the warning into ./warning, and invokes
-# ./warn_collector_test_reporter.sh to report the warning.
-warn_collector --test &
-trap cleanup EXIT
-
-# After a while, check that the first warning has been collected.
-sleep 1
-check_log 1
-
-# Add the same warning to messages, verify that it is NOT collected
-cat TEST_WARNING >> messages
-sleep 1
-check_log 1
-
-# Add a slightly different warning to messages, check that it is collected.
-sed s/intel_dp.c/intel_xx.c/ < TEST_WARNING >> messages
-sleep 1
-check_log 2
-
-# Emulate log rotation, add a warning, and check.
-mv messages messages.1
-sed s/intel_dp.c/intel_xy.c/ < TEST_WARNING > messages
-sleep 2
-check_log 3
-
-# Success!
-exit 0
diff --git a/crash_reporter/warn_collector_test_reporter.sh b/crash_reporter/warn_collector_test_reporter.sh
deleted file mode 100755
index d8f3fad..0000000
--- a/crash_reporter/warn_collector_test_reporter.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#! /bin/sh
-# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Replacement for the crash reporter, for testing. Log the first line of the
-# "warning" file, which by convention contains the warning hash, and remove the
-# file.
-
-set -e
-
-exec 1>> warn-test-log
-exec 2>> warn-test-log
-
-head -1 warning
-rm warning
diff --git a/debuggerd/.clang-format b/debuggerd/.clang-format
new file mode 100644
index 0000000..9b7478c
--- /dev/null
+++ b/debuggerd/.clang-format
@@ -0,0 +1,15 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+ContinuationIndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
new file mode 100644
index 0000000..ca881aa
--- /dev/null
+++ b/debuggerd/Android.bp
@@ -0,0 +1,201 @@
+cc_defaults {
+ name: "debuggerd_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wno-error",
+ "-Wno-nullability-completeness",
+ "-Os",
+ ],
+
+ local_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "libdebuggerd_handler",
+ defaults: ["debuggerd_defaults"],
+ srcs: ["handler/debuggerd_handler.cpp"],
+
+ // libdebuggerd_handler gets async signal safe logging via libc_logging,
+ // which defines its interface in bionic private headers.
+ include_dirs: ["bionic/libc"],
+ static_libs: ["libc_logging"],
+
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libdebuggerd_client",
+ defaults: ["debuggerd_defaults"],
+ srcs: [
+ "client/debuggerd_client.cpp",
+ "util.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libdebuggerd",
+ defaults: ["debuggerd_defaults"],
+
+ srcs: [
+ "libdebuggerd/backtrace.cpp",
+ "libdebuggerd/elf_utils.cpp",
+ "libdebuggerd/open_files_list.cpp",
+ "libdebuggerd/tombstone.cpp",
+ "libdebuggerd/utility.cpp",
+ ],
+
+ target: {
+ android_arm: {
+ srcs: ["libdebuggerd/arm/machine.cpp"],
+ },
+ android_arm64: {
+ srcs: ["libdebuggerd/arm64/machine.cpp"],
+ },
+ android_mips: {
+ srcs: ["libdebuggerd/mips/machine.cpp"],
+ },
+ android_mips64: {
+ srcs: ["libdebuggerd/mips64/machine.cpp"],
+ },
+ android_x86: {
+ srcs: ["libdebuggerd/x86/machine.cpp"],
+ },
+ android_x86_64: {
+ srcs: ["libdebuggerd/x86_64/machine.cpp"],
+ },
+ },
+
+ local_include_dirs: ["libdebuggerd/include"],
+ export_include_dirs: ["libdebuggerd/include"],
+
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+}
+
+cc_test {
+ name: "debuggerd_test",
+ defaults: ["debuggerd_defaults"],
+
+ cflags: ["-Wno-missing-field-initializers"],
+ srcs: [
+ "libdebuggerd/test/dump_memory_test.cpp",
+ "libdebuggerd/test/elf_fake.cpp",
+ "libdebuggerd/test/log_fake.cpp",
+ "libdebuggerd/test/open_files_list_test.cpp",
+ "libdebuggerd/test/property_fake.cpp",
+ "libdebuggerd/test/ptrace_fake.cpp",
+ "libdebuggerd/test/tombstone_test.cpp",
+ ],
+
+ target: {
+ android: {
+ srcs: [
+ "debuggerd_test.cpp",
+ "util.cpp"
+ ],
+ },
+ },
+
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "libcutils",
+ ],
+
+ static_libs: [
+ "libdebuggerd"
+ ],
+
+ local_include_dirs: [
+ "libdebuggerd",
+ ],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ stem: "debuggerd_test32",
+ },
+ lib64: {
+ stem: "debuggerd_test64",
+ },
+ },
+}
+
+cc_binary {
+ name: "crash_dump",
+ srcs: [
+ "crash_dump.cpp",
+ "util.cpp",
+ ],
+ defaults: ["debuggerd_defaults"],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "libdebuggerd",
+ "liblog",
+ "libprocinfo",
+ "libselinux",
+ ],
+}
+
+cc_binary {
+ name: "debuggerd",
+ srcs: [
+ "debuggerd.cpp",
+ ],
+ defaults: ["debuggerd_defaults"],
+
+ shared_libs: [
+ "libbase",
+ "libdebuggerd_client",
+ "liblog",
+ "libselinux",
+ ],
+
+ local_include_dirs: ["include"],
+}
+
+cc_binary {
+ name: "tombstoned",
+ srcs: [
+ "util.cpp",
+ "tombstoned/intercept_manager.cpp",
+ "tombstoned/tombstoned.cpp",
+ ],
+ defaults: ["debuggerd_defaults"],
+
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "libevent",
+ "liblog",
+ ],
+
+ init_rc: ["tombstoned/tombstoned.rc"]
+}
+
+subdirs = [
+ "crasher",
+]
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
deleted file mode 100644
index 3fca709..0000000
--- a/debuggerd/Android.mk
+++ /dev/null
@@ -1,129 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-common_cppflags := \
- -std=gnu++11 \
- -W \
- -Wall \
- -Wextra \
- -Wunused \
- -Werror \
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- backtrace.cpp \
- debuggerd.cpp \
- elf_utils.cpp \
- getevent.cpp \
- tombstone.cpp \
- utility.cpp \
-
-LOCAL_SRC_FILES_arm := arm/machine.cpp
-LOCAL_SRC_FILES_arm64 := arm64/machine.cpp
-LOCAL_SRC_FILES_mips := mips/machine.cpp
-LOCAL_SRC_FILES_mips64 := mips64/machine.cpp
-LOCAL_SRC_FILES_x86 := x86/machine.cpp
-LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
-
-LOCAL_CPPFLAGS := $(common_cppflags)
-
-ifeq ($(TARGET_IS_64_BIT),true)
-LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT
-endif
-
-LOCAL_SHARED_LIBRARIES := \
- libbacktrace \
- libbase \
- libcutils \
- liblog \
- libselinux \
-
-LOCAL_CLANG := true
-
-LOCAL_MODULE := debuggerd
-LOCAL_MODULE_STEM_32 := debuggerd
-LOCAL_MODULE_STEM_64 := debuggerd64
-LOCAL_MULTILIB := both
-
-include $(BUILD_EXECUTABLE)
-
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.c
-LOCAL_SRC_FILES_arm := arm/crashglue.S
-LOCAL_SRC_FILES_arm64 := arm64/crashglue.S
-LOCAL_SRC_FILES_mips := mips/crashglue.S
-LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
-LOCAL_SRC_FILES_x86 := x86/crashglue.S
-LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -fstack-protector-all -Werror -Wno-free-nonheap-object
-#LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_SHARED_LIBRARIES := libcutils liblog libc
-
-# The arm emulator has VFP but not VFPv3-D32.
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
-endif
-
-LOCAL_MODULE := crasher
-LOCAL_MODULE_STEM_32 := crasher
-LOCAL_MODULE_STEM_64 := crasher64
-LOCAL_MULTILIB := both
-
-include $(BUILD_EXECUTABLE)
-
-debuggerd_test_src_files := \
- utility.cpp \
- test/dump_memory_test.cpp \
- test/elf_fake.cpp \
- test/log_fake.cpp \
- test/property_fake.cpp \
- test/ptrace_fake.cpp \
- test/tombstone_test.cpp \
- test/selinux_fake.cpp \
-
-debuggerd_shared_libraries := \
- libbacktrace \
- libbase \
- libcutils \
-
-debuggerd_c_includes := \
- $(LOCAL_PATH)/test \
-
-debuggerd_cpp_flags := \
- $(common_cppflags) \
- -Wno-missing-field-initializers \
-
-# Only build the host tests on linux.
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := debuggerd_test
-LOCAL_SRC_FILES := $(debuggerd_test_src_files)
-LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
-LOCAL_C_INCLUDES := $(debuggerd_c_includes)
-LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
-
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := debuggerd_test
-LOCAL_SRC_FILES := $(debuggerd_test_src_files)
-LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
-LOCAL_C_INCLUDES := $(debuggerd_c_includes)
-LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
-
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_MULTILIB := both
-include $(BUILD_NATIVE_TEST)
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp
deleted file mode 100644
index b46f8f4..0000000
--- a/debuggerd/backtrace.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <time.h>
-#include <errno.h>
-#include <limits.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/ptrace.h>
-
-#include <memory>
-
-#include <backtrace/Backtrace.h>
-
-#include <log/log.h>
-
-#include "backtrace.h"
-
-#include "utility.h"
-
-static void dump_process_header(log_t* log, pid_t pid) {
- char path[PATH_MAX];
- char procnamebuf[1024];
- char* procname = NULL;
- FILE* fp;
-
- snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
- if ((fp = fopen(path, "r"))) {
- procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
- fclose(fp);
- }
-
- time_t t = time(NULL);
- struct tm tm;
- localtime_r(&t, &tm);
- char timestr[64];
- strftime(timestr, sizeof(timestr), "%F %T", &tm);
- _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr);
-
- if (procname) {
- _LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", procname);
- }
- _LOG(log, logtype::BACKTRACE, "ABI: '%s'\n", ABI_STRING);
-}
-
-static void dump_process_footer(log_t* log, pid_t pid) {
- _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
-}
-
-static void dump_thread(
- log_t* log, pid_t tid, bool attached, bool* detach_failed, int* total_sleep_time_usec) {
- char path[PATH_MAX];
- char threadnamebuf[1024];
- char* threadname = NULL;
- FILE* fp;
-
- snprintf(path, sizeof(path), "/proc/%d/comm", tid);
- if ((fp = fopen(path, "r"))) {
- threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
- fclose(fp);
- if (threadname) {
- size_t len = strlen(threadname);
- if (len && threadname[len - 1] == '\n') {
- threadname[len - 1] = '\0';
- }
- }
- }
-
- _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid);
-
- if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
- _LOG(log, logtype::BACKTRACE, "Could not attach to thread: %s\n", strerror(errno));
- return;
- }
-
- if (!attached && wait_for_sigstop(tid, total_sleep_time_usec, detach_failed) == -1) {
- return;
- }
-
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(tid, BACKTRACE_CURRENT_THREAD));
- if (backtrace->Unwind(0)) {
- dump_backtrace_to_log(backtrace.get(), log, " ");
- } else {
- ALOGE("Unwind failed: tid = %d", tid);
- }
-
- if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
- ALOGE("ptrace detach from %d failed: %s\n", tid, strerror(errno));
- *detach_failed = true;
- }
-}
-
-void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
- int* total_sleep_time_usec) {
- log_t log;
- log.tfd = fd;
- log.amfd = amfd;
-
- dump_process_header(&log, pid);
- dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec);
-
- char task_path[64];
- snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
- DIR* d = opendir(task_path);
- if (d != NULL) {
- struct dirent* de = NULL;
- while ((de = readdir(d)) != NULL) {
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
- continue;
- }
-
- char* end;
- pid_t new_tid = strtoul(de->d_name, &end, 10);
- if (*end || new_tid == tid) {
- continue;
- }
-
- dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec);
- }
- closedir(d);
- }
-
- dump_process_footer(&log, pid);
-}
-
-void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) {
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str());
- }
-}
diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h
deleted file mode 100644
index da14cd4..0000000
--- a/debuggerd/backtrace.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_BACKTRACE_H
-#define _DEBUGGERD_BACKTRACE_H
-
-#include <sys/types.h>
-
-#include "utility.h"
-
-class Backtrace;
-
-// Dumps a backtrace using a format similar to what Dalvik uses so that the result
-// can be intermixed in a bug report.
-void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
- int* total_sleep_time_usec);
-
-/* Dumps the backtrace in the backtrace data structure to the log. */
-void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
-
-#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
new file mode 100644
index 0000000..81d70df
--- /dev/null
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <debuggerd/client.h>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <debuggerd/handler.h>
+#include <debuggerd/protocol.h>
+#include <debuggerd/util.h>
+
+using android::base::unique_fd;
+
+static bool send_signal(pid_t pid, bool backtrace) {
+ sigval val;
+ val.sival_int = backtrace;
+ if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
+ return false;
+ }
+ return true;
+}
+
+static bool check_dumpable(pid_t pid) {
+ // /proc/<pid> is owned by the effective UID of the process.
+ // Ownership of most of the other files in /proc/<pid> varies based on PR_SET_DUMPABLE.
+ // If PR_GET_DUMPABLE would return 0, they're owned by root, instead.
+ std::string proc_pid_path = android::base::StringPrintf("/proc/%d/", pid);
+ std::string proc_pid_status_path = proc_pid_path + "/status";
+
+ unique_fd proc_pid_fd(open(proc_pid_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ if (proc_pid_fd == -1) {
+ return false;
+ }
+ unique_fd proc_pid_status_fd(openat(proc_pid_fd, "status", O_RDONLY | O_CLOEXEC));
+ if (proc_pid_status_fd == -1) {
+ return false;
+ }
+
+ struct stat proc_pid_st;
+ struct stat proc_pid_status_st;
+ if (fstat(proc_pid_fd.get(), &proc_pid_st) != 0 ||
+ fstat(proc_pid_status_fd.get(), &proc_pid_status_st) != 0) {
+ return false;
+ }
+
+ // We can't figure out if a process is dumpable if its effective UID is root, but that's fine
+ // because being root bypasses the PR_SET_DUMPABLE check for ptrace.
+ if (proc_pid_st.st_uid == 0) {
+ return true;
+ }
+
+ if (proc_pid_status_st.st_uid == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
+ int timeout_ms) {
+ LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
+ unique_fd sockfd;
+ const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
+ auto set_timeout = [timeout_ms, &sockfd, &end]() {
+ if (timeout_ms <= 0) {
+ return true;
+ }
+
+ auto now = std::chrono::steady_clock::now();
+ if (now > end) {
+ return false;
+ }
+
+ auto time_left = std::chrono::duration_cast<std::chrono::microseconds>(end - now);
+ auto seconds = std::chrono::duration_cast<std::chrono::seconds>(time_left);
+ auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(time_left - seconds);
+ struct timeval timeout = {
+ .tv_sec = static_cast<long>(seconds.count()),
+ .tv_usec = static_cast<long>(microseconds.count()),
+ };
+
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
+ return false;
+ }
+ if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
+ return false;
+ }
+
+ return true;
+ };
+
+ if (!check_dumpable(pid)) {
+ dprintf(output_fd.get(), "target pid %d is not dumpable\n", pid);
+ return true;
+ }
+
+ sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
+ if (sockfd == -1) {
+ PLOG(ERROR) << "libdebugger_client: failed to create socket";
+ return false;
+ }
+
+ if (!set_timeout()) {
+ PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ return false;
+ }
+
+ if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
+ return false;
+ }
+
+ InterceptRequest req = {.pid = pid };
+ if (!set_timeout()) {
+ PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ }
+
+ if (send_fd(sockfd.get(), &req, sizeof(req), std::move(output_fd)) != sizeof(req)) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
+ return false;
+ }
+
+ bool backtrace = dump_type == kDebuggerdBacktrace;
+ send_signal(pid, backtrace);
+
+ if (!set_timeout()) {
+ PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ }
+
+ InterceptResponse response;
+ ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd.get(), &response, sizeof(response), MSG_TRUNC));
+ if (rc == 0) {
+ LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+ return false;
+ } else if (rc != sizeof(response)) {
+ LOG(ERROR)
+ << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
+ << sizeof(response) << ", received " << rc;
+ return false;
+ }
+
+ if (response.success != 1) {
+ response.error_message[sizeof(response.error_message) - 1] = '\0';
+ LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
+ }
+
+ LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
+
+ return true;
+}
+
+int dump_backtrace_to_file(pid_t tid, int fd) {
+ return dump_backtrace_to_file_timeout(tid, fd, 0);
+}
+
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+ android::base::unique_fd copy(dup(fd));
+ if (copy == -1) {
+ return -1;
+ }
+ int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
+ return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1;
+}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
new file mode 100644
index 0000000..58eaed7
--- /dev/null
+++ b/debuggerd/crash_dump.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <limits>
+#include <memory>
+#include <set>
+#include <vector>
+
+#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/unique_fd.h>
+#include <cutils/sockets.h>
+#include <log/log.h>
+#include <procinfo/process.h>
+#include <selinux/selinux.h>
+
+#include "backtrace.h"
+#include "tombstone.h"
+#include "utility.h"
+
+#include "debuggerd/handler.h"
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+using android::base::unique_fd;
+using android::base::StringPrintf;
+
+static bool pid_contains_tid(pid_t pid, pid_t tid) {
+ std::string task_path = StringPrintf("/proc/%d/task/%d", pid, tid);
+ return access(task_path.c_str(), F_OK) == 0;
+}
+
+// Attach to a thread, and verify that it's still a member of the given process
+static bool ptrace_seize_thread(pid_t pid, pid_t tid, std::string* error) {
+ if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) {
+ *error = StringPrintf("failed to attach to thread %d: %s", tid, strerror(errno));
+ return false;
+ }
+
+ // Make sure that the task we attached to is actually part of the pid we're dumping.
+ if (!pid_contains_tid(pid, tid)) {
+ if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
+ PLOG(FATAL) << "failed to detach from thread " << tid;
+ }
+ *error = StringPrintf("thread %d is not in process %d", tid, pid);
+ return false;
+ }
+
+ // Put the task into ptrace-stop state.
+ if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) != 0) {
+ PLOG(FATAL) << "failed to interrupt thread " << tid;
+ }
+
+ return true;
+}
+
+static bool activity_manager_notify(int pid, int signal, const std::string& amfd_data) {
+ android::base::unique_fd amfd(socket_local_client("/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
+ if (amfd.get() == -1) {
+ PLOG(ERROR) << "unable to connect to activity manager";
+ return false;
+ }
+
+ struct timeval tv = {
+ .tv_sec = 1,
+ .tv_usec = 0,
+ };
+ if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
+ PLOG(ERROR) << "failed to set send timeout on activity manager socket";
+ return false;
+ }
+ tv.tv_sec = 3; // 3 seconds on handshake read
+ if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+ PLOG(ERROR) << "failed to set receive timeout on activity manager socket";
+ return false;
+ }
+
+ // Activity Manager protocol: binary 32-bit network-byte-order ints for the
+ // pid and signal number, followed by the raw text of the dump, culminating
+ // in a zero byte that marks end-of-data.
+ uint32_t datum = htonl(pid);
+ if (!android::base::WriteFully(amfd, &datum, 4)) {
+ PLOG(ERROR) << "AM pid write failed";
+ return false;
+ }
+ datum = htonl(signal);
+ if (!android::base::WriteFully(amfd, &datum, 4)) {
+ PLOG(ERROR) << "AM signal write failed";
+ return false;
+ }
+ if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size() + 1)) {
+ PLOG(ERROR) << "AM data write failed";
+ return false;
+ }
+
+ // 3 sec timeout reading the ack; we're fine if the read fails.
+ char ack;
+ android::base::ReadFully(amfd, &ack, 1);
+ return true;
+}
+
+static bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) {
+ unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ if (sockfd == -1) {
+ PLOG(ERROR) << "failed to connect to tombstoned";
+ return false;
+ }
+
+ TombstonedCrashPacket packet = {};
+ packet.packet_type = CrashPacketType::kDumpRequest;
+ packet.packet.dump_request.pid = pid;
+ if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
+ PLOG(ERROR) << "failed to write DumpRequest packet";
+ return false;
+ }
+
+ unique_fd tmp_output_fd;
+ ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+ if (rc == -1) {
+ PLOG(ERROR) << "failed to read response to DumpRequest packet";
+ return false;
+ } else if (rc != sizeof(packet)) {
+ LOG(ERROR) << "read DumpRequest response packet of incorrect length (expected "
+ << sizeof(packet) << ", got " << rc << ")";
+ return false;
+ }
+
+ *tombstoned_socket = std::move(sockfd);
+ *output_fd = std::move(tmp_output_fd);
+ return true;
+}
+
+static bool tombstoned_notify_completion(int tombstoned_socket) {
+ TombstonedCrashPacket packet = {};
+ packet.packet_type = CrashPacketType::kCompletedDump;
+ if (TEMP_FAILURE_RETRY(write(tombstoned_socket, &packet, sizeof(packet))) != sizeof(packet)) {
+ return false;
+ }
+ return true;
+}
+
+static void signal_handler(int) {
+ // We can't log easily, because the heap might be corrupt.
+ // Just die and let the surrounding log context explain things.
+ _exit(1);
+}
+
+static void abort_handler(pid_t target, const bool& tombstoned_connected,
+ unique_fd& tombstoned_socket, unique_fd& output_fd,
+ const char* abort_msg) {
+ // If we abort before we get an output fd, contact tombstoned to let any
+ // potential listeners know that we failed.
+ if (!tombstoned_connected) {
+ if (!tombstoned_connect(target, &tombstoned_socket, &output_fd)) {
+ // We failed to connect, not much we can do.
+ LOG(ERROR) << "failed to connected to tombstoned to report failure";
+ _exit(1);
+ }
+ }
+
+ dprintf(output_fd.get(), "crash_dump failed to dump process %d: %s\n", target, abort_msg);
+
+ _exit(1);
+}
+
+static void check_process(int proc_fd, pid_t expected_pid) {
+ android::procinfo::ProcessInfo proc_info;
+ if (!android::procinfo::GetProcessInfoFromProcPidFd(proc_fd, &proc_info)) {
+ LOG(FATAL) << "failed to fetch process info";
+ }
+
+ if (proc_info.pid != expected_pid) {
+ LOG(FATAL) << "pid mismatch: expected " << expected_pid << ", actual " << proc_info.ppid;
+ }
+}
+
+int main(int argc, char** argv) {
+ pid_t target = getppid();
+ bool tombstoned_connected = false;
+ unique_fd tombstoned_socket;
+ unique_fd output_fd;
+
+ android::base::InitLogging(argv);
+ android::base::SetAborter([&](const char* abort_msg) {
+ abort_handler(target, tombstoned_connected, tombstoned_socket, output_fd, abort_msg);
+ });
+
+ // Don't try to dump ourselves.
+ struct sigaction action = {};
+ action.sa_handler = signal_handler;
+ debuggerd_register_handlers(&action);
+
+ if (argc != 2) {
+ return 1;
+ }
+
+ pid_t main_tid;
+
+ if (target == 1) {
+ LOG(FATAL) << "target died before we could attach";
+ }
+
+ if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
+ LOG(FATAL) << "invalid main tid: " << argv[1];
+ }
+
+ android::procinfo::ProcessInfo target_info;
+ if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) {
+ LOG(FATAL) << "failed to fetch process info for target " << main_tid;
+ }
+
+ if (main_tid != target_info.tid || target != target_info.pid) {
+ LOG(FATAL) << "target info mismatch, expected pid " << target << ", tid " << main_tid
+ << ", received pid " << target_info.pid << ", tid " << target_info.tid;
+ }
+
+ // Open /proc/`getppid()` in the original process, and pass it down to the forked child.
+ std::string target_proc_path = "/proc/" + std::to_string(target);
+ int target_proc_fd = open(target_proc_path.c_str(), O_DIRECTORY | O_RDONLY);
+ if (target_proc_fd == -1) {
+ PLOG(FATAL) << "failed to open " << target_proc_path;
+ }
+
+ // Reparent ourselves to init, so that the signal handler can waitpid on the
+ // original process to avoid leaving a zombie for non-fatal dumps.
+ pid_t forkpid = fork();
+ if (forkpid == -1) {
+ PLOG(FATAL) << "fork failed";
+ } else if (forkpid != 0) {
+ exit(0);
+ }
+
+ // Die if we take too long.
+ alarm(20);
+
+ check_process(target_proc_fd, target);
+
+ std::string attach_error;
+ if (!ptrace_seize_thread(target, main_tid, &attach_error)) {
+ LOG(FATAL) << attach_error;
+ }
+
+ check_process(target_proc_fd, target);
+
+ LOG(INFO) << "obtaining output fd from tombstoned";
+ tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd);
+
+ // Write a '\1' to stdout to tell the crashing process to resume.
+ if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) {
+ PLOG(ERROR) << "failed to communicate to target process";
+ }
+
+ if (tombstoned_connected) {
+ if (TEMP_FAILURE_RETRY(dup2(output_fd.get(), STDOUT_FILENO)) == -1) {
+ PLOG(ERROR) << "failed to dup2 output fd (" << output_fd.get() << ") to STDOUT_FILENO";
+ }
+ } else {
+ unique_fd devnull(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+ TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO));
+ output_fd = std::move(devnull);
+ }
+
+ LOG(INFO) << "performing dump of process " << target << " (target tid = " << main_tid << ")";
+
+ // At this point, the thread that made the request has been attached and is
+ // in ptrace-stopped state. After resumption, the triggering signal that has
+ // been queued will be delivered.
+ if (ptrace(PTRACE_CONT, main_tid, 0, 0) != 0) {
+ PLOG(ERROR) << "PTRACE_CONT(" << main_tid << ") failed";
+ exit(1);
+ }
+
+ siginfo_t siginfo = {};
+ if (!wait_for_signal(main_tid, &siginfo)) {
+ printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
+ exit(1);
+ }
+
+ int signo = siginfo.si_signo;
+ bool backtrace = false;
+ uintptr_t abort_address = 0;
+
+ // si_value can represent three things:
+ // 0: dump tombstone
+ // 1: dump backtrace
+ // everything else: abort message address (implies dump tombstone)
+ if (siginfo.si_value.sival_int == 1) {
+ backtrace = true;
+ } else if (siginfo.si_value.sival_ptr != nullptr) {
+ abort_address = reinterpret_cast<uintptr_t>(siginfo.si_value.sival_ptr);
+ }
+
+ // Now that we have the signal that kicked things off, attach all of the
+ // sibling threads, and then proceed.
+ bool fatal_signal = signo != DEBUGGER_SIGNAL;
+ std::set<pid_t> siblings;
+ std::set<pid_t> attached_siblings;
+ if (fatal_signal || backtrace) {
+ if (!android::procinfo::GetProcessTids(target, &siblings)) {
+ PLOG(FATAL) << "failed to get process siblings";
+ }
+ siblings.erase(main_tid);
+
+ for (pid_t sibling_tid : siblings) {
+ if (!ptrace_seize_thread(target, sibling_tid, &attach_error)) {
+ LOG(WARNING) << attach_error;
+ } else {
+ attached_siblings.insert(sibling_tid);
+ }
+ }
+ }
+
+ check_process(target_proc_fd, target);
+
+ // TODO: Use seccomp to lock ourselves down.
+
+ std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
+ std::string amfd_data;
+
+ if (backtrace) {
+ dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, attached_siblings, 0);
+ } else {
+ // Collect the list of open files.
+ OpenFilesList open_files;
+ populate_open_files_list(target, &open_files);
+
+ engrave_tombstone(output_fd.get(), backtrace_map.get(), open_files, target, main_tid,
+ attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr);
+ }
+
+ // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
+ // group-stop state, which is true as long as no stopping signals are sent.
+
+ bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
+ if (!fatal_signal || siginfo.si_code == SI_USER) {
+ // Don't wait_for_gdb when the process didn't actually crash.
+ wait_for_gdb = false;
+ }
+
+ // If the process crashed or we need to send it SIGSTOP for wait_for_gdb,
+ // get it in a state where it can receive signals, and then send the relevant
+ // signal.
+ if (wait_for_gdb || fatal_signal) {
+ if (ptrace(PTRACE_INTERRUPT, main_tid, 0, 0) != 0) {
+ PLOG(ERROR) << "failed to use PTRACE_INTERRUPT on " << main_tid;
+ }
+
+ if (tgkill(target, main_tid, wait_for_gdb ? SIGSTOP : signo) != 0) {
+ PLOG(ERROR) << "failed to resend signal " << signo << " to " << main_tid;
+ }
+ }
+
+ if (wait_for_gdb) {
+ // Use ALOGI to line up with output from engrave_tombstone.
+ ALOGI(
+ "***********************************************************\n"
+ "* Process %d has been suspended while crashing.\n"
+ "* To attach gdbserver and start gdb, run this on the host:\n"
+ "*\n"
+ "* gdbclient.py -p %d\n"
+ "*\n"
+ "***********************************************************",
+ target, main_tid);
+ }
+
+ if (fatal_signal) {
+ activity_manager_notify(target, signo, amfd_data);
+ }
+
+ // Close stdout before we notify tombstoned of completion.
+ close(STDOUT_FILENO);
+ if (tombstoned_connected && !tombstoned_notify_completion(tombstoned_socket.get())) {
+ LOG(ERROR) << "failed to notify tombstoned of completion";
+ }
+
+ return 0;
+}
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
deleted file mode 100644
index b5064b4..0000000
--- a/debuggerd/crasher.c
+++ /dev/null
@@ -1,206 +0,0 @@
-#include <assert.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sched.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/mman.h>
-#include <sys/ptrace.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <cutils/sockets.h>
-#include <log/log.h>
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-extern const char* __progname;
-
-void crash1(void);
-void crashnostack(void);
-static int do_action(const char* arg);
-
-static void maybe_abort() {
- if (time(0) != 42) {
- abort();
- }
-}
-
-static char* smash_stack_dummy_buf;
-__attribute__ ((noinline)) static void smash_stack_dummy_function(volatile int* plen) {
- smash_stack_dummy_buf[*plen] = 0;
-}
-
-// This must be marked with "__attribute__ ((noinline))", to ensure the
-// compiler generates the proper stack guards around this function.
-// Assign local array address to global variable to force stack guards.
-// Use another noinline function to corrupt the stack.
-__attribute__ ((noinline)) static int smash_stack(volatile int* plen) {
- printf("crasher: deliberately corrupting stack...\n");
-
- char buf[128];
- smash_stack_dummy_buf = buf;
- // This should corrupt stack guards and make process abort.
- smash_stack_dummy_function(plen);
- return 0;
-}
-
-static void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
-
-__attribute__((noinline)) static void overflow_stack(void* p) {
- void* buf[1];
- buf[0] = p;
- global = buf;
- overflow_stack(&buf);
-}
-
-static void *noisy(void *x)
-{
- char c = (uintptr_t) x;
- for(;;) {
- usleep(250*1000);
- write(2, &c, 1);
- if(c == 'C') *((volatile unsigned*) 0) = 42;
- }
- return NULL;
-}
-
-static int ctest()
-{
- pthread_t thr;
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- pthread_create(&thr, &attr, noisy, (void*) 'A');
- pthread_create(&thr, &attr, noisy, (void*) 'B');
- pthread_create(&thr, &attr, noisy, (void*) 'C');
- for(;;) ;
- return 0;
-}
-
-static void* thread_callback(void* raw_arg)
-{
- return (void*) (uintptr_t) do_action((const char*) raw_arg);
-}
-
-static int do_action_on_thread(const char* arg)
-{
- pthread_t t;
- pthread_create(&t, NULL, thread_callback, (void*) arg);
- void* result = NULL;
- pthread_join(t, &result);
- return (int) (uintptr_t) result;
-}
-
-__attribute__((noinline)) static int crash3(int a) {
- *((int*) 0xdead) = a;
- return a*4;
-}
-
-__attribute__((noinline)) static int crash2(int a) {
- a = crash3(a) + 2;
- return a*3;
-}
-
-__attribute__((noinline)) static int crash(int a) {
- a = crash2(a) + 1;
- return a*2;
-}
-
-static void abuse_heap() {
- char buf[16];
- free((void*) buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
-}
-
-static void sigsegv_non_null() {
- int* a = (int *)(&do_action);
- *a = 42;
-}
-
-static int do_action(const char* arg)
-{
- fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
-
- if (!strncmp(arg, "thread-", strlen("thread-"))) {
- return do_action_on_thread(arg + strlen("thread-"));
- } else if (!strcmp(arg, "SIGSEGV-non-null")) {
- sigsegv_non_null();
- } else if (!strcmp(arg, "smash-stack")) {
- volatile int len = 128;
- return smash_stack(&len);
- } else if (!strcmp(arg, "stack-overflow")) {
- overflow_stack(NULL);
- } else if (!strcmp(arg, "nostack")) {
- crashnostack();
- } else if (!strcmp(arg, "ctest")) {
- return ctest();
- } else if (!strcmp(arg, "exit")) {
- exit(1);
- } else if (!strcmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
- return crash(42);
- } else if (!strcmp(arg, "abort")) {
- maybe_abort();
- } else if (!strcmp(arg, "assert")) {
- __assert("some_file.c", 123, "false");
- } else if (!strcmp(arg, "assert2")) {
- __assert2("some_file.c", 123, "some_function", "false");
- } else if (!strcmp(arg, "LOG_ALWAYS_FATAL")) {
- LOG_ALWAYS_FATAL("hello %s", "world");
- } else if (!strcmp(arg, "LOG_ALWAYS_FATAL_IF")) {
- LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
- } else if (!strcmp(arg, "SIGFPE")) {
- raise(SIGFPE);
- return EXIT_SUCCESS;
- } else if (!strcmp(arg, "SIGTRAP")) {
- raise(SIGTRAP);
- return EXIT_SUCCESS;
- } else if (!strcmp(arg, "heap-usage")) {
- abuse_heap();
- } else if (!strcmp(arg, "SIGSEGV-unmapped")) {
- char* map = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
- munmap(map, sizeof(int));
- map[0] = '8';
- }
-
- fprintf(stderr, "%s OP\n", __progname);
- fprintf(stderr, "where OP is:\n");
- fprintf(stderr, " smash-stack overwrite a stack-guard canary\n");
- fprintf(stderr, " stack-overflow recurse until the stack overflows\n");
- fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n");
- fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
- fprintf(stderr, " nostack crash with a NULL stack pointer\n");
- fprintf(stderr, " ctest (obsoleted by thread-crash?)\n");
- fprintf(stderr, " exit call exit(1)\n");
- fprintf(stderr, " abort call abort()\n");
- fprintf(stderr, " assert call assert() without a function\n");
- fprintf(stderr, " assert2 call assert() with a function\n");
- fprintf(stderr, " LOG_ALWAYS_FATAL call LOG_ALWAYS_FATAL\n");
- fprintf(stderr, " LOG_ALWAYS_FATAL_IF call LOG_ALWAYS_FATAL\n");
- fprintf(stderr, " SIGFPE cause a SIGFPE\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");
- fprintf(stderr, " SIGTRAP cause a SIGTRAP\n");
- fprintf(stderr, "prefix any of the above with 'thread-' to not run\n");
- fprintf(stderr, "on the process' main thread.\n");
- return EXIT_SUCCESS;
-}
-
-int main(int argc, char **argv)
-{
- fprintf(stderr,"crasher: built at " __TIME__ "!@\n");
-
- if(argc > 1) {
- return do_action(argv[1]);
- } else {
- crash1();
- }
-
- return 0;
-}
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
new file mode 100644
index 0000000..4727894
--- /dev/null
+++ b/debuggerd/crasher/Android.bp
@@ -0,0 +1,81 @@
+cc_defaults {
+ name: "crasher-defaults",
+
+ cppflags: [
+ "-std=gnu++14",
+ "-W",
+ "-Wall",
+ "-Wextra",
+ "-Wunused",
+ "-Werror",
+ "-O0",
+ "-fstack-protector-all",
+ "-Wno-free-nonheap-object",
+ "-Wno-date-time",
+ ],
+ srcs: ["crasher.cpp"],
+ arch: {
+ arm: {
+ srcs: ["arm/crashglue.S"],
+
+ armv7_a_neon: {
+ asflags: ["-DHAS_VFP_D32"],
+ },
+ },
+ arm64: {
+ srcs: ["arm64/crashglue.S"],
+ },
+ mips: {
+ srcs: ["mips/crashglue.S"],
+ },
+ mips64: {
+ srcs: ["mips64/crashglue.S"],
+ },
+ x86: {
+ srcs: ["x86/crashglue.S"],
+ },
+ x86_64: {
+ srcs: ["x86_64/crashglue.S"],
+ },
+ },
+ compile_multilib: "both",
+}
+
+cc_binary {
+ name: "crasher",
+
+ defaults: ["crasher-defaults"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ multilib: {
+ lib32: {
+ stem: "crasher",
+ },
+ lib64: {
+ stem: "crasher64",
+ },
+ },
+}
+
+cc_binary {
+ name: "static_crasher",
+
+ defaults: ["crasher-defaults"],
+ cppflags: ["-DSTATIC_CRASHER"],
+ static_executable: true,
+ static_libs: [
+ "libdebuggerd_handler",
+ "libbase",
+ "liblog",
+ ],
+ multilib: {
+ lib32: {
+ stem: "static_crasher",
+ },
+ lib64: {
+ stem: "static_crasher64",
+ },
+ },
+}
diff --git a/debuggerd/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
similarity index 100%
rename from debuggerd/arm/crashglue.S
rename to debuggerd/crasher/arm/crashglue.S
diff --git a/debuggerd/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
similarity index 100%
rename from debuggerd/arm64/crashglue.S
rename to debuggerd/crasher/arm64/crashglue.S
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
new file mode 100644
index 0000000..64a38dd
--- /dev/null
+++ b/debuggerd/crasher/crasher.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "crasher"
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+// We test both kinds of logging.
+#include <android-base/logging.h>
+#include <log/log.h>
+
+#if defined(STATIC_CRASHER)
+#include "debuggerd/handler.h"
+#endif
+
+#if defined(__arm__)
+// See https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt for details.
+#define __kuser_helper_version (*(int32_t*) 0xffff0ffc)
+typedef void * (__kuser_get_tls_t)(void);
+#define __kuser_get_tls (*(__kuser_get_tls_t*) 0xffff0fe0)
+typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
+#define __kuser_cmpxchg (*(__kuser_cmpxchg_t*) 0xffff0fc0)
+typedef void (__kuser_dmb_t)(void);
+#define __kuser_dmb (*(__kuser_dmb_t*) 0xffff0fa0)
+typedef int (__kuser_cmpxchg64_t)(const int64_t*, const int64_t*, volatile int64_t*);
+#define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t*) 0xffff0f60)
+#endif
+
+#define noinline __attribute__((__noinline__))
+
+// Avoid name mangling so that stacks are more readable.
+extern "C" {
+
+void crash1(void);
+void crashnostack(void);
+
+int do_action(const char* arg);
+
+noinline void maybe_abort() {
+ if (time(0) != 42) {
+ abort();
+ }
+}
+
+char* smash_stack_dummy_buf;
+noinline void smash_stack_dummy_function(volatile int* plen) {
+ smash_stack_dummy_buf[*plen] = 0;
+}
+
+// This must be marked with "__attribute__ ((noinline))", to ensure the
+// compiler generates the proper stack guards around this function.
+// Assign local array address to global variable to force stack guards.
+// Use another noinline function to corrupt the stack.
+noinline int smash_stack(volatile int* plen) {
+ printf("%s: deliberately corrupting stack...\n", getprogname());
+
+ char buf[128];
+ smash_stack_dummy_buf = buf;
+ // This should corrupt stack guards and make process abort.
+ smash_stack_dummy_function(plen);
+ return 0;
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+
+void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
+
+noinline void overflow_stack(void* p) {
+ void* buf[1];
+ buf[0] = p;
+ global = buf;
+ overflow_stack(&buf);
+}
+
+#pragma clang diagnostic pop
+
+noinline void* thread_callback(void* raw_arg) {
+ const char* arg = reinterpret_cast<const char*>(raw_arg);
+ return reinterpret_cast<void*>(static_cast<uintptr_t>(do_action(arg)));
+}
+
+noinline int do_action_on_thread(const char* arg) {
+ pthread_t t;
+ pthread_create(&t, nullptr, thread_callback, const_cast<char*>(arg));
+ void* result = nullptr;
+ pthread_join(t, &result);
+ return reinterpret_cast<uintptr_t>(result);
+}
+
+noinline int crash3(int a) {
+ *reinterpret_cast<int*>(0xdead) = a;
+ return a*4;
+}
+
+noinline int crash2(int a) {
+ a = crash3(a) + 2;
+ return a*3;
+}
+
+noinline int crash(int a) {
+ a = crash2(a) + 1;
+ return a*2;
+}
+
+noinline void abuse_heap() {
+ char buf[16];
+ free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
+}
+
+noinline void sigsegv_non_null() {
+ int* a = (int *)(&do_action);
+ *a = 42;
+}
+
+noinline void fprintf_null() {
+ fprintf(nullptr, "oops");
+}
+
+noinline void readdir_null() {
+ readdir(nullptr);
+}
+
+noinline int strlen_null() {
+ char* sneaky_null = nullptr;
+ return strlen(sneaky_null);
+}
+
+static int usage() {
+ fprintf(stderr, "usage: %s KIND\n", getprogname());
+ fprintf(stderr, "\n");
+ fprintf(stderr, "where KIND is:\n");
+ fprintf(stderr, " smash-stack overwrite a -fstack-protector guard\n");
+ fprintf(stderr, " stack-overflow recurse until the stack overflows\n");
+ fprintf(stderr, " nostack crash with a NULL stack pointer\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n");
+ fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " abort call abort()\n");
+ fprintf(stderr, " assert call assert() without a function\n");
+ fprintf(stderr, " assert2 call assert() with a function\n");
+ fprintf(stderr, " exit call exit(1)\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " fortify fail a _FORTIFY_SOURCE check\n");
+ fprintf(stderr, " seccomp fail a seccomp check\n");
+#if defined(__arm__)
+ fprintf(stderr, " kuser_helper_version call kuser_helper_version\n");
+ fprintf(stderr, " kuser_get_tls call kuser_get_tls\n");
+ fprintf(stderr, " kuser_cmpxchg call kuser_cmpxchg\n");
+ fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n");
+ fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n");
+#endif
+ fprintf(stderr, "\n");
+ fprintf(stderr, " LOG_ALWAYS_FATAL call liblog LOG_ALWAYS_FATAL\n");
+ fprintf(stderr, " LOG_ALWAYS_FATAL_IF call liblog LOG_ALWAYS_FATAL_IF\n");
+ fprintf(stderr, " LOG-FATAL call libbase LOG(FATAL)\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " SIGFPE cause a SIGFPE\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");
+ fprintf(stderr, " SIGTRAP cause a SIGTRAP\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " fprintf-NULL pass a null pointer to fprintf\n");
+ fprintf(stderr, " readdir-NULL pass a null pointer to readdir\n");
+ fprintf(stderr, " strlen-NULL pass a null pointer to strlen\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n");
+ fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
+ fprintf(stderr, "all available file descriptors before crashing.\n");
+ fprintf(stderr, "prefix any of the above with 'wait-' to wait until input is received on stdin\n");
+
+ return EXIT_FAILURE;
+}
+
+noinline int do_action(const char* arg) {
+ // Prefixes.
+ if (!strncmp(arg, "wait-", strlen("wait-"))) {
+ char buf[1];
+ TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
+ return do_action(arg + strlen("wait-"));
+ } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
+ errno = 0;
+ while (errno != EMFILE) {
+ open("/dev/null", O_RDONLY);
+ }
+ return do_action(arg + strlen("exhaustfd-"));
+ } else if (!strncmp(arg, "thread-", strlen("thread-"))) {
+ return do_action_on_thread(arg + strlen("thread-"));
+ }
+
+ // Actions.
+ if (!strcasecmp(arg, "SIGSEGV-non-null")) {
+ sigsegv_non_null();
+ } else if (!strcasecmp(arg, "smash-stack")) {
+ volatile int len = 128;
+ return smash_stack(&len);
+ } else if (!strcasecmp(arg, "stack-overflow")) {
+ overflow_stack(nullptr);
+ } else if (!strcasecmp(arg, "nostack")) {
+ crashnostack();
+ } else if (!strcasecmp(arg, "exit")) {
+ exit(1);
+ } else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
+ return crash(42);
+ } else if (!strcasecmp(arg, "abort")) {
+ maybe_abort();
+ } else if (!strcasecmp(arg, "assert")) {
+ __assert("some_file.c", 123, "false");
+ } else if (!strcasecmp(arg, "assert2")) {
+ __assert2("some_file.c", 123, "some_function", "false");
+ } else if (!strcasecmp(arg, "fortify")) {
+ char buf[10];
+ __read_chk(-1, buf, 32, 10);
+ while (true) pause();
+ } else if (!strcasecmp(arg, "LOG(FATAL)")) {
+ LOG(FATAL) << "hello " << 123;
+ } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL")) {
+ LOG_ALWAYS_FATAL("hello %s", "world");
+ } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL_IF")) {
+ LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
+ } else if (!strcasecmp(arg, "SIGFPE")) {
+ raise(SIGFPE);
+ return EXIT_SUCCESS;
+ } else if (!strcasecmp(arg, "SIGTRAP")) {
+ raise(SIGTRAP);
+ return EXIT_SUCCESS;
+ } else if (!strcasecmp(arg, "fprintf-NULL")) {
+ fprintf_null();
+ } else if (!strcasecmp(arg, "readdir-NULL")) {
+ readdir_null();
+ } else if (!strcasecmp(arg, "strlen-NULL")) {
+ return strlen_null();
+ } else if (!strcasecmp(arg, "heap-usage")) {
+ abuse_heap();
+ } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
+ char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+ munmap(map, sizeof(int));
+ map[0] = '8';
+ } else if (!strcasecmp(arg, "seccomp")) {
+ syscall(99999);
+#if defined(__arm__)
+ } else if (!strcasecmp(arg, "kuser_helper_version")) {
+ return __kuser_helper_version;
+ } else if (!strcasecmp(arg, "kuser_get_tls")) {
+ return !__kuser_get_tls();
+ } else if (!strcasecmp(arg, "kuser_cmpxchg")) {
+ return __kuser_cmpxchg(0, 0, 0);
+ } else if (!strcasecmp(arg, "kuser_memory_barrier")) {
+ __kuser_dmb();
+ } else if (!strcasecmp(arg, "kuser_cmpxchg64")) {
+ return __kuser_cmpxchg64(0, 0, 0);
+#endif
+ } else {
+ return usage();
+ }
+
+ fprintf(stderr, "%s: exiting normally!\n", getprogname());
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char** argv) {
+#if defined(STATIC_CRASHER)
+ debuggerd_callbacks_t callbacks = {
+ .get_abort_message = []() {
+ static struct {
+ size_t size;
+ char msg[32];
+ } msg;
+
+ msg.size = strlen("dummy abort message");
+ memcpy(msg.msg, "dummy abort message", strlen("dummy abort message"));
+ return reinterpret_cast<abort_msg_t*>(&msg);
+ },
+ .post_dump = nullptr
+ };
+ debuggerd_init(&callbacks);
+#endif
+
+ if (argc == 1) crash1();
+ else if (argc == 2) return do_action(argv[1]);
+
+ return usage();
+}
+
+};
diff --git a/debuggerd/mips/crashglue.S b/debuggerd/crasher/mips/crashglue.S
similarity index 100%
rename from debuggerd/mips/crashglue.S
rename to debuggerd/crasher/mips/crashglue.S
diff --git a/debuggerd/mips64/crashglue.S b/debuggerd/crasher/mips64/crashglue.S
similarity index 100%
rename from debuggerd/mips64/crashglue.S
rename to debuggerd/crasher/mips64/crashglue.S
diff --git a/debuggerd/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
similarity index 100%
rename from debuggerd/x86/crashglue.S
rename to debuggerd/crasher/x86/crashglue.S
diff --git a/debuggerd/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
similarity index 100%
rename from debuggerd/x86_64/crashglue.S
rename to debuggerd/crasher/x86_64/crashglue.S
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 599995c..0c5d3cf 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2006, The Android Open Source Project
+ * Copyright 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,580 +14,68 @@
* limitations under the License.
*/
+#include <err.h>
#include <stdio.h>
-#include <errno.h>
-#include <signal.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <time.h>
+#include <stdlib.h>
+#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/wait.h>
-#include <elf.h>
-#include <sys/stat.h>
-#include <sys/poll.h>
+#include <limits>
+#include <thread>
-#include <selinux/android.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+#include <debuggerd/client.h>
+#include <debuggerd/util.h>
+#include <selinux/selinux.h>
-#include <log/logger.h>
+using android::base::unique_fd;
-#include <cutils/sockets.h>
-#include <cutils/properties.h>
-#include <cutils/debugger.h>
+static void usage(int exit_code) {
+ fprintf(stderr, "usage: debuggerd [-b] PID\n");
+ _exit(exit_code);
+}
-#include <linux/input.h>
-
-#include <private/android_filesystem_config.h>
-
-#include "backtrace.h"
-#include "getevent.h"
-#include "tombstone.h"
-#include "utility.h"
-
-// If the 32 bit executable is compiled on a 64 bit system,
-// use the 32 bit socket name.
-#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
-#define SOCKET_NAME DEBUGGER32_SOCKET_NAME
-#else
-#define SOCKET_NAME DEBUGGER_SOCKET_NAME
-#endif
-
-struct debugger_request_t {
- debugger_action_t action;
- pid_t pid, tid;
- uid_t uid, gid;
- uintptr_t abort_msg_address;
- int32_t original_si_code;
-};
-
-static void wait_for_user_action(const debugger_request_t& request) {
- // Explain how to attach the debugger.
- ALOGI("***********************************************************\n"
- "* Process %d has been suspended while crashing.\n"
- "* To attach gdbserver and start gdb, run this on the host:\n"
- "*\n"
- "* gdbclient %d\n"
- "*\n"
- "* Wait for gdb to start, then press the VOLUME DOWN key\n"
- "* to let the process continue crashing.\n"
- "***********************************************************",
- request.pid, request.tid);
-
- // Wait for VOLUME DOWN.
- if (init_getevent() == 0) {
+static std::thread spawn_redirect_thread(unique_fd fd) {
+ return std::thread([fd{ std::move(fd) }]() {
while (true) {
- input_event e;
- if (get_event(&e, -1) == 0) {
- if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
- break;
- }
+ char buf[BUFSIZ];
+ ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, sizeof(buf)));
+ if (rc <= 0) {
+ return;
+ }
+
+ if (!android::base::WriteFully(STDOUT_FILENO, buf, rc)) {
+ return;
}
}
- uninit_getevent();
- }
-
- ALOGI("debuggerd resuming process %d", request.pid);
+ });
}
-static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) {
- char path[64];
- snprintf(path, sizeof(path), "/proc/%d/status", tid);
+int main(int argc, char* argv[]) {
+ if (argc <= 1) usage(0);
+ if (argc > 3) usage(1);
+ if (argc == 3 && strcmp(argv[1], "-b") != 0) usage(1);
- FILE* fp = fopen(path, "r");
- if (!fp) {
- return -1;
+ pid_t pid;
+ if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
+ usage(1);
}
- int fields = 0;
- char line[1024];
- while (fgets(line, sizeof(line), fp)) {
- size_t len = strlen(line);
- if (len > 6 && !memcmp(line, "Tgid:\t", 6)) {
- *out_pid = atoi(line + 6);
- fields |= 1;
- } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) {
- *out_uid = atoi(line + 5);
- fields |= 2;
- } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) {
- *out_gid = atoi(line + 5);
- fields |= 4;
- }
- }
- fclose(fp);
- return fields == 7 ? 0 : -1;
-}
-
-/*
- * Corresponds with debugger_action_t enum type in
- * include/cutils/debugger.h.
- */
-static const char *debuggerd_perms[] = {
- NULL, /* crash is only used on self, no check applied */
- "dump_tombstone",
- "dump_backtrace"
-};
-
-static bool selinux_action_allowed(int s, pid_t tid, debugger_action_t action)
-{
- char *scon = NULL, *tcon = NULL;
- const char *tclass = "debuggerd";
- const char *perm;
- bool allowed = false;
-
- if (action <= 0 || action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
- ALOGE("SELinux: No permission defined for debugger action %d", action);
- return false;
+ unique_fd piperead, pipewrite;
+ if (!Pipe(&piperead, &pipewrite)) {
+ err(1, "failed to create pipe");
}
- perm = debuggerd_perms[action];
-
- if (getpeercon(s, &scon) < 0) {
- ALOGE("Cannot get peer context from socket\n");
- goto out;
+ std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
+ bool backtrace = argc == 3;
+ if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
+ backtrace ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
+ redirect_thread.join();
+ errx(1, "failed to dump process %d", pid);
}
- if (getpidcon(tid, &tcon) < 0) {
- ALOGE("Cannot get context for tid %d\n", tid);
- goto out;
- }
-
- allowed = (selinux_check_access(scon, tcon, tclass, perm, NULL) == 0);
-
-out:
- freecon(scon);
- freecon(tcon);
- return allowed;
-}
-
-static int read_request(int fd, debugger_request_t* out_request) {
- ucred cr;
- socklen_t len = sizeof(cr);
- int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
- if (status != 0) {
- ALOGE("cannot get credentials");
- return -1;
- }
-
- ALOGV("reading tid");
- fcntl(fd, F_SETFL, O_NONBLOCK);
-
- pollfd pollfds[1];
- pollfds[0].fd = fd;
- pollfds[0].events = POLLIN;
- pollfds[0].revents = 0;
- status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000));
- if (status != 1) {
- ALOGE("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid);
- return -1;
- }
-
- debugger_msg_t msg;
- memset(&msg, 0, sizeof(msg));
- status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));
- if (status < 0) {
- ALOGE("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid);
- return -1;
- }
- if (status != sizeof(debugger_msg_t)) {
- ALOGE("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid);
- return -1;
- }
-
- out_request->action = static_cast<debugger_action_t>(msg.action);
- out_request->tid = msg.tid;
- out_request->pid = cr.pid;
- out_request->uid = cr.uid;
- out_request->gid = cr.gid;
- out_request->abort_msg_address = msg.abort_msg_address;
- out_request->original_si_code = msg.original_si_code;
-
- if (msg.action == DEBUGGER_ACTION_CRASH) {
- // Ensure that the tid reported by the crashing process is valid.
- char buf[64];
- struct stat s;
- snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid);
- if (stat(buf, &s)) {
- ALOGE("tid %d does not exist in pid %d. ignoring debug request\n",
- out_request->tid, out_request->pid);
- return -1;
- }
- } else if (cr.uid == 0
- || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {
- // Only root or system can ask us to attach to any process and dump it explicitly.
- // However, system is only allowed to collect backtraces but cannot dump tombstones.
- status = get_process_info(out_request->tid, &out_request->pid,
- &out_request->uid, &out_request->gid);
- if (status < 0) {
- ALOGE("tid %d does not exist. ignoring explicit dump request\n", out_request->tid);
- return -1;
- }
-
- if (!selinux_action_allowed(fd, out_request->tid, out_request->action))
- return -1;
- } else {
- // No one else is allowed to dump arbitrary processes.
- return -1;
- }
+ redirect_thread.join();
return 0;
}
-
-static bool should_attach_gdb(debugger_request_t* request) {
- if (request->action == DEBUGGER_ACTION_CRASH) {
- return property_get_bool("debug.debuggerd.wait_for_gdb", false);
- }
- return false;
-}
-
-#if defined(__LP64__)
-static bool is32bit(pid_t tid) {
- char* exeline;
- if (asprintf(&exeline, "/proc/%d/exe", tid) == -1) {
- return false;
- }
- int fd = TEMP_FAILURE_RETRY(open(exeline, O_RDONLY | O_CLOEXEC));
- int saved_errno = errno;
- free(exeline);
- if (fd == -1) {
- ALOGW("Failed to open /proc/%d/exe %s", tid, strerror(saved_errno));
- return false;
- }
-
- char ehdr[EI_NIDENT];
- ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &ehdr, sizeof(ehdr)));
- close(fd);
- if (bytes != (ssize_t) sizeof(ehdr) || memcmp(ELFMAG, ehdr, SELFMAG) != 0) {
- return false;
- }
- if (ehdr[EI_CLASS] == ELFCLASS32) {
- return true;
- }
- return false;
-}
-
-static void redirect_to_32(int fd, debugger_request_t* request) {
- debugger_msg_t msg;
- memset(&msg, 0, sizeof(msg));
- msg.tid = request->tid;
- msg.action = request->action;
-
- int sock_fd = socket_local_client(DEBUGGER32_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
- SOCK_STREAM | SOCK_CLOEXEC);
- if (sock_fd < 0) {
- ALOGE("Failed to connect to debuggerd32: %s", strerror(errno));
- return;
- }
-
- if (TEMP_FAILURE_RETRY(write(sock_fd, &msg, sizeof(msg))) != (ssize_t) sizeof(msg)) {
- ALOGE("Failed to write request to debuggerd32 socket: %s", strerror(errno));
- close(sock_fd);
- return;
- }
-
- char ack;
- if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) == -1) {
- ALOGE("Failed to read ack from debuggerd32 socket: %s", strerror(errno));
- close(sock_fd);
- return;
- }
-
- char buffer[1024];
- ssize_t bytes_read;
- while ((bytes_read = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
- ssize_t bytes_to_send = bytes_read;
- ssize_t bytes_written;
- do {
- bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer + bytes_read - bytes_to_send,
- bytes_to_send));
- if (bytes_written == -1) {
- if (errno == EAGAIN) {
- // Retry the write.
- continue;
- }
- ALOGE("Error while writing data to fd: %s", strerror(errno));
- break;
- }
- bytes_to_send -= bytes_written;
- } while (bytes_written != 0 && bytes_to_send > 0);
- if (bytes_to_send != 0) {
- ALOGE("Failed to write all data to fd: read %zd, sent %zd", bytes_read, bytes_to_send);
- break;
- }
- }
- close(sock_fd);
-}
-#endif
-
-static void handle_request(int fd) {
- ALOGV("handle_request(%d)\n", fd);
-
- debugger_request_t request;
- memset(&request, 0, sizeof(request));
- int status = read_request(fd, &request);
- if (!status) {
- ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n",
- request.pid, request.uid, request.gid, request.tid);
-
-#if defined(__LP64__)
- // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
- // to the 64 bit debuggerd. If the process is a 32 bit executable,
- // redirect the request to the 32 bit debuggerd.
- if (is32bit(request.tid)) {
- // Only dump backtrace and dump tombstone requests can be redirected.
- if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE
- || request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- redirect_to_32(fd, &request);
- } else {
- ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n",
- request.action);
- }
- close(fd);
- return;
- }
-#endif
-
- // At this point, the thread that made the request is blocked in
- // a read() call. If the thread has crashed, then this gives us
- // time to PTRACE_ATTACH to it before it has a chance to really fault.
- //
- // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
- // won't necessarily have stopped by the time ptrace() returns. (We
- // currently assume it does.) We write to the file descriptor to
- // ensure that it can run as soon as we call PTRACE_CONT below.
- // See details in bionic/libc/linker/debugger.c, in function
- // debugger_signal_handler().
- if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) {
- ALOGE("ptrace attach failed: %s\n", strerror(errno));
- } else {
- bool detach_failed = false;
- bool tid_unresponsive = false;
- bool attach_gdb = should_attach_gdb(&request);
- if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
- ALOGE("failed responding to client: %s\n", strerror(errno));
- } else {
- char* tombstone_path = NULL;
-
- if (request.action == DEBUGGER_ACTION_CRASH) {
- close(fd);
- fd = -1;
- }
-
- int total_sleep_time_usec = 0;
- for (;;) {
- int signal = wait_for_sigstop(request.tid, &total_sleep_time_usec, &detach_failed);
- if (signal == -1) {
- tid_unresponsive = true;
- break;
- }
-
- switch (signal) {
- case SIGSTOP:
- if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- ALOGV("stopped -- dumping to tombstone\n");
- tombstone_path = engrave_tombstone(request.pid, request.tid,
- signal, request.original_si_code,
- request.abort_msg_address, true,
- &detach_failed, &total_sleep_time_usec);
- } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
- ALOGV("stopped -- dumping to fd\n");
- dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed,
- &total_sleep_time_usec);
- } else {
- ALOGV("stopped -- continuing\n");
- status = ptrace(PTRACE_CONT, request.tid, 0, 0);
- if (status) {
- ALOGE("ptrace continue failed: %s\n", strerror(errno));
- }
- continue; // loop again
- }
- break;
-
- case SIGABRT:
- case SIGBUS:
- case SIGFPE:
- case SIGILL:
- case SIGSEGV:
-#ifdef SIGSTKFLT
- case SIGSTKFLT:
-#endif
- case SIGTRAP:
- ALOGV("stopped -- fatal signal\n");
- // Send a SIGSTOP to the process to make all of
- // the non-signaled threads stop moving. Without
- // this we get a lot of "ptrace detach failed:
- // No such process".
- kill(request.pid, SIGSTOP);
- // don't dump sibling threads when attaching to GDB because it
- // makes the process less reliable, apparently...
- tombstone_path = engrave_tombstone(request.pid, request.tid,
- signal, request.original_si_code,
- request.abort_msg_address, !attach_gdb,
- &detach_failed, &total_sleep_time_usec);
- break;
-
- default:
- ALOGE("process stopped due to unexpected signal %d\n", signal);
- break;
- }
- break;
- }
-
- if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- if (tombstone_path) {
- write(fd, tombstone_path, strlen(tombstone_path));
- }
- close(fd);
- fd = -1;
- }
- free(tombstone_path);
- }
-
- if (!tid_unresponsive) {
- ALOGV("detaching");
- if (attach_gdb) {
- // stop the process so we can debug
- kill(request.pid, SIGSTOP);
- }
- if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {
- ALOGE("ptrace detach from %d failed: %s", request.tid, strerror(errno));
- detach_failed = true;
- } else if (attach_gdb) {
- // if debug.db.uid is set, its value indicates if we should wait
- // for user action for the crashing process.
- // in this case, we log a message and turn the debug LED on
- // waiting for a gdb connection (for instance)
- wait_for_user_action(request);
- }
- }
-
- // resume stopped process (so it can crash in peace).
- kill(request.pid, SIGCONT);
-
- // If we didn't successfully detach, we're still the parent, and the
- // actual parent won't receive a death notification via wait(2). At this point
- // there's not much we can do about that.
- if (detach_failed) {
- ALOGE("debuggerd committing suicide to free the zombie!\n");
- kill(getpid(), SIGKILL);
- }
- }
-
- }
- if (fd >= 0) {
- close(fd);
- }
-}
-
-static int do_server() {
- // debuggerd crashes can't be reported to debuggerd.
- // Reset all of the crash handlers.
- signal(SIGABRT, SIG_DFL);
- signal(SIGBUS, SIG_DFL);
- signal(SIGFPE, SIG_DFL);
- signal(SIGILL, SIG_DFL);
- signal(SIGSEGV, SIG_DFL);
-#ifdef SIGSTKFLT
- signal(SIGSTKFLT, SIG_DFL);
-#endif
- signal(SIGTRAP, SIG_DFL);
-
- // Ignore failed writes to closed sockets
- signal(SIGPIPE, SIG_IGN);
-
- int logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
- if (logsocket < 0) {
- logsocket = -1;
- } else {
- fcntl(logsocket, F_SETFD, FD_CLOEXEC);
- }
-
- struct sigaction act;
- act.sa_handler = SIG_DFL;
- sigemptyset(&act.sa_mask);
- sigaddset(&act.sa_mask,SIGCHLD);
- act.sa_flags = SA_NOCLDWAIT;
- sigaction(SIGCHLD, &act, 0);
-
- int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
- if (s < 0)
- return 1;
- fcntl(s, F_SETFD, FD_CLOEXEC);
-
- ALOGI("debuggerd: " __DATE__ " " __TIME__ "\n");
-
- for (;;) {
- sockaddr addr;
- socklen_t alen = sizeof(addr);
-
- ALOGV("waiting for connection\n");
- int fd = accept(s, &addr, &alen);
- if (fd < 0) {
- ALOGV("accept failed: %s\n", strerror(errno));
- continue;
- }
-
- fcntl(fd, F_SETFD, FD_CLOEXEC);
-
- handle_request(fd);
- }
- return 0;
-}
-
-static int do_explicit_dump(pid_t tid, bool dump_backtrace) {
- fprintf(stdout, "Sending request to dump task %d.\n", tid);
-
- if (dump_backtrace) {
- fflush(stdout);
- if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) {
- fputs("Error dumping backtrace.\n", stderr);
- return 1;
- }
- } else {
- char tombstone_path[PATH_MAX];
- if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) {
- fputs("Error dumping tombstone.\n", stderr);
- return 1;
- }
- fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);
- }
- return 0;
-}
-
-static void usage() {
- fputs("Usage: -b [<tid>]\n"
- " -b dump backtrace to console, otherwise dump full tombstone file\n"
- "\n"
- "If tid specified, sends a request to debuggerd to dump that task.\n"
- "Otherwise, starts the debuggerd server.\n", stderr);
-}
-
-int main(int argc, char** argv) {
- union selinux_callback cb;
- if (argc == 1) {
- cb.func_log = selinux_log_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
- return do_server();
- }
-
- bool dump_backtrace = false;
- bool have_tid = false;
- pid_t tid = 0;
- for (int i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-b")) {
- dump_backtrace = true;
- } else if (!have_tid) {
- tid = atoi(argv[i]);
- have_tid = true;
- } else {
- usage();
- return 1;
- }
- }
- if (!have_tid) {
- usage();
- return 1;
- }
- return do_explicit_dump(tid, dump_backtrace);
-}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
new file mode 100644
index 0000000..e7503e9
--- /dev/null
+++ b/debuggerd/debuggerd_test.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <regex>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <debuggerd/handler.h>
+#include <debuggerd/protocol.h>
+#include <debuggerd/util.h>
+#include <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+#if defined(__LP64__)
+#define CRASHER_PATH "/system/xbin/crasher64"
+#define ARCH_SUFFIX "64"
+#else
+#define CRASHER_PATH "/system/xbin/crasher"
+#define ARCH_SUFFIX ""
+#endif
+
+constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb";
+
+#define TIMEOUT(seconds, expr) \
+ [&]() { \
+ struct sigaction old_sigaction; \
+ struct sigaction new_sigaction = {}; \
+ new_sigaction.sa_handler = [](int) {}; \
+ if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
+ err(1, "sigaction failed"); \
+ } \
+ alarm(seconds); \
+ auto value = expr; \
+ int saved_errno = errno; \
+ if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \
+ err(1, "sigaction failed"); \
+ } \
+ alarm(0); \
+ errno = saved_errno; \
+ return value; \
+ }()
+
+#define ASSERT_MATCH(str, pattern) \
+ do { \
+ std::regex r((pattern)); \
+ if (!std::regex_search((str), r)) { \
+ FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
+ } \
+ } while (0)
+
+class CrasherTest : public ::testing::Test {
+ public:
+ pid_t crasher_pid = -1;
+ bool previous_wait_for_gdb;
+ unique_fd crasher_pipe;
+ unique_fd intercept_fd;
+
+ CrasherTest();
+ ~CrasherTest();
+
+ void StartIntercept(unique_fd* output_fd);
+
+ // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
+ void FinishIntercept(int* result);
+
+ void StartProcess(std::function<void()> function);
+ void StartCrasher(const std::string& crash_type);
+ void FinishCrasher();
+ void AssertDeath(int signo);
+};
+
+CrasherTest::CrasherTest() {
+ previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
+ android::base::SetProperty(kWaitForGdbKey, "0");
+}
+
+CrasherTest::~CrasherTest() {
+ if (crasher_pid != -1) {
+ kill(crasher_pid, SIGKILL);
+ int status;
+ waitpid(crasher_pid, &status, WUNTRACED);
+ }
+
+ android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
+}
+
+void CrasherTest::StartIntercept(unique_fd* output_fd) {
+ if (crasher_pid == -1) {
+ FAIL() << "crasher hasn't been started";
+ }
+
+ intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ if (intercept_fd == -1) {
+ FAIL() << "failed to contact tombstoned: " << strerror(errno);
+ }
+
+ InterceptRequest req = {.pid = crasher_pid };
+
+ unique_fd output_pipe_write;
+ if (!Pipe(output_fd, &output_pipe_write)) {
+ FAIL() << "failed to create output pipe: " << strerror(errno);
+ }
+
+ std::string pipe_size_str;
+ int pipe_buffer_size;
+ if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+ FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
+ }
+
+ pipe_size_str = android::base::Trim(pipe_size_str);
+
+ if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+ FAIL() << "failed to parse pipe max size";
+ }
+
+ if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+ FAIL() << "failed to set pipe size: " << strerror(errno);
+ }
+
+ if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+ FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
+ }
+}
+
+void CrasherTest::FinishIntercept(int* result) {
+ InterceptResponse response;
+
+ // Timeout for tombstoned intercept is 10 seconds.
+ ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
+ if (rc == -1) {
+ FAIL() << "failed to read response from tombstoned: " << strerror(errno);
+ } else if (rc == 0) {
+ *result = -1;
+ } else if (rc != sizeof(response)) {
+ FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+ << ", received " << rc;
+ } else {
+ *result = response.success;
+ }
+}
+
+void CrasherTest::StartProcess(std::function<void()> function) {
+ unique_fd read_pipe;
+ unique_fd crasher_read_pipe;
+ if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
+ FAIL() << "failed to create pipe: " << strerror(errno);
+ }
+
+ crasher_pid = fork();
+ if (crasher_pid == -1) {
+ FAIL() << "fork failed: " << strerror(errno);
+ } else if (crasher_pid == 0) {
+ unique_fd devnull(open("/dev/null", O_WRONLY));
+ dup2(crasher_read_pipe.get(), STDIN_FILENO);
+ dup2(devnull.get(), STDOUT_FILENO);
+ dup2(devnull.get(), STDERR_FILENO);
+ function();
+ _exit(0);
+ }
+}
+
+void CrasherTest::StartCrasher(const std::string& crash_type) {
+ std::string type = "wait-" + crash_type;
+ StartProcess([type]() {
+ execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
+ err(1, "exec failed");
+ });
+}
+
+void CrasherTest::FinishCrasher() {
+ if (crasher_pipe == -1) {
+ FAIL() << "crasher pipe uninitialized";
+ }
+
+ ssize_t rc = write(crasher_pipe.get(), "\n", 1);
+ if (rc == -1) {
+ FAIL() << "failed to write to crasher pipe: " << strerror(errno);
+ } else if (rc == 0) {
+ FAIL() << "crasher pipe was closed";
+ }
+}
+
+void CrasherTest::AssertDeath(int signo) {
+ int status;
+ pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
+ if (pid != crasher_pid) {
+ FAIL() << "failed to wait for crasher: " << strerror(errno);
+ }
+
+ if (!WIFSIGNALED(status)) {
+ FAIL() << "crasher didn't terminate via a signal";
+ }
+ ASSERT_EQ(signo, WTERMSIG(status));
+ crasher_pid = -1;
+}
+
+static void ConsumeFd(unique_fd fd, std::string* output) {
+ constexpr size_t read_length = PAGE_SIZE;
+ std::string result;
+
+ while (true) {
+ size_t offset = result.size();
+ result.resize(result.size() + PAGE_SIZE);
+ ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
+ if (rc == -1) {
+ FAIL() << "read failed: " << strerror(errno);
+ } else if (rc == 0) {
+ result.resize(result.size() - PAGE_SIZE);
+ break;
+ }
+
+ result.resize(result.size() - PAGE_SIZE + rc);
+ }
+
+ *output = std::move(result);
+}
+
+TEST_F(CrasherTest, smoke) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("SIGSEGV");
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
+}
+
+TEST_F(CrasherTest, abort) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("abort");
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+}
+
+TEST_F(CrasherTest, signal) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("abort");
+ StartIntercept(&output_fd);
+
+ // Wait for a bit, or we might end up killing the process before the signal
+ // handler even gets a chance to be registered.
+ std::this_thread::sleep_for(100ms);
+ ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
+
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
+ ASSERT_MATCH(result, R"(backtrace:)");
+}
+
+TEST_F(CrasherTest, abort_message) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("smash-stack");
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(Abort message: 'stack corruption detected \(-fstack-protector\)')");
+}
+
+TEST_F(CrasherTest, intercept_timeout) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("abort");
+ StartIntercept(&output_fd);
+
+ // Don't let crasher finish until we timeout.
+ FinishIntercept(&intercept_result);
+
+ ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
+ << intercept_result << ")";
+
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+}
+
+TEST_F(CrasherTest, wait_for_gdb) {
+ if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
+ FAIL() << "failed to enable wait_for_gdb";
+ }
+ sleep(1);
+
+ StartCrasher("abort");
+ FinishCrasher();
+
+ int status;
+ ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
+ ASSERT_TRUE(WIFSTOPPED(status));
+ ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
+
+ ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
+
+ AssertDeath(SIGABRT);
+}
+
+// wait_for_gdb shouldn't trigger on manually sent signals.
+TEST_F(CrasherTest, wait_for_gdb_signal) {
+ if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
+ FAIL() << "failed to enable wait_for_gdb";
+ }
+
+ StartCrasher("abort");
+ ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
+ AssertDeath(SIGSEGV);
+}
+
+TEST_F(CrasherTest, backtrace) {
+ std::string result;
+ int intercept_result;
+ unique_fd output_fd;
+ StartCrasher("abort");
+ StartIntercept(&output_fd);
+
+ std::this_thread::sleep_for(500ms);
+
+ sigval val;
+ val.sival_int = 1;
+ ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
+
+ int status;
+ ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+}
+
+TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
+ StartProcess([]() {
+ prctl(PR_SET_DUMPABLE, 0);
+ volatile char* null = static_cast<char*>(nullptr);
+ *null = '\0';
+ });
+ AssertDeath(SIGSEGV);
+}
+
+TEST_F(CrasherTest, PR_SET_DUMPABLE_0_raise) {
+ StartProcess([]() {
+ prctl(PR_SET_DUMPABLE, 0);
+ raise(SIGUSR1);
+ });
+ AssertDeath(SIGUSR1);
+}
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/elf_utils.cpp
deleted file mode 100644
index 5ea03e7..0000000
--- a/debuggerd/elf_utils.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <elf.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-
-#include <backtrace/Backtrace.h>
-#include <base/stringprintf.h>
-#include <log/log.h>
-
-#include "elf_utils.h"
-
-#define NOTE_ALIGN(size) ((size + 3) & ~3)
-
-template <typename HdrType, typename PhdrType, typename NhdrType>
-static bool get_build_id(
- Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) {
- HdrType hdr;
-
- memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
-
- // First read the rest of the header.
- if (backtrace->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
- sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
- return false;
- }
-
- for (size_t i = 0; i < hdr.e_phnum; i++) {
- PhdrType phdr;
- if (backtrace->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
- reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
- return false;
- }
- // Looking for the .note.gnu.build-id note.
- if (phdr.p_type == PT_NOTE) {
- size_t hdr_size = phdr.p_filesz;
- uintptr_t addr = base_addr + phdr.p_offset;
- while (hdr_size >= sizeof(NhdrType)) {
- NhdrType nhdr;
- if (backtrace->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
- return false;
- }
- addr += sizeof(nhdr);
- if (nhdr.n_type == NT_GNU_BUILD_ID) {
- // Skip the name (which is the owner and should be "GNU").
- addr += NOTE_ALIGN(nhdr.n_namesz);
- uint8_t build_id_data[128];
- if (nhdr.n_namesz > sizeof(build_id_data)) {
- ALOGE("Possible corrupted note, name size value is too large: %u",
- nhdr.n_namesz);
- return false;
- }
- if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
- return false;
- }
-
- build_id->clear();
- for (size_t bytes = 0; bytes < nhdr.n_descsz; bytes++) {
- *build_id += android::base::StringPrintf("%02x", build_id_data[bytes]);
- }
-
- return true;
- } else {
- // Move past the extra note data.
- hdr_size -= sizeof(nhdr);
- size_t skip_bytes = NOTE_ALIGN(nhdr.n_namesz) + NOTE_ALIGN(nhdr.n_descsz);
- addr += skip_bytes;
- if (hdr_size < skip_bytes) {
- break;
- }
- hdr_size -= skip_bytes;
- }
- }
- }
- }
- return false;
-}
-
-bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_id) {
- // Read and verify the elf magic number first.
- uint8_t e_ident[EI_NIDENT];
- if (backtrace->Read(addr, e_ident, SELFMAG) != SELFMAG) {
- return false;
- }
-
- if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
- return false;
- }
-
- // Read the rest of EI_NIDENT.
- if (backtrace->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
- return false;
- }
-
- if (e_ident[EI_CLASS] == ELFCLASS32) {
- return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(backtrace, addr, e_ident, build_id);
- } else if (e_ident[EI_CLASS] == ELFCLASS64) {
- return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(backtrace, addr, e_ident, build_id);
- }
-
- return false;
-}
diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp
deleted file mode 100644
index 751c4fb..0000000
--- a/debuggerd/getevent.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/inotify.h>
-#include <sys/limits.h>
-#include <sys/poll.h>
-#include <linux/input.h>
-#include <errno.h>
-#include <cutils/log.h>
-
-static struct pollfd* ufds;
-static char** device_names;
-static int nfds;
-
-static int open_device(const char* device) {
- int version;
- int fd;
- struct pollfd* new_ufds;
- char** new_device_names;
- char name[80];
- char location[80];
- char idstr[80];
- struct input_id id;
-
- fd = open(device, O_RDWR);
- if (fd < 0) {
- return -1;
- }
-
- if (ioctl(fd, EVIOCGVERSION, &version)) {
- return -1;
- }
- if (ioctl(fd, EVIOCGID, &id)) {
- return -1;
- }
- name[sizeof(name) - 1] = '\0';
- location[sizeof(location) - 1] = '\0';
- idstr[sizeof(idstr) - 1] = '\0';
- if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
- name[0] = '\0';
- }
- if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
- location[0] = '\0';
- }
- if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
- idstr[0] = '\0';
- }
-
- new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1)));
- if (new_ufds == NULL) {
- fprintf(stderr, "out of memory\n");
- return -1;
- }
- ufds = new_ufds;
- new_device_names = reinterpret_cast<char**>(realloc(
- device_names, sizeof(device_names[0]) * (nfds + 1)));
- if (new_device_names == NULL) {
- fprintf(stderr, "out of memory\n");
- return -1;
- }
- device_names = new_device_names;
- ufds[nfds].fd = fd;
- ufds[nfds].events = POLLIN;
- device_names[nfds] = strdup(device);
- nfds++;
-
- return 0;
-}
-
-int close_device(const char* device) {
- int i;
- for (i = 1; i < nfds; i++) {
- if (strcmp(device_names[i], device) == 0) {
- int count = nfds - i - 1;
- free(device_names[i]);
- memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
- memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
- nfds--;
- return 0;
- }
- }
- return -1;
-}
-
-static int read_notify(const char* dirname, int nfd) {
- int res;
- char devname[PATH_MAX];
- char* filename;
- char event_buf[512];
- int event_size;
- int event_pos = 0;
- struct inotify_event *event;
-
- res = read(nfd, event_buf, sizeof(event_buf));
- if (res < (int)sizeof(*event)) {
- if (errno == EINTR)
- return 0;
- fprintf(stderr, "could not get event, %s\n", strerror(errno));
- return 1;
- }
-
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
-
- while (res >= (int)sizeof(*event)) {
- event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
- if (event->len) {
- strcpy(filename, event->name);
- if (event->mask & IN_CREATE) {
- open_device(devname);
- } else {
- close_device(devname);
- }
- }
- event_size = sizeof(*event) + event->len;
- res -= event_size;
- event_pos += event_size;
- }
- return 0;
-}
-
-static int scan_dir(const char* dirname) {
- char devname[PATH_MAX];
- char* filename;
- DIR* dir;
- struct dirent* de;
- dir = opendir(dirname);
- if (dir == NULL)
- return -1;
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
- while ((de = readdir(dir))) {
- if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
- (de->d_name[1] == '.' && de->d_name[2] == '\0'))
- continue;
- strcpy(filename, de->d_name);
- open_device(devname);
- }
- closedir(dir);
- return 0;
-}
-
-int init_getevent() {
- int res;
- const char* device_path = "/dev/input";
-
- nfds = 1;
- ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0])));
- ufds[0].fd = inotify_init();
- ufds[0].events = POLLIN;
-
- res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
- if (res < 0) {
- return 1;
- }
- res = scan_dir(device_path);
- if (res < 0) {
- return 1;
- }
- return 0;
-}
-
-void uninit_getevent() {
- int i;
- for (i = 0; i < nfds; i++) {
- close(ufds[i].fd);
- }
- free(ufds);
- ufds = 0;
- nfds = 0;
-}
-
-int get_event(struct input_event* event, int timeout) {
- int res;
- int i;
- int pollres;
- const char* device_path = "/dev/input";
- while (1) {
- pollres = poll(ufds, nfds, timeout);
- if (pollres == 0) {
- return 1;
- }
- if (ufds[0].revents & POLLIN) {
- read_notify(device_path, ufds[0].fd);
- }
- for (i = 1; i < nfds; i++) {
- if (ufds[i].revents) {
- if (ufds[i].revents & POLLIN) {
- res = read(ufds[i].fd, event, sizeof(*event));
- if (res < static_cast<int>(sizeof(event))) {
- fprintf(stderr, "could not get event\n");
- return -1;
- }
- return 0;
- }
- }
- }
- }
- return 0;
-}
diff --git a/debuggerd/getevent.h b/debuggerd/getevent.h
deleted file mode 100644
index 426139d..0000000
--- a/debuggerd/getevent.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_GETEVENT_H
-#define _DEBUGGERD_GETEVENT_H
-
-int init_getevent();
-void uninit_getevent();
-int get_event(struct input_event* event, int timeout);
-
-#endif // _DEBUGGERD_GETEVENT_H
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
new file mode 100644
index 0000000..9469bbd
--- /dev/null
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2008 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 "debuggerd/handler.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "private/bionic_futex.h"
+#include "private/libc_logging.h"
+
+// see man(2) prctl, specifically the section about PR_GET_NAME
+#define MAX_TASK_NAME_LEN (16)
+
+#if defined(__LP64__)
+#define CRASH_DUMP_NAME "crash_dump64"
+#else
+#define CRASH_DUMP_NAME "crash_dump32"
+#endif
+
+#define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME
+
+static debuggerd_callbacks_t g_callbacks;
+
+// Mutex to ensure only one crashing thread dumps itself.
+static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
+static void __noreturn __printflike(1, 2) fatal(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ __libc_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args);
+ _exit(1);
+}
+
+static void __noreturn __printflike(1, 2) fatal_errno(const char* fmt, ...) {
+ int err = errno;
+ va_list args;
+ va_start(args, fmt);
+
+ char buf[4096];
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ fatal("%s: %s", buf, strerror(err));
+}
+
+/*
+ * Writes a summary of the signal to the log file. We do this so that, if
+ * for some reason we're not able to contact debuggerd, there is still some
+ * indication of the failure in the log.
+ *
+ * We could be here as a result of native heap corruption, or while a
+ * mutex is being held, so we don't want to use any libc functions that
+ * could allocate memory or hold a lock.
+ */
+static void log_signal_summary(int signum, const siginfo_t* info) {
+ char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
+ if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
+ strcpy(thread_name, "<name unknown>");
+ } else {
+ // short names are null terminated by prctl, but the man page
+ // implies that 16 byte names are not.
+ thread_name[MAX_TASK_NAME_LEN] = 0;
+ }
+
+ if (signum == DEBUGGER_SIGNAL) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "Requested dump for tid %d (%s)", gettid(),
+ thread_name);
+ return;
+ }
+
+ const char* signal_name = "???";
+ bool has_address = false;
+ switch (signum) {
+ case SIGABRT:
+ signal_name = "SIGABRT";
+ break;
+ case SIGBUS:
+ signal_name = "SIGBUS";
+ has_address = true;
+ break;
+ case SIGFPE:
+ signal_name = "SIGFPE";
+ has_address = true;
+ break;
+ case SIGILL:
+ signal_name = "SIGILL";
+ has_address = true;
+ break;
+ case SIGSEGV:
+ signal_name = "SIGSEGV";
+ has_address = true;
+ break;
+#if defined(SIGSTKFLT)
+ case SIGSTKFLT:
+ signal_name = "SIGSTKFLT";
+ break;
+#endif
+ case SIGSYS:
+ signal_name = "SIGSYS";
+ break;
+ case SIGTRAP:
+ signal_name = "SIGTRAP";
+ break;
+ }
+
+ // "info" will be null if the siginfo_t information was not available.
+ // Many signals don't have an address or a code.
+ char code_desc[32]; // ", code -6"
+ char addr_desc[32]; // ", fault addr 0x1234"
+ addr_desc[0] = code_desc[0] = 0;
+ if (info != nullptr) {
+ __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
+ if (has_address) {
+ __libc_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
+ }
+ }
+
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)", signum,
+ signal_name, code_desc, addr_desc, gettid(), thread_name);
+}
+
+/*
+ * Returns true if the handler for signal "signum" has SA_SIGINFO set.
+ */
+static bool have_siginfo(int signum) {
+ struct sigaction old_action;
+ if (sigaction(signum, nullptr, &old_action) < 0) {
+ __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
+ strerror(errno));
+ return false;
+ }
+ return (old_action.sa_flags & SA_SIGINFO) != 0;
+}
+
+struct debugger_thread_info {
+ bool crash_dump_started;
+ pid_t crashing_tid;
+ pid_t pseudothread_tid;
+ int signal_number;
+ siginfo_t* info;
+};
+
+// Logging and contacting debuggerd requires free file descriptors, which we might not have.
+// Work around this by spawning a "thread" that shares its parent's address space, but not its file
+// descriptor table, so that we can close random file descriptors without affecting the original
+// process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning
+// process.
+static void* pseudothread_stack;
+
+static int debuggerd_dispatch_pseudothread(void* arg) {
+ debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
+
+ for (int i = 0; i < 1024; ++i) {
+ close(i);
+ }
+
+ int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
+
+ // devnull will be 0.
+ TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO));
+ TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO));
+
+ int pipefds[2];
+ if (pipe(pipefds) != 0) {
+ fatal_errno("failed to create pipe");
+ }
+
+ // Don't use fork(2) to avoid calling pthread_atfork handlers.
+ int forkpid = clone(nullptr, nullptr, SIGCHLD, nullptr);
+ if (forkpid == -1) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s",
+ strerror(errno));
+ } else if (forkpid == 0) {
+ TEMP_FAILURE_RETRY(dup2(pipefds[1], STDOUT_FILENO));
+ close(pipefds[0]);
+ close(pipefds[1]);
+
+ char buf[10];
+ snprintf(buf, sizeof(buf), "%d", thread_info->crashing_tid);
+ execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, buf, nullptr);
+
+ fatal_errno("exec failed");
+ } else {
+ close(pipefds[1]);
+ char buf[4];
+ ssize_t rc = TEMP_FAILURE_RETRY(read(pipefds[0], &buf, sizeof(buf)));
+ if (rc == -1) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+ } else if (rc == 0) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+ } else if (rc != 1) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc",
+ "read of IPC pipe returned unexpected value: %zd", rc);
+ } else {
+ if (buf[0] != '\1') {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+ } else {
+ thread_info->crash_dump_started = true;
+ }
+ }
+ close(pipefds[0]);
+
+ // Don't leave a zombie child.
+ siginfo_t child_siginfo;
+ if (TEMP_FAILURE_RETRY(waitid(P_PID, forkpid, &child_siginfo, WEXITED)) != 0) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+ strerror(errno));
+ thread_info->crash_dump_started = false;
+ }
+ }
+
+ syscall(__NR_exit, 0);
+ return 0;
+}
+
+static void resend_signal(siginfo_t* info, bool crash_dump_started) {
+ // Signals can either be fatal or nonfatal.
+ // For fatal signals, crash_dump will send us the signal we crashed with
+ // before resuming us, so that processes using waitpid on us will see that we
+ // exited with the correct exit status (e.g. so that sh will report
+ // "Segmentation fault" instead of "Killed"). For this to work, we need
+ // to deregister our signal handler for that signal before continuing.
+ if (info->si_signo != DEBUGGER_SIGNAL) {
+ signal(info->si_signo, SIG_DFL);
+ }
+
+ // We need to return from our signal handler so that crash_dump can see the
+ // signal via ptrace and dump the thread that crashed. However, returning
+ // does not guarantee that the signal will be thrown again, even for SIGSEGV
+ // and friends, since the signal could have been sent manually. We blocked
+ // all signals when registering the handler, so resending the signal (using
+ // rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered
+ // when our signal handler returns.
+ if (crash_dump_started || info->si_signo != DEBUGGER_SIGNAL) {
+ int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), info->si_signo, info);
+ if (rc != 0) {
+ fatal_errno("failed to resend signal during crash");
+ }
+ }
+
+ if (info->si_signo == DEBUGGER_SIGNAL) {
+ pthread_mutex_unlock(&crash_mutex);
+ }
+}
+
+// Handler that does crash dumping by forking and doing the processing in the child.
+// Do this by ptracing the relevant thread, and then execing debuggerd to do the actual dump.
+static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) {
+ int ret = pthread_mutex_lock(&crash_mutex);
+ if (ret != 0) {
+ __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+ return;
+ }
+
+ // It's possible somebody cleared the SA_SIGINFO flag, which would mean
+ // our "info" arg holds an undefined value.
+ if (!have_siginfo(signal_number)) {
+ info = nullptr;
+ }
+
+ struct siginfo si = {};
+ if (!info) {
+ memset(&si, 0, sizeof(si));
+ si.si_signo = signal_number;
+ si.si_code = SI_USER;
+ si.si_pid = getpid();
+ si.si_uid = getuid();
+ info = &si;
+ } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
+ // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
+ // that contain commit 66dd34a (3.9+). The manpage claims to only allow
+ // negative si_code values that are not SI_TKILL, but 66dd34a changed the
+ // check to allow all si_code values in calls coming from inside the house.
+ }
+
+ log_signal_summary(signal_number, info);
+
+ if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+ // The process has NO_NEW_PRIVS enabled, so we can't transition to the crash_dump context.
+ __libc_format_log(ANDROID_LOG_INFO, "libc",
+ "Suppressing debuggerd output because prctl(PR_GET_NO_NEW_PRIVS)==1");
+ resend_signal(info, false);
+ return;
+ }
+
+ void* abort_message = nullptr;
+ if (g_callbacks.get_abort_message) {
+ abort_message = g_callbacks.get_abort_message();
+ }
+ // Populate si_value with the abort message address, if found.
+ if (abort_message) {
+ info->si_value.sival_ptr = abort_message;
+ }
+
+ debugger_thread_info thread_info = {
+ .crash_dump_started = false,
+ .pseudothread_tid = -1,
+ .crashing_tid = gettid(),
+ .signal_number = signal_number,
+ .info = info
+ };
+
+ // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
+ pid_t child_pid =
+ clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
+ CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
+ &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
+ if (child_pid == -1) {
+ fatal_errno("failed to spawn debuggerd dispatch thread");
+ }
+
+ // Wait for the child to start...
+ __futex_wait(&thread_info.pseudothread_tid, -1, nullptr);
+
+ // and then wait for it to finish.
+ __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr);
+
+ // Signals can either be fatal or nonfatal.
+ // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
+ // crashed with, so that processes using waitpid on us will see that we
+ // exited with the correct exit status (e.g. so that sh will report
+ // "Segmentation fault" instead of "Killed"). For this to work, we need
+ // to deregister our signal handler for that signal before continuing.
+ if (signal_number != DEBUGGER_SIGNAL) {
+ signal(signal_number, SIG_DFL);
+ }
+
+ resend_signal(info, thread_info.crash_dump_started);
+}
+
+void debuggerd_init(debuggerd_callbacks_t* callbacks) {
+ if (callbacks) {
+ g_callbacks = *callbacks;
+ }
+
+ void* thread_stack_allocation =
+ mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (thread_stack_allocation == MAP_FAILED) {
+ fatal_errno("failed to allocate debuggerd thread stack");
+ }
+
+ char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
+ if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
+ fatal_errno("failed to mprotect debuggerd thread stack");
+ }
+
+ // Stack grows negatively, set it to the last byte in the page...
+ stack = (stack + PAGE_SIZE - 1);
+ // and align it.
+ stack -= 15;
+ pseudothread_stack = stack;
+
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ sigfillset(&action.sa_mask);
+ action.sa_sigaction = debuggerd_signal_handler;
+ action.sa_flags = SA_RESTART | SA_SIGINFO;
+
+ // Use the alternate signal stack if available so we can catch stack overflows.
+ action.sa_flags |= SA_ONSTACK;
+ debuggerd_register_handlers(&action);
+}
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
new file mode 100644
index 0000000..91f143b
--- /dev/null
+++ b/debuggerd/include/debuggerd/client.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+enum DebuggerdDumpType {
+ kDebuggerdBacktrace,
+ kDebuggerdTombstone,
+};
+
+// Trigger a dump of specified process to output_fd.
+// output_fd is *not* consumed, timeouts <= 0 will wait forever.
+bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
+ enum DebuggerdDumpType dump_type, int timeout_ms);
+
+int dump_backtrace_to_file(pid_t tid, int fd);
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
new file mode 100644
index 0000000..7196e0a
--- /dev/null
+++ b/debuggerd/include/debuggerd/handler.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+// These callbacks are called in a signal handler, and thus must be async signal safe.
+// If null, the callbacks will not be called.
+typedef struct {
+ struct abort_msg_t* (*get_abort_message)();
+ void (*post_dump)();
+} debuggerd_callbacks_t;
+
+void debuggerd_init(debuggerd_callbacks_t* callbacks);
+
+// DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both
+// triggered via DEBUGGER_SIGNAL. The debugger_action_t is sent via si_value
+// using sigqueue(2) or equivalent. If no si_value is specified (e.g. if the
+// signal is sent by kill(2)), the default behavior is to print the backtrace
+// to the log.
+#define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
+
+static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
+ sigaction(SIGABRT, action, nullptr);
+ sigaction(SIGBUS, action, nullptr);
+ sigaction(SIGFPE, action, nullptr);
+ sigaction(SIGILL, action, nullptr);
+ sigaction(SIGSEGV, action, nullptr);
+#if defined(SIGSTKFLT)
+ sigaction(SIGSTKFLT, action, nullptr);
+#endif
+ sigaction(SIGSYS, action, nullptr);
+ sigaction(SIGTRAP, action, nullptr);
+ sigaction(DEBUGGER_SIGNAL, action, nullptr);
+}
+
+__END_DECLS
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
new file mode 100644
index 0000000..bb2ab0d
--- /dev/null
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+// Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
+// Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
+constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
+constexpr char kTombstonedInterceptSocketName[] = "tombstoned_intercept";
+
+enum class CrashPacketType : uint8_t {
+ // Initial request from crash_dump.
+ kDumpRequest = 0,
+
+ // Notification of a completed crash dump.
+ // Sent after a dump is completed and the process has been untraced, but
+ // before it has been resumed with SIGCONT.
+ kCompletedDump,
+
+ // Responses to kRequest.
+ // kPerformDump sends along an output fd via cmsg(3).
+ kPerformDump = 128,
+ kAbortDump,
+};
+
+struct DumpRequest {
+ int32_t pid;
+};
+
+// The full packet must always be written, regardless of whether the union is used.
+struct TombstonedCrashPacket {
+ CrashPacketType packet_type;
+ union {
+ DumpRequest dump_request;
+ } packet;
+};
+
+// Comes with a file descriptor via SCM_RIGHTS.
+// This packet should be sent before an actual dump happens.
+struct InterceptRequest {
+ int32_t pid;
+};
+
+// Sent either immediately upon failure, or when the intercept has been used.
+struct InterceptResponse {
+ uint8_t success; // 0 or 1
+ char error_message[127]; // always null-terminated
+};
diff --git a/debuggerd/include/debuggerd/util.h b/debuggerd/include/debuggerd/util.h
new file mode 100644
index 0000000..6051714
--- /dev/null
+++ b/debuggerd/include/debuggerd/util.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+// *** WARNING ***
+// tombstoned's sockets are SOCK_SEQPACKET sockets.
+// Short reads are treated as errors and short writes are assumed to not happen.
+
+// Sends a packet with an attached fd.
+ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
+
+// Receives a packet and optionally, its attached fd.
+// If out_fd is non-null, packets can optionally have an attached fd.
+// If out_fd is null, received packets must not have an attached fd.
+//
+// Errors:
+// EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
+// The first len bytes of the packet are stored in data, but the
+// rest of the packet is dropped.
+// ERANGE: too many file descriptors were attached to the packet.
+// ENOMSG: not enough file descriptors were attached to the packet.
+//
+// plus any errors returned by the underlying recvmsg.
+ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
+ android::base::unique_fd* _Nullable out_fd);
+
+bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write);
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
similarity index 100%
rename from debuggerd/arm/machine.cpp
rename to debuggerd/libdebuggerd/arm/machine.cpp
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
similarity index 100%
rename from debuggerd/arm64/machine.cpp
rename to debuggerd/libdebuggerd/arm64/machine.cpp
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
new file mode 100644
index 0000000..0664442
--- /dev/null
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DEBUG"
+
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+#include "backtrace.h"
+
+#include "utility.h"
+
+static void dump_process_header(log_t* log, pid_t pid) {
+ char path[PATH_MAX];
+ char procnamebuf[1024];
+ char* procname = NULL;
+ FILE* fp;
+
+ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+ if ((fp = fopen(path, "r"))) {
+ procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
+ fclose(fp);
+ }
+
+ time_t t = time(NULL);
+ struct tm tm;
+ localtime_r(&t, &tm);
+ char timestr[64];
+ strftime(timestr, sizeof(timestr), "%F %T", &tm);
+ _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr);
+
+ if (procname) {
+ _LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", procname);
+ }
+ _LOG(log, logtype::BACKTRACE, "ABI: '%s'\n", ABI_STRING);
+}
+
+static void dump_process_footer(log_t* log, pid_t pid) {
+ _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
+}
+
+static void dump_thread(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid) {
+ char path[PATH_MAX];
+ char threadnamebuf[1024];
+ char* threadname = NULL;
+ FILE* fp;
+
+ snprintf(path, sizeof(path), "/proc/%d/comm", tid);
+ if ((fp = fopen(path, "r"))) {
+ threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
+ fclose(fp);
+ if (threadname) {
+ size_t len = strlen(threadname);
+ if (len && threadname[len - 1] == '\n') {
+ threadname[len - 1] = '\0';
+ }
+ }
+ }
+
+ _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
+ if (backtrace->Unwind(0)) {
+ dump_backtrace_to_log(backtrace.get(), log, " ");
+ } else {
+ ALOGE("Unwind failed: tid = %d: %s", tid,
+ backtrace->GetErrorString(backtrace->GetError()).c_str());
+ }
+}
+
+void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid,
+ const std::set<pid_t>& siblings, std::string* amfd_data) {
+ log_t log;
+ log.tfd = fd;
+ log.amfd_data = amfd_data;
+
+ dump_process_header(&log, pid);
+ dump_thread(&log, map, pid, tid);
+
+ for (pid_t sibling : siblings) {
+ dump_thread(&log, map, pid, sibling);
+ }
+
+ dump_process_footer(&log, pid);
+}
+
+void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) {
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str());
+ }
+}
diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
new file mode 100644
index 0000000..4e798e2
--- /dev/null
+++ b/debuggerd/libdebuggerd/elf_utils.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DEBUG"
+
+#include <elf.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+#include "elf_utils.h"
+
+#define NOTE_ALIGN(size) (((size) + 3) & ~3)
+
+template <typename HdrType, typename PhdrType, typename NhdrType>
+static bool get_build_id(
+ Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) {
+ HdrType hdr;
+
+ memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
+
+ // First read the rest of the header.
+ if (backtrace->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
+ sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
+ return false;
+ }
+
+ for (size_t i = 0; i < hdr.e_phnum; i++) {
+ PhdrType phdr;
+ if (backtrace->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
+ reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
+ return false;
+ }
+ // Looking for the .note.gnu.build-id note.
+ if (phdr.p_type == PT_NOTE) {
+ size_t hdr_size = phdr.p_filesz;
+ uintptr_t addr = base_addr + phdr.p_offset;
+ while (hdr_size >= sizeof(NhdrType)) {
+ NhdrType nhdr;
+ if (backtrace->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
+ return false;
+ }
+ addr += sizeof(nhdr);
+ if (nhdr.n_type == NT_GNU_BUILD_ID) {
+ // Skip the name (which is the owner and should be "GNU").
+ addr += NOTE_ALIGN(nhdr.n_namesz);
+ uint8_t build_id_data[160];
+ if (nhdr.n_descsz > sizeof(build_id_data)) {
+ ALOGE("Possible corrupted note, desc size value is too large: %u",
+ nhdr.n_descsz);
+ return false;
+ }
+ if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
+ return false;
+ }
+
+ build_id->clear();
+ for (size_t bytes = 0; bytes < nhdr.n_descsz; bytes++) {
+ *build_id += android::base::StringPrintf("%02x", build_id_data[bytes]);
+ }
+
+ return true;
+ } else {
+ // Move past the extra note data.
+ hdr_size -= sizeof(nhdr);
+ size_t skip_bytes = NOTE_ALIGN(nhdr.n_namesz) + NOTE_ALIGN(nhdr.n_descsz);
+ addr += skip_bytes;
+ if (hdr_size < skip_bytes) {
+ break;
+ }
+ hdr_size -= skip_bytes;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_id) {
+ // Read and verify the elf magic number first.
+ uint8_t e_ident[EI_NIDENT];
+ if (backtrace->Read(addr, e_ident, SELFMAG) != SELFMAG) {
+ return false;
+ }
+
+ if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
+ return false;
+ }
+
+ // Read the rest of EI_NIDENT.
+ if (backtrace->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
+ return false;
+ }
+
+ if (e_ident[EI_CLASS] == ELFCLASS32) {
+ return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(backtrace, addr, e_ident, build_id);
+ } else if (e_ident[EI_CLASS] == ELFCLASS64) {
+ return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(backtrace, addr, e_ident, build_id);
+ }
+
+ return false;
+}
diff --git a/debuggerd/libdebuggerd/include/backtrace.h b/debuggerd/libdebuggerd/include/backtrace.h
new file mode 100644
index 0000000..acd5eaa
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/backtrace.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_BACKTRACE_H
+#define _DEBUGGERD_BACKTRACE_H
+
+#include <sys/types.h>
+
+#include <set>
+#include <string>
+
+#include "utility.h"
+
+class Backtrace;
+class BacktraceMap;
+
+// Dumps a backtrace using a format similar to what Dalvik uses so that the result
+// can be intermixed in a bug report.
+void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid,
+ const std::set<pid_t>& siblings, std::string* amfd_data);
+
+/* Dumps the backtrace in the backtrace data structure to the log. */
+void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
+
+#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/elf_utils.h b/debuggerd/libdebuggerd/include/elf_utils.h
similarity index 100%
rename from debuggerd/elf_utils.h
rename to debuggerd/libdebuggerd/include/elf_utils.h
diff --git a/debuggerd/machine.h b/debuggerd/libdebuggerd/include/machine.h
similarity index 100%
rename from debuggerd/machine.h
rename to debuggerd/libdebuggerd/include/machine.h
diff --git a/debuggerd/libdebuggerd/include/open_files_list.h b/debuggerd/libdebuggerd/include/open_files_list.h
new file mode 100644
index 0000000..b37228d
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/open_files_list.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_OPEN_FILES_LIST_H
+#define _DEBUGGERD_OPEN_FILES_LIST_H
+
+#include <sys/types.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "utility.h"
+
+typedef std::vector<std::pair<int, std::string>> OpenFilesList;
+
+/* Populates the given list with open files for the given process. */
+void populate_open_files_list(pid_t pid, OpenFilesList* list);
+
+/* Dumps the open files list to the log. */
+void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix);
+
+#endif // _DEBUGGERD_OPEN_FILES_LIST_H
diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h
new file mode 100644
index 0000000..4ff24af
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/tombstone.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TOMBSTONE_H
+#define _DEBUGGERD_TOMBSTONE_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <set>
+#include <string>
+
+#include "open_files_list.h"
+
+class BacktraceMap;
+
+/* Create and open a tombstone file for writing.
+ * Returns a writable file descriptor, or -1 with errno set appropriately.
+ * If out_path is non-null, *out_path is set to the path of the tombstone file.
+ */
+int open_tombstone(std::string* path);
+
+/* Creates a tombstone file and writes the crash dump to it. */
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map,
+ const OpenFilesList& open_files, pid_t pid, pid_t tid,
+ const std::set<pid_t>& siblings, uintptr_t abort_msg_address,
+ std::string* amfd_data);
+
+#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/include/utility.h b/debuggerd/libdebuggerd/include/utility.h
new file mode 100644
index 0000000..bbc4546
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/utility.h
@@ -0,0 +1,86 @@
+/* system/debuggerd/utility.h
+**
+** Copyright 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.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef _DEBUGGERD_UTILITY_H
+#define _DEBUGGERD_UTILITY_H
+
+#include <signal.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+
+// Figure out the abi based on defined macros.
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#else
+#error "Unsupported ABI"
+#endif
+
+
+struct log_t{
+ // Tombstone file descriptor.
+ int tfd;
+ // Data to be sent to the Activity Manager.
+ std::string* amfd_data;
+ // The tid of the thread that crashed.
+ pid_t crashed_tid;
+ // The tid of the thread we are currently working with.
+ pid_t current_tid;
+ // logd daemon crash, can block asking for logcat data, allow suppression.
+ bool should_retrieve_logcat;
+
+ log_t()
+ : tfd(-1), amfd_data(nullptr), crashed_tid(-1), current_tid(-1),
+ should_retrieve_logcat(true) {}
+};
+
+// List of types of logs to simplify the logging decision in _LOG
+enum logtype {
+ HEADER,
+ THREAD,
+ REGISTERS,
+ FP_REGISTERS,
+ BACKTRACE,
+ MAPS,
+ MEMORY,
+ STACK,
+ LOGS,
+ OPEN_FILES
+};
+
+// Log information onto the tombstone.
+void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
+ __attribute__ ((format(printf, 3, 4)));
+
+bool wait_for_signal(pid_t tid, siginfo_t* siginfo);
+
+void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
+
+#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
similarity index 100%
rename from debuggerd/mips/machine.cpp
rename to debuggerd/libdebuggerd/mips/machine.cpp
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
similarity index 100%
rename from debuggerd/mips64/machine.cpp
rename to debuggerd/libdebuggerd/mips64/machine.cpp
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
new file mode 100644
index 0000000..5c7ea70
--- /dev/null
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DEBUG"
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <log/log.h>
+
+#include "open_files_list.h"
+
+#include "utility.h"
+
+void populate_open_files_list(pid_t pid, OpenFilesList* list) {
+ std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd";
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(fd_dir_name.c_str()), closedir);
+ if (dir == nullptr) {
+ ALOGE("failed to open directory %s: %s", fd_dir_name.c_str(), strerror(errno));
+ return;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir.get())) != nullptr) {
+ if (*de->d_name == '.') {
+ continue;
+ }
+
+ int fd = atoi(de->d_name);
+ std::string path = fd_dir_name + "/" + std::string(de->d_name);
+ std::string target;
+ if (android::base::Readlink(path, &target)) {
+ list->emplace_back(fd, target);
+ } else {
+ ALOGE("failed to readlink %s: %s", path.c_str(), strerror(errno));
+ list->emplace_back(fd, "???");
+ }
+ }
+}
+
+void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix) {
+ for (auto& file : files) {
+ _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s\n", prefix, file.first, file.second.c_str());
+ }
+}
+
diff --git a/debuggerd/libdebuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h
new file mode 100644
index 0000000..6104f7e
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/BacktraceMock.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H
+#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ucontext.h>
+
+#include <string>
+#include <vector>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+class BacktraceMapMock : public BacktraceMap {
+ public:
+ BacktraceMapMock() : BacktraceMap(0) {}
+ virtual ~BacktraceMapMock() {}
+
+ void AddMap(backtrace_map_t& map) {
+ maps_.push_back(map);
+ }
+};
+
+
+class BacktraceMock : public Backtrace {
+ public:
+ explicit BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) {
+ if (map_ == nullptr) {
+ abort();
+ }
+ }
+ virtual ~BacktraceMock() {}
+
+ virtual bool Unwind(size_t, ucontext_t*) { return false; }
+ virtual bool ReadWord(uintptr_t, word_t*) { return false;}
+
+ virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; }
+
+ virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+ size_t offset = 0;
+ if (last_read_addr_ > 0) {
+ offset = addr - last_read_addr_;
+ }
+ size_t bytes_available = buffer_.size() - offset;
+
+ if (do_partial_read_) {
+ // Do a partial read.
+ if (bytes > bytes_partial_read_) {
+ bytes = bytes_partial_read_;
+ }
+ bytes_partial_read_ -= bytes;
+ // Only support a single partial read.
+ do_partial_read_ = false;
+ } else if (bytes > bytes_available) {
+ bytes = bytes_available;
+ }
+
+ if (bytes > 0) {
+ memcpy(buffer, buffer_.data() + offset, bytes);
+ }
+
+ last_read_addr_ = addr;
+ return bytes;
+ }
+
+ void SetReadData(uint8_t* buffer, size_t bytes) {
+ buffer_.resize(bytes);
+ memcpy(buffer_.data(), buffer, bytes);
+ bytes_partial_read_ = 0;
+ do_partial_read_ = false;
+ last_read_addr_ = 0;
+ }
+
+ void SetPartialReadAmount(size_t bytes) {
+ if (bytes > buffer_.size()) {
+ abort();
+ }
+ bytes_partial_read_ = bytes;
+ do_partial_read_ = true;
+ }
+
+ private:
+ std::vector<uint8_t> buffer_;
+ size_t bytes_partial_read_ = 0;
+ uintptr_t last_read_addr_ = 0;
+ bool do_partial_read_ = false;
+};
+
+#endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
new file mode 100644
index 0000000..49f3690
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -0,0 +1,740 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+#include <android-base/file.h>
+
+#include "BacktraceMock.h"
+#include "log_fake.h"
+#include "utility.h"
+
+const char g_expected_full_dump[] =
+"\nmemory near r1:\n"
+#if defined(__LP64__)
+" 0000000012345658 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 0000000012345668 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 0000000012345678 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 0000000012345688 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 0000000012345698 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 00000000123456a8 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 00000000123456b8 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 00000000123456c8 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 00000000123456d8 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 00000000123456e8 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 00000000123456f8 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 0000000012345708 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 0000000012345718 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000012345728 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000012345738 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 0000000012345748 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#else
+" 12345658 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" 12345668 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" 12345678 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" 12345688 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" 12345698 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" 123456a8 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" 123456b8 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" 123456c8 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" 123456d8 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" 123456e8 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" 123456f8 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" 12345708 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" 12345718 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 12345728 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 12345738 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" 12345748 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+#endif
+
+const char g_expected_partial_dump[] = \
+"\nmemory near pc:\n"
+#if defined(__LP64__)
+" 00000000123455e0 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 00000000123455f0 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 0000000012345600 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 0000000012345610 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 0000000012345620 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 0000000012345640 6766656463626160 ---------------- `abcdefg........\n"
+" 0000000012345650 ---------------- ---------------- ................\n"
+" 0000000012345660 ---------------- ---------------- ................\n"
+" 0000000012345670 ---------------- ---------------- ................\n"
+" 0000000012345680 ---------------- ---------------- ................\n"
+" 0000000012345690 ---------------- ---------------- ................\n"
+" 00000000123456a0 ---------------- ---------------- ................\n"
+" 00000000123456b0 ---------------- ---------------- ................\n"
+" 00000000123456c0 ---------------- ---------------- ................\n"
+" 00000000123456d0 ---------------- ---------------- ................\n";
+#else
+" 123455e0 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" 123455f0 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" 12345600 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" 12345610 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" 12345620 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" 12345640 63626160 67666564 -------- -------- `abcdefg........\n"
+" 12345650 -------- -------- -------- -------- ................\n"
+" 12345660 -------- -------- -------- -------- ................\n"
+" 12345670 -------- -------- -------- -------- ................\n"
+" 12345680 -------- -------- -------- -------- ................\n"
+" 12345690 -------- -------- -------- -------- ................\n"
+" 123456a0 -------- -------- -------- -------- ................\n"
+" 123456b0 -------- -------- -------- -------- ................\n"
+" 123456c0 -------- -------- -------- -------- ................\n"
+" 123456d0 -------- -------- -------- -------- ................\n";
+#endif
+
+class DumpMemoryTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ map_mock_.reset(new BacktraceMapMock());
+ backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
+
+ char tmp_file[256];
+ const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
+ memcpy(tmp_file, data_template, sizeof(data_template));
+ int tombstone_fd = mkstemp(tmp_file);
+ if (tombstone_fd == -1) {
+ const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
+ memcpy(tmp_file, tmp_template, sizeof(tmp_template));
+ tombstone_fd = mkstemp(tmp_file);
+ if (tombstone_fd == -1) {
+ abort();
+ }
+ }
+ if (unlink(tmp_file) == -1) {
+ abort();
+ }
+
+ log_.tfd = tombstone_fd;
+ log_.amfd_data = nullptr;
+ log_.crashed_tid = 12;
+ log_.current_tid = 12;
+ log_.should_retrieve_logcat = false;
+
+ resetLogs();
+ }
+
+ virtual void TearDown() {
+ if (log_.tfd >= 0) {
+ close(log_.tfd);
+ }
+ }
+
+ std::unique_ptr<BacktraceMapMock> map_mock_;
+ std::unique_ptr<BacktraceMock> backtrace_mock_;
+
+ log_t log_;
+};
+
+TEST_F(DumpMemoryTest, aligned_addr) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, partial_read) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(96);
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, unaligned_addr) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_unreadable) {
+ dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near pc:\n"
+#if defined(__LP64__)
+" 00000000a2345658 ---------------- ---------------- ................\n"
+" 00000000a2345668 ---------------- ---------------- ................\n"
+" 00000000a2345678 ---------------- ---------------- ................\n"
+" 00000000a2345688 ---------------- ---------------- ................\n"
+" 00000000a2345698 ---------------- ---------------- ................\n"
+" 00000000a23456a8 ---------------- ---------------- ................\n"
+" 00000000a23456b8 ---------------- ---------------- ................\n"
+" 00000000a23456c8 ---------------- ---------------- ................\n"
+" 00000000a23456d8 ---------------- ---------------- ................\n"
+" 00000000a23456e8 ---------------- ---------------- ................\n"
+" 00000000a23456f8 ---------------- ---------------- ................\n"
+" 00000000a2345708 ---------------- ---------------- ................\n"
+" 00000000a2345718 ---------------- ---------------- ................\n"
+" 00000000a2345728 ---------------- ---------------- ................\n"
+" 00000000a2345738 ---------------- ---------------- ................\n"
+" 00000000a2345748 ---------------- ---------------- ................\n";
+#else
+" a2345658 -------- -------- -------- -------- ................\n"
+" a2345668 -------- -------- -------- -------- ................\n"
+" a2345678 -------- -------- -------- -------- ................\n"
+" a2345688 -------- -------- -------- -------- ................\n"
+" a2345698 -------- -------- -------- -------- ................\n"
+" a23456a8 -------- -------- -------- -------- ................\n"
+" a23456b8 -------- -------- -------- -------- ................\n"
+" a23456c8 -------- -------- -------- -------- ................\n"
+" a23456d8 -------- -------- -------- -------- ................\n"
+" a23456e8 -------- -------- -------- -------- ................\n"
+" a23456f8 -------- -------- -------- -------- ................\n"
+" a2345708 -------- -------- -------- -------- ................\n"
+" a2345718 -------- -------- -------- -------- ................\n"
+" a2345728 -------- -------- -------- -------- ................\n"
+" a2345738 -------- -------- -------- -------- ................\n"
+" a2345748 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_partially_unreadable) {
+ uint8_t buffer[104];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) {
+ uint8_t buffer[104];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(102);
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+
+#if defined(__LP64__)
+ ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
+#else
+ ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
+#endif
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) {
+ uint8_t buffer[106];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(45);
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+
+#if defined(__LP64__)
+ ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 8\n"
+ "6 DEBUG Bytes after second read 106, is not a multiple of 8\n",
+ getFakeLogPrint().c_str());
+#else
+ ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 4\n"
+ "6 DEBUG Bytes after second read 106, is not a multiple of 4\n",
+ getFakeLogPrint().c_str());
+#endif
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+}
+
+TEST_F(DumpMemoryTest, address_low_fence) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r1:\n"
+#if defined(__LP64__)
+" 0000000000001000 0000000000000000 0000000000000000 ................\n"
+" 0000000000001010 0000000000000000 0000000000000000 ................\n"
+" 0000000000001020 0000000000000000 0000000000000000 ................\n"
+" 0000000000001030 0000000000000000 0000000000000000 ................\n"
+" 0000000000001040 0000000000000000 0000000000000000 ................\n"
+" 0000000000001050 0000000000000000 0000000000000000 ................\n"
+" 0000000000001060 0000000000000000 0000000000000000 ................\n"
+" 0000000000001070 0000000000000000 0000000000000000 ................\n"
+" 0000000000001080 0000000000000000 0000000000000000 ................\n"
+" 0000000000001090 0000000000000000 0000000000000000 ................\n"
+" 00000000000010a0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010b0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010c0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010d0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010e0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010f0 0000000000000000 0000000000000000 ................\n";
+#else
+" 00001000 00000000 00000000 00000000 00000000 ................\n"
+" 00001010 00000000 00000000 00000000 00000000 ................\n"
+" 00001020 00000000 00000000 00000000 00000000 ................\n"
+" 00001030 00000000 00000000 00000000 00000000 ................\n"
+" 00001040 00000000 00000000 00000000 00000000 ................\n"
+" 00001050 00000000 00000000 00000000 00000000 ................\n"
+" 00001060 00000000 00000000 00000000 00000000 ................\n"
+" 00001070 00000000 00000000 00000000 00000000 ................\n"
+" 00001080 00000000 00000000 00000000 00000000 ................\n"
+" 00001090 00000000 00000000 00000000 00000000 ................\n"
+" 000010a0 00000000 00000000 00000000 00000000 ................\n"
+" 000010b0 00000000 00000000 00000000 00000000 ................\n"
+" 000010c0 00000000 00000000 00000000 00000000 ................\n"
+" 000010d0 00000000 00000000 00000000 00000000 ................\n"
+" 000010e0 00000000 00000000 00000000 00000000 ................\n"
+" 000010f0 00000000 00000000 00000000 00000000 ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_too_low) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_too_high) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+ dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
+ dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
+ dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
+#else
+ dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
+ dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
+ dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
+#endif
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_would_overflow) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+ dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
+#else
+ dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
+#endif
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_nearly_too_high) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+ dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
+#else
+ dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
+#endif
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 3fffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 3fffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 3fffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 3fffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 3fffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 3fffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 3fffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 3fffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 3fffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 3fffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 3fffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 3fffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 3fffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 3fffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 3fffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 3ffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#else
+" fffeff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" fffeff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" fffeff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" fffeff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" fffeff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" fffeff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" fffeff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" fffeff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" fffeff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" fffeff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" fffeffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" fffeffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" fffeffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" fffeffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" fffeffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" fffefff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ uintptr_t addr = 0x10000020 + page_size - 120;
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f88 ---------------- ---------------- ................\n"
+" 0000000010000f98 ---------------- ---------------- ................\n"
+" 0000000010000fa8 ---------------- ---------------- ................\n"
+" 0000000010000fb8 ---------------- ---------------- ................\n"
+" 0000000010000fc8 ---------------- ---------------- ................\n"
+" 0000000010000fd8 ---------------- ---------------- ................\n"
+" 0000000010000fe8 ---------------- ---------------- ................\n"
+" 0000000010000ff8 ---------------- 7f7e7d7c7b7a7978 ........xyz{|}~.\n"
+" 0000000010001008 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 0000000010001018 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#else
+" 10000f88 -------- -------- -------- -------- ................\n"
+" 10000f98 -------- -------- -------- -------- ................\n"
+" 10000fa8 -------- -------- -------- -------- ................\n"
+" 10000fb8 -------- -------- -------- -------- ................\n"
+" 10000fc8 -------- -------- -------- -------- ................\n"
+" 10000fd8 -------- -------- -------- -------- ................\n"
+" 10000fe8 -------- -------- -------- -------- ................\n"
+" 10000ff8 -------- -------- 7b7a7978 7f7e7d7c ........xyz{|}~.\n"
+" 10001008 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" 10001018 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" 10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" 10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" 10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" 10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) {
+ uint8_t buffer[224];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ uintptr_t addr = 0x10000020 + page_size - 192;
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f40 ---------------- ---------------- ................\n"
+" 0000000010000f50 ---------------- ---------------- ................\n"
+" 0000000010000f60 ---------------- ---------------- ................\n"
+" 0000000010000f70 ---------------- ---------------- ................\n"
+" 0000000010000f80 ---------------- ---------------- ................\n"
+" 0000000010000f90 ---------------- ---------------- ................\n"
+" 0000000010000fa0 ---------------- ---------------- ................\n"
+" 0000000010000fb0 ---------------- ---------------- ................\n"
+" 0000000010000fc0 ---------------- ---------------- ................\n"
+" 0000000010000fd0 ---------------- ---------------- ................\n"
+" 0000000010000fe0 ---------------- ---------------- ................\n"
+" 0000000010000ff0 ---------------- ---------------- ................\n"
+" 0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000010001020 ---------------- ---------------- ................\n"
+" 0000000010001030 ---------------- ---------------- ................\n";
+#else
+" 10000f40 -------- -------- -------- -------- ................\n"
+" 10000f50 -------- -------- -------- -------- ................\n"
+" 10000f60 -------- -------- -------- -------- ................\n"
+" 10000f70 -------- -------- -------- -------- ................\n"
+" 10000f80 -------- -------- -------- -------- ................\n"
+" 10000f90 -------- -------- -------- -------- ................\n"
+" 10000fa0 -------- -------- -------- -------- ................\n"
+" 10000fb0 -------- -------- -------- -------- ................\n"
+" 10000fc0 -------- -------- -------- -------- ................\n"
+" 10000fd0 -------- -------- -------- -------- ................\n"
+" 10000fe0 -------- -------- -------- -------- ................\n"
+" 10000ff0 -------- -------- -------- -------- ................\n"
+" 10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 10001020 -------- -------- -------- -------- ................\n"
+" 10001030 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ uintptr_t addr = 0x10000020;
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000000 ---------------- ---------------- ................\n"
+" 0000000010000010 ---------------- ---------------- ................\n"
+" 0000000010000020 ---------------- ---------------- ................\n"
+" 0000000010000030 ---------------- ---------------- ................\n"
+" 0000000010000040 ---------------- ---------------- ................\n"
+" 0000000010000050 ---------------- ---------------- ................\n"
+" 0000000010000060 ---------------- ---------------- ................\n"
+" 0000000010000070 ---------------- ---------------- ................\n"
+" 0000000010000080 ---------------- ---------------- ................\n"
+" 0000000010000090 ---------------- ---------------- ................\n"
+" 00000000100000a0 ---------------- ---------------- ................\n"
+" 00000000100000b0 ---------------- ---------------- ................\n"
+" 00000000100000c0 ---------------- ---------------- ................\n"
+" 00000000100000d0 ---------------- ---------------- ................\n"
+" 00000000100000e0 ---------------- ---------------- ................\n"
+" 00000000100000f0 ---------------- ---------------- ................\n";
+#else
+" 10000000 -------- -------- -------- -------- ................\n"
+" 10000010 -------- -------- -------- -------- ................\n"
+" 10000020 -------- -------- -------- -------- ................\n"
+" 10000030 -------- -------- -------- -------- ................\n"
+" 10000040 -------- -------- -------- -------- ................\n"
+" 10000050 -------- -------- -------- -------- ................\n"
+" 10000060 -------- -------- -------- -------- ................\n"
+" 10000070 -------- -------- -------- -------- ................\n"
+" 10000080 -------- -------- -------- -------- ................\n"
+" 10000090 -------- -------- -------- -------- ................\n"
+" 100000a0 -------- -------- -------- -------- ................\n"
+" 100000b0 -------- -------- -------- -------- ................\n"
+" 100000c0 -------- -------- -------- -------- ................\n"
+" 100000d0 -------- -------- -------- -------- ................\n"
+" 100000e0 -------- -------- -------- -------- ................\n"
+" 100000f0 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ uintptr_t addr = 0x10000020 + page_size - 256;
+
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f00 ---------------- ---------------- ................\n"
+" 0000000010000f10 ---------------- ---------------- ................\n"
+" 0000000010000f20 ---------------- ---------------- ................\n"
+" 0000000010000f30 ---------------- ---------------- ................\n"
+" 0000000010000f40 ---------------- ---------------- ................\n"
+" 0000000010000f50 ---------------- ---------------- ................\n"
+" 0000000010000f60 ---------------- ---------------- ................\n"
+" 0000000010000f70 ---------------- ---------------- ................\n"
+" 0000000010000f80 ---------------- ---------------- ................\n"
+" 0000000010000f90 ---------------- ---------------- ................\n"
+" 0000000010000fa0 ---------------- ---------------- ................\n"
+" 0000000010000fb0 ---------------- ---------------- ................\n"
+" 0000000010000fc0 ---------------- ---------------- ................\n"
+" 0000000010000fd0 ---------------- ---------------- ................\n"
+" 0000000010000fe0 ---------------- ---------------- ................\n"
+" 0000000010000ff0 ---------------- ---------------- ................\n";
+#else
+" 10000f00 -------- -------- -------- -------- ................\n"
+" 10000f10 -------- -------- -------- -------- ................\n"
+" 10000f20 -------- -------- -------- -------- ................\n"
+" 10000f30 -------- -------- -------- -------- ................\n"
+" 10000f40 -------- -------- -------- -------- ................\n"
+" 10000f50 -------- -------- -------- -------- ................\n"
+" 10000f60 -------- -------- -------- -------- ................\n"
+" 10000f70 -------- -------- -------- -------- ................\n"
+" 10000f80 -------- -------- -------- -------- ................\n"
+" 10000f90 -------- -------- -------- -------- ................\n"
+" 10000fa0 -------- -------- -------- -------- ................\n"
+" 10000fb0 -------- -------- -------- -------- ................\n"
+" 10000fc0 -------- -------- -------- -------- ................\n"
+" 10000fd0 -------- -------- -------- -------- ................\n"
+" 10000fe0 -------- -------- -------- -------- ................\n"
+" 10000ff0 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
similarity index 100%
rename from debuggerd/test/elf_fake.cpp
rename to debuggerd/libdebuggerd/test/elf_fake.cpp
diff --git a/debuggerd/test/elf_fake.h b/debuggerd/libdebuggerd/test/elf_fake.h
similarity index 100%
rename from debuggerd/test/elf_fake.h
rename to debuggerd/libdebuggerd/test/elf_fake.h
diff --git a/debuggerd/libdebuggerd/test/host_signal_fixup.h b/debuggerd/libdebuggerd/test/host_signal_fixup.h
new file mode 100644
index 0000000..762bae5
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/host_signal_fixup.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
+#define _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
+
+#include <signal.h>
+
+#if !defined(__BIONIC__)
+
+// In order to compile parts of debuggerd for the host, we need to
+// define these values.
+
+#if !defined(NSIGILL)
+#define NSIGILL ILL_BADSTK
+#endif
+
+#if !defined(BUS_MCEERR_AR)
+#define BUS_MCEERR_AR 4
+#endif
+#if !defined(BUS_MCEERR_AO)
+#define BUS_MCEERR_AO 5
+#endif
+#if !defined(NSIGBUS)
+#define NSIGBUS BUS_MCEERR_AO
+#endif
+
+#if !defined(NSIGFPE)
+#define NSIGFPE FPE_FLTSUB
+#endif
+
+#if !defined(NSIGSEGV)
+#define NSIGSEGV SEGV_ACCERR
+#endif
+
+#if !defined(TRAP_BRANCH)
+#define TRAP_BRANCH 3
+#endif
+#if !defined(TRAP_HWBKPT)
+#define TRAP_HWBKPT 4
+#endif
+#if !defined(NSIGTRAP)
+#define NSIGTRAP TRAP_HWBKPT
+#endif
+
+#if !defined(SI_DETHREAD)
+#define SI_DETHREAD (-7)
+#endif
+
+#endif
+
+#endif // _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
diff --git a/debuggerd/libdebuggerd/test/log_fake.cpp b/debuggerd/libdebuggerd/test/log_fake.cpp
new file mode 100644
index 0000000..3336bcb
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/log_fake.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+
+// Forward declarations.
+class Backtrace;
+struct EventTagMap;
+struct AndroidLogEntry;
+
+std::string g_fake_log_buf;
+
+std::string g_fake_log_print;
+
+void resetLogs() {
+ g_fake_log_buf = "";
+ g_fake_log_print = "";
+}
+
+std::string getFakeLogBuf() {
+ return g_fake_log_buf;
+}
+
+std::string getFakeLogPrint() {
+ return g_fake_log_print;
+}
+
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+ g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
+ g_fake_log_buf += tag;
+ g_fake_log_buf += ' ';
+ g_fake_log_buf += msg;
+ return 1;
+}
+
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+ g_fake_log_print += std::to_string(prio) + ' ';
+ g_fake_log_print += tag;
+ g_fake_log_print += ' ';
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&g_fake_log_print, fmt, ap);
+ va_end(ap);
+
+ g_fake_log_print += '\n';
+
+ return 1;
+}
+
+extern "C" log_id_t android_name_to_log_id(const char*) {
+ return LOG_ID_SYSTEM;
+}
+
+extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+ errno = EACCES;
+ return nullptr;
+}
+
+extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
+ return 0;
+}
+
+extern "C" EventTagMap* android_openEventTagMap(const char*) {
+ return nullptr;
+}
+
+extern "C" int android_log_processBinaryLogBuffer(
+ struct logger_entry*,
+ AndroidLogEntry*, const EventTagMap*, char*, int) {
+ return 0;
+}
+
+extern "C" void android_logger_list_free(struct logger_list*) {
+}
diff --git a/debuggerd/test/log_fake.h b/debuggerd/libdebuggerd/test/log_fake.h
similarity index 100%
rename from debuggerd/test/log_fake.h
rename to debuggerd/libdebuggerd/test/log_fake.h
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
new file mode 100644
index 0000000..85e0695
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "android-base/test_utils.h"
+
+#include "open_files_list.h"
+
+// Check that we can produce a list of open files for the current process, and
+// that it includes a known open file.
+TEST(OpenFilesListTest, BasicTest) {
+ // Open a temporary file that we can check for in the list of open files.
+ TemporaryFile tf;
+
+ // Get the list of open files for this process.
+ OpenFilesList list;
+ populate_open_files_list(getpid(), &list);
+
+ // Verify our open file is in the list.
+ bool found = false;
+ for (auto& file : list) {
+ if (file.first == tf.fd) {
+ EXPECT_EQ(file.second, std::string(tf.path));
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+}
diff --git a/debuggerd/test/property_fake.cpp b/debuggerd/libdebuggerd/test/property_fake.cpp
similarity index 100%
rename from debuggerd/test/property_fake.cpp
rename to debuggerd/libdebuggerd/test/property_fake.cpp
diff --git a/debuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
similarity index 100%
rename from debuggerd/test/ptrace_fake.cpp
rename to debuggerd/libdebuggerd/test/ptrace_fake.cpp
diff --git a/debuggerd/test/ptrace_fake.h b/debuggerd/libdebuggerd/test/ptrace_fake.h
similarity index 100%
rename from debuggerd/test/ptrace_fake.h
rename to debuggerd/libdebuggerd/test/ptrace_fake.h
diff --git a/debuggerd/test/sys/system_properties.h b/debuggerd/libdebuggerd/test/sys/system_properties.h
similarity index 100%
rename from debuggerd/test/sys/system_properties.h
rename to debuggerd/libdebuggerd/test/sys/system_properties.h
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
new file mode 100644
index 0000000..5ee07cd
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+#include <android-base/file.h>
+
+#include "utility.h"
+
+#include "BacktraceMock.h"
+#include "elf_fake.h"
+#include "host_signal_fixup.h"
+#include "log_fake.h"
+#include "ptrace_fake.h"
+
+// In order to test this code, we need to include the tombstone.cpp code.
+// Including it, also allows us to override the ptrace function.
+#define ptrace ptrace_fake
+
+#include "tombstone.cpp"
+
+void dump_registers(log_t*, pid_t) {
+}
+
+void dump_memory_and_code(log_t*, Backtrace*) {
+}
+
+void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
+}
+
+class TombstoneTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ map_mock_.reset(new BacktraceMapMock());
+ backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
+
+ char tmp_file[256];
+ const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
+ memcpy(tmp_file, data_template, sizeof(data_template));
+ int tombstone_fd = mkstemp(tmp_file);
+ if (tombstone_fd == -1) {
+ const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
+ memcpy(tmp_file, tmp_template, sizeof(tmp_template));
+ tombstone_fd = mkstemp(tmp_file);
+ if (tombstone_fd == -1) {
+ abort();
+ }
+ }
+ if (unlink(tmp_file) == -1) {
+ abort();
+ }
+
+ log_.tfd = tombstone_fd;
+ amfd_data_.clear();
+ log_.amfd_data = &amfd_data_;
+ log_.crashed_tid = 12;
+ log_.current_tid = 12;
+ log_.should_retrieve_logcat = false;
+
+ resetLogs();
+ elf_set_fake_build_id("");
+ siginfo_t si;
+ si.si_signo = SIGABRT;
+ ptrace_set_fake_getsiginfo(si);
+ }
+
+ virtual void TearDown() {
+ if (log_.tfd >= 0) {
+ close(log_.tfd);
+ }
+ }
+
+ std::unique_ptr<BacktraceMapMock> map_mock_;
+ std::unique_ptr<BacktraceMock> backtrace_mock_;
+
+ log_t log_;
+ std::string amfd_data_;
+};
+
+TEST_F(TombstoneTest, single_map) {
+ backtrace_map_t map;
+#if defined(__LP64__)
+ map.start = 0x123456789abcd000UL;
+ map.end = 0x123456789abdf000UL;
+#else
+ map.start = 0x1234000;
+ map.end = 0x1235000;
+#endif
+ map_mock_->AddMap(map);
+
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 12345678'9abcd000-12345678'9abdefff --- 0 12000\n";
+#else
+" 01234000-01234fff --- 0 1000\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, single_map_elf_build_id) {
+ backtrace_map_t map;
+#if defined(__LP64__)
+ map.start = 0x123456789abcd000UL;
+ map.end = 0x123456789abdf000UL;
+#else
+ map.start = 0x1234000;
+ map.end = 0x1235000;
+#endif
+ map.flags = PROT_READ;
+ map.name = "/system/lib/libfake.so";
+ map_mock_->AddMap(map);
+
+ elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 12345678'9abcd000-12345678'9abdefff r-- 0 12000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
+#else
+" 01234000-01234fff r-- 0 1000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+// Even though build id is present, it should not be printed in either of
+// these cases.
+TEST_F(TombstoneTest, single_map_no_build_id) {
+ backtrace_map_t map;
+#if defined(__LP64__)
+ map.start = 0x123456789abcd000UL;
+ map.end = 0x123456789abdf000UL;
+#else
+ map.start = 0x1234000;
+ map.end = 0x1235000;
+#endif
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.name = "/system/lib/libfake.so";
+ map_mock_->AddMap(map);
+
+ elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 12345678'9abcd000-12345678'9abdefff -w- 0 12000\n"
+" 12345678'9abcd000-12345678'9abdefff -w- 0 12000 /system/lib/libfake.so\n";
+#else
+" 01234000-01234fff -w- 0 1000\n"
+" 01234000-01234fff -w- 0 1000 /system/lib/libfake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps) {
+ backtrace_map_t map;
+
+ map.start = 0xa234000;
+ map.end = 0xa235000;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa334000;
+ map.end = 0xa335000;
+ map.offset = 0xf000;
+ map.flags = PROT_READ;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 00000000'0a234000-00000000'0a234fff --- 0 1000\n"
+" 00000000'0a334000-00000000'0a334fff r-- f000 1000\n"
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#else
+" 0a234000-0a234fff --- 0 1000\n"
+" 0a334000-0a334fff r-- f000 1000\n"
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = SIGBUS;
+ si.si_addr = reinterpret_cast<void*>(0x1000);
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#else
+"--->Fault address falls at 00001000 before any mapped regions\n"
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = SIGBUS;
+ si.si_addr = reinterpret_cast<void*>(0xa533000);
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+"--->Fault address falls at 00000000'0a533000 between mapped regions\n"
+" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#else
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+"--->Fault address falls at 0a533000 between mapped regions\n"
+" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = SIGBUS;
+ si.si_addr = reinterpret_cast<void*>(0xa534040);
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+"--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#else
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+"--->0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = SIGBUS;
+#if defined(__LP64__)
+ si.si_addr = reinterpret_cast<void*>(0x12345a534040UL);
+#else
+ si.si_addr = reinterpret_cast<void*>(0xf534040UL);
+#endif
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"
+"--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
+#else
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"
+"--->Fault address falls at 0f534040 after any mapped regions\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = 0;
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n";
+#else
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ for (int i = 1; i < 255; i++) {
+ ASSERT_TRUE(ftruncate(log_.tfd, 0) == 0);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+
+ siginfo_t si;
+ si.si_signo = i;
+ si.si_addr = reinterpret_cast<void*>(0x1000);
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ bool has_addr = false;
+ switch (si.si_signo) {
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+ case SIGTRAP:
+ has_addr = true;
+ break;
+ }
+
+ const char* expected_addr_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
+#else
+"--->Fault address falls at 00001000 before any mapped regions\n"
+" 0a434000-0a434fff -w- 0 1000\n";
+#endif
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
+#else
+" 0a434000-0a434fff -w- 0 1000\n";
+#endif
+ if (has_addr) {
+ ASSERT_STREQ(expected_addr_dump, tombstone_contents.c_str())
+ << "Signal " << si.si_signo << " expected to include an address.";
+ } else {
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str())
+ << "Signal " << si.si_signo << " is not expected to include an address.";
+ }
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+ }
+}
+
+TEST_F(TombstoneTest, dump_signal_info_error) {
+ siginfo_t si;
+ si.si_signo = 0;
+ ptrace_set_fake_getsiginfo(si);
+
+ dump_signal_info(&log_, 123);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+}
+
+TEST_F(TombstoneTest, dump_log_file_error) {
+ log_.should_retrieve_logcat = true;
+ dump_log_file(&log_, 123, "/fake/filename", 10);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
+ getFakeLogPrint().c_str());
+
+ ASSERT_STREQ("", amfd_data_.c_str());
+}
+
+TEST_F(TombstoneTest, dump_header_info) {
+ dump_header_info(&log_);
+
+ std::string expected = "Build fingerprint: 'unknown'\nRevision: 'unknown'\n";
+ expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING);
+ ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
new file mode 100644
index 0000000..ac2c0b6
--- /dev/null
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -0,0 +1,750 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DEBUG"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include <memory>
+#include <string>
+
+#include <android/log.h>
+#include <android-base/stringprintf.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <log/logprint.h>
+#include <private/android_filesystem_config.h>
+
+#include "debuggerd/handler.h"
+
+#include "backtrace.h"
+#include "elf_utils.h"
+#include "machine.h"
+#include "open_files_list.h"
+#include "tombstone.h"
+
+using android::base::StringPrintf;
+
+#define STACK_WORDS 16
+
+#define MAX_TOMBSTONES 10
+#define TOMBSTONE_DIR "/data/tombstones"
+#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d")
+
+static bool signal_has_si_addr(int si_signo, int si_code) {
+ // Manually sent signals won't have si_addr.
+ if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) {
+ return false;
+ }
+
+ switch (si_signo) {
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+ case SIGTRAP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char* get_signame(int sig) {
+ switch (sig) {
+ case SIGABRT: return "SIGABRT";
+ case SIGBUS: return "SIGBUS";
+ case SIGFPE: return "SIGFPE";
+ case SIGILL: return "SIGILL";
+ case SIGSEGV: return "SIGSEGV";
+#if defined(SIGSTKFLT)
+ case SIGSTKFLT: return "SIGSTKFLT";
+#endif
+ case SIGSTOP: return "SIGSTOP";
+ case SIGSYS: return "SIGSYS";
+ case SIGTRAP: return "SIGTRAP";
+ case DEBUGGER_SIGNAL: return "<debuggerd signal>";
+ default: return "?";
+ }
+}
+
+static const char* get_sigcode(int signo, int code) {
+ // Try the signal-specific codes...
+ switch (signo) {
+ case SIGILL:
+ switch (code) {
+ case ILL_ILLOPC: return "ILL_ILLOPC";
+ case ILL_ILLOPN: return "ILL_ILLOPN";
+ case ILL_ILLADR: return "ILL_ILLADR";
+ case ILL_ILLTRP: return "ILL_ILLTRP";
+ case ILL_PRVOPC: return "ILL_PRVOPC";
+ case ILL_PRVREG: return "ILL_PRVREG";
+ case ILL_COPROC: return "ILL_COPROC";
+ case ILL_BADSTK: return "ILL_BADSTK";
+ }
+ static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
+ break;
+ case SIGBUS:
+ switch (code) {
+ case BUS_ADRALN: return "BUS_ADRALN";
+ case BUS_ADRERR: return "BUS_ADRERR";
+ case BUS_OBJERR: return "BUS_OBJERR";
+ case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
+ case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
+ }
+ static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
+ break;
+ case SIGFPE:
+ switch (code) {
+ case FPE_INTDIV: return "FPE_INTDIV";
+ case FPE_INTOVF: return "FPE_INTOVF";
+ case FPE_FLTDIV: return "FPE_FLTDIV";
+ case FPE_FLTOVF: return "FPE_FLTOVF";
+ case FPE_FLTUND: return "FPE_FLTUND";
+ case FPE_FLTRES: return "FPE_FLTRES";
+ case FPE_FLTINV: return "FPE_FLTINV";
+ case FPE_FLTSUB: return "FPE_FLTSUB";
+ }
+ static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
+ break;
+ case SIGSEGV:
+ switch (code) {
+ case SEGV_MAPERR: return "SEGV_MAPERR";
+ case SEGV_ACCERR: return "SEGV_ACCERR";
+#if defined(SEGV_BNDERR)
+ case SEGV_BNDERR: return "SEGV_BNDERR";
+#endif
+#if defined(SEGV_PKUERR)
+ case SEGV_PKUERR: return "SEGV_PKUERR";
+#endif
+ }
+#if defined(SEGV_PKUERR)
+ static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code");
+#elif defined(SEGV_BNDERR)
+ static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
+#else
+ static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
+#endif
+ break;
+#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
+ case SIGSYS:
+ switch (code) {
+ case SYS_SECCOMP: return "SYS_SECCOMP";
+ }
+ static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
+ break;
+#endif
+ case SIGTRAP:
+ switch (code) {
+ case TRAP_BRKPT: return "TRAP_BRKPT";
+ case TRAP_TRACE: return "TRAP_TRACE";
+ case TRAP_BRANCH: return "TRAP_BRANCH";
+ case TRAP_HWBKPT: return "TRAP_HWBKPT";
+ }
+ static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+ break;
+ }
+ // Then the other codes...
+ switch (code) {
+ case SI_USER: return "SI_USER";
+ case SI_KERNEL: return "SI_KERNEL";
+ case SI_QUEUE: return "SI_QUEUE";
+ case SI_TIMER: return "SI_TIMER";
+ case SI_MESGQ: return "SI_MESGQ";
+ case SI_ASYNCIO: return "SI_ASYNCIO";
+ case SI_SIGIO: return "SI_SIGIO";
+ case SI_TKILL: return "SI_TKILL";
+ case SI_DETHREAD: return "SI_DETHREAD";
+ }
+ // Then give up...
+ return "?";
+}
+
+static void dump_header_info(log_t* log) {
+ char fingerprint[PROPERTY_VALUE_MAX];
+ char revision[PROPERTY_VALUE_MAX];
+
+ property_get("ro.build.fingerprint", fingerprint, "unknown");
+ property_get("ro.revision", revision, "unknown");
+
+ _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint);
+ _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision);
+ _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
+}
+
+static void dump_probable_cause(log_t* log, const siginfo_t& si) {
+ std::string cause;
+ if (si.si_signo == SIGSEGV && si.si_code == SEGV_MAPERR) {
+ if (si.si_addr < reinterpret_cast<void*>(4096)) {
+ cause = StringPrintf("null pointer dereference");
+ } else if (si.si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
+ cause = "call to kuser_helper_version";
+ } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
+ cause = "call to kuser_get_tls";
+ } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
+ cause = "call to kuser_cmpxchg";
+ } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
+ cause = "call to kuser_memory_barrier";
+ } else if (si.si_addr == reinterpret_cast<void*>(0xffff0f60)) {
+ cause = "call to kuser_cmpxchg64";
+ }
+ } else if (si.si_signo == SIGSYS && si.si_code == SYS_SECCOMP) {
+ cause = StringPrintf("seccomp prevented call to disallowed system call %d", si.si_syscall);
+ }
+
+ if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
+}
+
+static void dump_signal_info(log_t* log, pid_t tid) {
+ siginfo_t si;
+ memset(&si, 0, sizeof(si));
+ if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
+ ALOGE("cannot get siginfo: %s\n", strerror(errno));
+ return;
+ }
+
+ char addr_desc[32]; // ", fault addr 0x1234"
+ if (signal_has_si_addr(si.si_signo, si.si_code)) {
+ snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
+ } else {
+ snprintf(addr_desc, sizeof(addr_desc), "--------");
+ }
+
+ _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si.si_signo,
+ get_signame(si.si_signo), si.si_code, get_sigcode(si.si_signo, si.si_code), addr_desc);
+
+ dump_probable_cause(log, si);
+}
+
+static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) {
+ char path[64];
+ char threadnamebuf[1024];
+ char* threadname = nullptr;
+ FILE *fp;
+
+ snprintf(path, sizeof(path), "/proc/%d/comm", tid);
+ if ((fp = fopen(path, "r"))) {
+ threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
+ fclose(fp);
+ if (threadname) {
+ size_t len = strlen(threadname);
+ if (len && threadname[len - 1] == '\n') {
+ threadname[len - 1] = '\0';
+ }
+ }
+ }
+ // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
+ static const char logd[] = "logd";
+ if (threadname != nullptr && !strncmp(threadname, logd, sizeof(logd) - 1)
+ && (!threadname[sizeof(logd) - 1] || (threadname[sizeof(logd) - 1] == '.'))) {
+ log->should_retrieve_logcat = false;
+ }
+
+ char procnamebuf[1024];
+ char* procname = nullptr;
+
+ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+ if ((fp = fopen(path, "r"))) {
+ procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
+ fclose(fp);
+ }
+
+ _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid,
+ threadname ? threadname : "UNKNOWN", procname ? procname : "UNKNOWN");
+}
+
+static void dump_stack_segment(
+ Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) {
+ // Read the data all at once.
+ word_t stack_data[words];
+ size_t bytes_read = backtrace->Read(*sp, reinterpret_cast<uint8_t*>(&stack_data[0]), sizeof(word_t) * words);
+ words = bytes_read / sizeof(word_t);
+ std::string line;
+ for (size_t i = 0; i < words; i++) {
+ line = " ";
+ if (i == 0 && label >= 0) {
+ // Print the label once.
+ line += StringPrintf("#%02d ", label);
+ } else {
+ line += " ";
+ }
+ line += StringPrintf("%" PRIPTR " %" PRIPTR, *sp, stack_data[i]);
+
+ backtrace_map_t map;
+ backtrace->FillInMap(stack_data[i], &map);
+ if (BacktraceMap::IsValid(map) && !map.name.empty()) {
+ line += " " + map.name;
+ uintptr_t offset = 0;
+ std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset));
+ if (!func_name.empty()) {
+ line += " (" + func_name;
+ if (offset) {
+ line += StringPrintf("+%" PRIuPTR, offset);
+ }
+ line += ')';
+ }
+ }
+ _LOG(log, logtype::STACK, "%s\n", line.c_str());
+
+ *sp += sizeof(word_t);
+ }
+}
+
+static void dump_stack(Backtrace* backtrace, log_t* log) {
+ size_t first = 0, last;
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
+ if (frame->sp) {
+ if (!first) {
+ first = i+1;
+ }
+ last = i;
+ }
+ }
+ if (!first) {
+ return;
+ }
+ first--;
+
+ // Dump a few words before the first frame.
+ word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t);
+ dump_stack_segment(backtrace, log, &sp, STACK_WORDS, -1);
+
+ // Dump a few words from all successive frames.
+ // Only log the first 3 frames, put the rest in the tombstone.
+ for (size_t i = first; i <= last; i++) {
+ const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
+ if (sp != frame->sp) {
+ _LOG(log, logtype::STACK, " ........ ........\n");
+ sp = frame->sp;
+ }
+ if (i == last) {
+ dump_stack_segment(backtrace, log, &sp, STACK_WORDS, i);
+ if (sp < frame->sp + frame->stack_size) {
+ _LOG(log, logtype::STACK, " ........ ........\n");
+ }
+ } else {
+ size_t words = frame->stack_size / sizeof(word_t);
+ if (words == 0) {
+ words = 1;
+ } else if (words > STACK_WORDS) {
+ words = STACK_WORDS;
+ }
+ dump_stack_segment(backtrace, log, &sp, words, i);
+ }
+ }
+}
+
+static std::string get_addr_string(uintptr_t addr) {
+ std::string addr_str;
+#if defined(__LP64__)
+ addr_str = StringPrintf("%08x'%08x",
+ static_cast<uint32_t>(addr >> 32),
+ static_cast<uint32_t>(addr & 0xffffffff));
+#else
+ addr_str = StringPrintf("%08x", addr);
+#endif
+ return addr_str;
+}
+
+static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
+ if (address == 0) {
+ return;
+ }
+
+ address += sizeof(size_t); // Skip the buffer length.
+
+ char msg[512];
+ memset(msg, 0, sizeof(msg));
+ char* p = &msg[0];
+ while (p < &msg[sizeof(msg)]) {
+ word_t data;
+ size_t len = sizeof(word_t);
+ if (!backtrace->ReadWord(address, &data)) {
+ break;
+ }
+ address += sizeof(word_t);
+
+ while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0) {
+ len--;
+ }
+ }
+ msg[sizeof(msg) - 1] = '\0';
+
+ _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
+}
+
+static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
+ bool print_fault_address_marker = false;
+ uintptr_t addr = 0;
+ siginfo_t si;
+ memset(&si, 0, sizeof(si));
+ if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) {
+ print_fault_address_marker = signal_has_si_addr(si.si_signo, si.si_code);
+ addr = reinterpret_cast<uintptr_t>(si.si_addr);
+ } else {
+ ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
+ }
+
+ ScopedBacktraceMapIteratorLock lock(map);
+ _LOG(log, logtype::MAPS, "\n");
+ if (!print_fault_address_marker) {
+ _LOG(log, logtype::MAPS, "memory map:\n");
+ } else {
+ _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n");
+ if (map->begin() != map->end() && addr < map->begin()->start) {
+ _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n",
+ get_addr_string(addr).c_str());
+ print_fault_address_marker = false;
+ }
+ }
+
+ std::string line;
+ for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
+ line = " ";
+ if (print_fault_address_marker) {
+ if (addr < it->start) {
+ _LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
+ get_addr_string(addr).c_str());
+ print_fault_address_marker = false;
+ } else if (addr >= it->start && addr < it->end) {
+ line = "--->";
+ print_fault_address_marker = false;
+ }
+ }
+ line += get_addr_string(it->start) + '-' + get_addr_string(it->end - 1) + ' ';
+ if (it->flags & PROT_READ) {
+ line += 'r';
+ } else {
+ line += '-';
+ }
+ if (it->flags & PROT_WRITE) {
+ line += 'w';
+ } else {
+ line += '-';
+ }
+ if (it->flags & PROT_EXEC) {
+ line += 'x';
+ } else {
+ line += '-';
+ }
+ line += StringPrintf(" %8" PRIxPTR " %8" PRIxPTR, it->offset, it->end - it->start);
+ bool space_needed = true;
+ if (it->name.length() > 0) {
+ space_needed = false;
+ line += " " + it->name;
+ std::string build_id;
+ if ((it->flags & PROT_READ) && elf_get_build_id(backtrace, it->start, &build_id)) {
+ line += " (BuildId: " + build_id + ")";
+ }
+ }
+ if (it->load_base != 0) {
+ if (space_needed) {
+ line += ' ';
+ }
+ line += StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
+ }
+ _LOG(log, logtype::MAPS, "%s\n", line.c_str());
+ }
+ if (print_fault_address_marker) {
+ _LOG(log, logtype::MAPS, "--->Fault address falls at %s after any mapped regions\n",
+ get_addr_string(addr).c_str());
+ }
+}
+
+static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
+ if (backtrace->NumFrames()) {
+ _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
+ dump_backtrace_to_log(backtrace, log, " ");
+
+ _LOG(log, logtype::STACK, "\nstack:\n");
+ dump_stack(backtrace, log);
+ }
+}
+
+static void dump_thread(log_t* log, pid_t pid, pid_t tid, BacktraceMap* map,
+ uintptr_t abort_msg_address, bool primary_thread) {
+ log->current_tid = tid;
+ if (!primary_thread) {
+ _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
+ }
+ dump_thread_info(log, pid, tid);
+ dump_signal_info(log, tid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
+ if (primary_thread) {
+ dump_abort_message(backtrace.get(), log, abort_msg_address);
+ }
+ dump_registers(log, tid);
+ if (backtrace->Unwind(0)) {
+ dump_backtrace_and_stack(backtrace.get(), log);
+ } else {
+ ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+ }
+
+ if (primary_thread) {
+ dump_memory_and_code(log, backtrace.get());
+ if (map) {
+ dump_all_maps(backtrace.get(), map, log, tid);
+ }
+ }
+
+ log->current_tid = log->crashed_tid;
+}
+
+// Reads the contents of the specified log device, filters out the entries
+// that don't match the specified pid, and writes them to the tombstone file.
+//
+// If "tail" is non-zero, log the last "tail" number of lines.
+static EventTagMap* g_eventTagMap = NULL;
+
+static void dump_log_file(
+ log_t* log, pid_t pid, const char* filename, unsigned int tail) {
+ bool first = true;
+ struct logger_list* logger_list;
+
+ if (!log->should_retrieve_logcat) {
+ return;
+ }
+
+ logger_list = android_logger_list_open(
+ android_name_to_log_id(filename), ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tail, pid);
+
+ if (!logger_list) {
+ ALOGE("Unable to open %s: %s\n", filename, strerror(errno));
+ return;
+ }
+
+ struct log_msg log_entry;
+
+ while (true) {
+ ssize_t actual = android_logger_list_read(logger_list, &log_entry);
+ struct logger_entry* entry;
+
+ if (actual < 0) {
+ if (actual == -EINTR) {
+ // interrupted by signal, retry
+ continue;
+ } else if (actual == -EAGAIN) {
+ // non-blocking EOF; we're done
+ break;
+ } else {
+ ALOGE("Error while reading log: %s\n", strerror(-actual));
+ break;
+ }
+ } else if (actual == 0) {
+ ALOGE("Got zero bytes while reading log: %s\n", strerror(errno));
+ break;
+ }
+
+ // NOTE: if you ALOGV something here, this will spin forever,
+ // because you will be writing as fast as you're reading. Any
+ // high-frequency debug diagnostics should just be written to
+ // the tombstone file.
+
+ entry = &log_entry.entry_v1;
+
+ if (first) {
+ _LOG(log, logtype::LOGS, "--------- %slog %s\n",
+ tail ? "tail end of " : "", filename);
+ first = false;
+ }
+
+ // Msg format is: <priority:1><tag:N>\0<message:N>\0
+ //
+ // We want to display it in the same format as "logcat -v threadtime"
+ // (although in this case the pid is redundant).
+ static const char* kPrioChars = "!.VDIWEFS";
+ unsigned hdr_size = log_entry.entry.hdr_size;
+ if (!hdr_size) {
+ hdr_size = sizeof(log_entry.entry_v1);
+ }
+ if ((hdr_size < sizeof(log_entry.entry_v1)) ||
+ (hdr_size > sizeof(log_entry.entry))) {
+ continue;
+ }
+ char* msg = reinterpret_cast<char*>(log_entry.buf) + hdr_size;
+
+ char timeBuf[32];
+ time_t sec = static_cast<time_t>(entry->sec);
+ struct tm tmBuf;
+ struct tm* ptm;
+ ptm = localtime_r(&sec, &tmBuf);
+ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+ if (log_entry.id() == LOG_ID_EVENTS) {
+ if (!g_eventTagMap) {
+ g_eventTagMap = android_openEventTagMap(NULL);
+ }
+ AndroidLogEntry e;
+ char buf[512];
+ android_log_processBinaryLogBuffer(entry, &e, g_eventTagMap, buf, sizeof(buf));
+ _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n",
+ timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
+ 'I', (int)e.tagLen, e.tag, e.message);
+ continue;
+ }
+
+ unsigned char prio = msg[0];
+ char* tag = msg + 1;
+ msg = tag + strlen(tag) + 1;
+
+ // consume any trailing newlines
+ char* nl = msg + strlen(msg) - 1;
+ while (nl >= msg && *nl == '\n') {
+ *nl-- = '\0';
+ }
+
+ char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
+
+ // Look for line breaks ('\n') and display each text line
+ // on a separate line, prefixed with the header, like logcat does.
+ do {
+ nl = strchr(msg, '\n');
+ if (nl) {
+ *nl = '\0';
+ ++nl;
+ }
+
+ _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
+ timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
+ prioChar, tag, msg);
+ } while ((msg = nl));
+ }
+
+ android_logger_list_free(logger_list);
+}
+
+// Dumps the logs generated by the specified pid to the tombstone, from both
+// "system" and "main" log devices. Ideally we'd interleave the output.
+static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
+ dump_log_file(log, pid, "system", tail);
+ dump_log_file(log, pid, "main", tail);
+}
+
+// Dumps all information about the specified pid to the tombstone.
+static void dump_crash(log_t* log, BacktraceMap* map,
+ const OpenFilesList& open_files, pid_t pid, pid_t tid,
+ const std::set<pid_t>& siblings, uintptr_t abort_msg_address) {
+ // don't copy log messages to tombstone unless this is a dev device
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.debuggable", value, "0");
+ bool want_logs = (value[0] == '1');
+
+ _LOG(log, logtype::HEADER,
+ "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+ dump_header_info(log);
+ dump_thread(log, pid, tid, map, abort_msg_address, true);
+ if (want_logs) {
+ dump_logs(log, pid, 5);
+ }
+
+ if (!siblings.empty()) {
+ for (pid_t sibling : siblings) {
+ dump_thread(log, pid, sibling, map, 0, false);
+ }
+ }
+
+ _LOG(log, logtype::OPEN_FILES, "\nopen files:\n");
+ dump_open_files_list_to_log(open_files, log, " ");
+
+ if (want_logs) {
+ dump_logs(log, pid, 0);
+ }
+}
+
+// open_tombstone - find an available tombstone slot, if any, of the
+// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
+// file is available, we reuse the least-recently-modified file.
+int open_tombstone(std::string* out_path) {
+ // In a single pass, find an available slot and, in case none
+ // exist, find and record the least-recently-modified file.
+ char path[128];
+ int fd = -1;
+ int oldest = -1;
+ struct stat oldest_sb;
+ for (int i = 0; i < MAX_TOMBSTONES; i++) {
+ snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
+
+ struct stat sb;
+ if (stat(path, &sb) == 0) {
+ if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
+ oldest = i;
+ oldest_sb.st_mtime = sb.st_mtime;
+ }
+ continue;
+ }
+ if (errno != ENOENT) continue;
+
+ fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
+ if (fd < 0) continue; // raced ?
+
+ if (out_path) {
+ *out_path = path;
+ }
+ fchown(fd, AID_SYSTEM, AID_SYSTEM);
+ return fd;
+ }
+
+ if (oldest < 0) {
+ ALOGE("debuggerd: failed to find a valid tombstone, default to using tombstone 0.\n");
+ oldest = 0;
+ }
+
+ // we didn't find an available file, so we clobber the oldest one
+ snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
+ fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
+ if (fd < 0) {
+ ALOGE("debuggerd: failed to open tombstone file '%s': %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ if (out_path) {
+ *out_path = path;
+ }
+ fchown(fd, AID_SYSTEM, AID_SYSTEM);
+ return fd;
+}
+
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map,
+ const OpenFilesList& open_files, pid_t pid, pid_t tid,
+ const std::set<pid_t>& siblings, uintptr_t abort_msg_address,
+ std::string* amfd_data) {
+ log_t log;
+ log.current_tid = tid;
+ log.crashed_tid = tid;
+
+ if (tombstone_fd < 0) {
+ ALOGE("debuggerd: skipping tombstone write, nothing to do.\n");
+ return;
+ }
+
+ log.tfd = tombstone_fd;
+ log.amfd_data = amfd_data;
+ dump_crash(&log, map, open_files, pid, tid, siblings, abort_msg_address);
+}
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
new file mode 100644
index 0000000..57209aa
--- /dev/null
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DEBUG"
+
+#include "utility.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+// Whitelist output desired in the logcat output.
+bool is_allowed_in_logcat(enum logtype ltype) {
+ if ((ltype == HEADER)
+ || (ltype == REGISTERS)
+ || (ltype == BACKTRACE)) {
+ return true;
+ }
+ return false;
+}
+
+void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
+ bool write_to_tombstone = (log->tfd != -1);
+ bool write_to_logcat = is_allowed_in_logcat(ltype)
+ && log->crashed_tid != -1
+ && log->current_tid != -1
+ && (log->crashed_tid == log->current_tid);
+
+ char buf[512];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ size_t len = strlen(buf);
+ if (len <= 0) {
+ return;
+ }
+
+ if (write_to_tombstone) {
+ TEMP_FAILURE_RETRY(write(log->tfd, buf, len));
+ }
+
+ if (write_to_logcat) {
+ __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, buf);
+ if (log->amfd_data != nullptr) {
+ *log->amfd_data += buf;
+ }
+ }
+}
+
+bool wait_for_signal(pid_t tid, siginfo_t* siginfo) {
+ while (true) {
+ int status;
+ pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL));
+ if (n == -1) {
+ ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
+ return false;
+ } else if (n == tid) {
+ if (WIFSTOPPED(status)) {
+ if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, siginfo) != 0) {
+ ALOGE("PTRACE_GETSIGINFO failed: %s", strerror(errno));
+ return false;
+ }
+ return true;
+ } else {
+ ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
+ // This is the only circumstance under which we can allow a detach
+ // to fail with ESRCH, which indicates the tid has exited.
+ return false;
+ }
+ }
+ }
+}
+
+#define MEMORY_BYTES_TO_DUMP 256
+#define MEMORY_BYTES_PER_LINE 16
+
+void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) {
+ std::string log_msg;
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&log_msg, fmt, ap);
+ va_end(ap);
+
+ // Align the address to sizeof(long) and start 32 bytes before the address.
+ addr &= ~(sizeof(long) - 1);
+ if (addr >= 4128) {
+ addr -= 32;
+ }
+
+ // Don't bother if the address looks too low, or looks too high.
+ if (addr < 4096 ||
+#if defined(__LP64__)
+ addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) {
+#else
+ addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) {
+#endif
+ return;
+ }
+
+ _LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str());
+
+ // Dump 256 bytes
+ uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
+ memset(data, 0, MEMORY_BYTES_TO_DUMP);
+ size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
+ if (bytes % sizeof(uintptr_t) != 0) {
+ // This should never happen, but just in case.
+ ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
+ bytes &= ~(sizeof(uintptr_t) - 1);
+ }
+
+ uintptr_t start = 0;
+ bool skip_2nd_read = false;
+ if (bytes == 0) {
+ // In this case, we might want to try another read at the beginning of
+ // the next page only if it's within the amount of memory we would have
+ // read.
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ start = ((addr + (page_size - 1)) & ~(page_size - 1)) - addr;
+ if (start == 0 || start >= MEMORY_BYTES_TO_DUMP) {
+ skip_2nd_read = true;
+ }
+ }
+
+ if (bytes < MEMORY_BYTES_TO_DUMP && !skip_2nd_read) {
+ // Try to do one more read. This could happen if a read crosses a map,
+ // but the maps do not have any break between them. Or it could happen
+ // if reading from an unreadable map, but the read would cross back
+ // into a readable map. Only requires one extra read because a map has
+ // to contain at least one page, and the total number of bytes to dump
+ // is smaller than a page.
+ size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+ sizeof(data) - bytes - start);
+ bytes += bytes2;
+ if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
+ // This should never happen, but we'll try and continue any way.
+ ALOGE("Bytes after second read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
+ bytes &= ~(sizeof(uintptr_t) - 1);
+ }
+ }
+
+ // Dump the code around memory as:
+ // addr contents ascii
+ // 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q
+ // 0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000 ......-..p......
+ // On 32-bit machines, there are still 16 bytes per line but addresses and
+ // words are of course presented differently.
+ uintptr_t* data_ptr = data;
+ size_t current = 0;
+ size_t total_bytes = start + bytes;
+ for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
+ std::string logline;
+ android::base::StringAppendF(&logline, " %" PRIPTR, addr);
+
+ addr += MEMORY_BYTES_PER_LINE;
+ std::string ascii;
+ for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
+ if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
+ android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
+
+ // Fill out the ascii string from the data.
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr);
+ for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) {
+ if (*ptr >= 0x20 && *ptr < 0x7f) {
+ ascii += *ptr;
+ } else {
+ ascii += '.';
+ }
+ }
+ data_ptr++;
+ } else {
+ logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
+ ascii += std::string(sizeof(uintptr_t), '.');
+ }
+ current += sizeof(uintptr_t);
+ }
+ _LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str());
+ }
+}
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
similarity index 100%
rename from debuggerd/x86/machine.cpp
rename to debuggerd/libdebuggerd/x86/machine.cpp
diff --git a/debuggerd/libdebuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
new file mode 100644
index 0000000..bf2c2b4
--- /dev/null
+++ b/debuggerd/libdebuggerd/x86_64/machine.cpp
@@ -0,0 +1,67 @@
+/*
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "DEBUG"
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+#include "machine.h"
+#include "utility.h"
+
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+ struct user_regs_struct r;
+ if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:");
+
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:");
+}
+
+void dump_registers(log_t* log, pid_t tid) {
+ struct user_regs_struct r;
+ if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n",
+ r.rax, r.rbx, r.rcx, r.rdx);
+ _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n",
+ r.rsi, r.rdi);
+ _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n",
+ r.r8, r.r9, r.r10, r.r11);
+ _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n",
+ r.r12, r.r13, r.r14, r.r15);
+ _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n",
+ r.cs, r.ss);
+ _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n",
+ r.rip, r.rbp, r.rsp, r.eflags);
+}
diff --git a/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp
new file mode 100644
index 0000000..42a8e77
--- /dev/null
+++ b/debuggerd/signal_sender.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "debuggerd-signal"
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include "signal_sender.h"
+
+static int signal_fd = -1;
+static pid_t signal_pid;
+struct signal_message {
+ pid_t pid;
+ pid_t tid;
+ int signal;
+};
+
+static void set_signal_sender_process_name() {
+#if defined(__LP64__)
+ static constexpr char long_process_name[] = "debuggerd64:signaller";
+ static constexpr char short_process_name[] = "debuggerd64:sig";
+ static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd64"), "");
+#else
+ static constexpr char long_process_name[] = "debuggerd:signaller";
+ static constexpr char short_process_name[] = "debuggerd:sig";
+ static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd"), "");
+#endif
+
+ // pthread_setname_np has a maximum length of 16 chars, including null terminator.
+ static_assert(sizeof(short_process_name) <= 16, "");
+ pthread_setname_np(pthread_self(), short_process_name);
+
+ char* progname = const_cast<char*>(getprogname());
+ if (strlen(progname) <= strlen(long_process_name)) {
+ ALOGE("debuggerd: unexpected progname %s", progname);
+ return;
+ }
+
+ memset(progname, 0, strlen(progname));
+ strcpy(progname, long_process_name);
+}
+
+// Fork a process to send signals for the worker processes to use after they've dropped privileges.
+bool start_signal_sender() {
+ if (signal_pid != 0) {
+ ALOGE("debuggerd: attempted to start signal sender multiple times");
+ return false;
+ }
+
+ int sfd[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sfd) != 0) {
+ ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno));
+ return false;
+ }
+
+ pid_t parent = getpid();
+ pid_t fork_pid = fork();
+ if (fork_pid == -1) {
+ ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
+ return false;
+ } else if (fork_pid == 0) {
+ close(sfd[1]);
+
+ set_signal_sender_process_name();
+
+ while (true) {
+ signal_message msg;
+ int rc = TEMP_FAILURE_RETRY(read(sfd[0], &msg, sizeof(msg)));
+ if (rc < 0) {
+ ALOGE("debuggerd: signal sender failed to read from socket");
+ break;
+ } else if (rc != sizeof(msg)) {
+ ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc);
+ break;
+ }
+
+ // Report success after sending a signal
+ int err = 0;
+ if (msg.tid > 0) {
+ if (syscall(SYS_tgkill, msg.pid, msg.tid, msg.signal) != 0) {
+ err = errno;
+ }
+ } else {
+ if (kill(msg.pid, msg.signal) != 0) {
+ err = errno;
+ }
+ }
+
+ if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) {
+ ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno));
+ }
+ }
+
+ // Our parent proably died, but if not, kill them.
+ if (getppid() == parent) {
+ kill(parent, SIGKILL);
+ }
+ _exit(1);
+ } else {
+ close(sfd[0]);
+ signal_fd = sfd[1];
+ signal_pid = fork_pid;
+ return true;
+ }
+}
+
+bool stop_signal_sender() {
+ if (signal_pid <= 0) {
+ return false;
+ }
+
+ if (kill(signal_pid, SIGKILL) != 0) {
+ ALOGE("debuggerd: failed to kill signal sender: %s", strerror(errno));
+ return false;
+ }
+
+ close(signal_fd);
+ signal_fd = -1;
+
+ int status;
+ waitpid(signal_pid, &status, 0);
+ signal_pid = 0;
+
+ return true;
+}
+
+bool send_signal(pid_t pid, pid_t tid, int signal) {
+ if (signal_fd == -1) {
+ ALOGE("debuggerd: attempted to send signal before signal sender was started");
+ errno = EHOSTUNREACH;
+ return false;
+ }
+
+ signal_message msg = {.pid = pid, .tid = tid, .signal = signal };
+ if (TEMP_FAILURE_RETRY(write(signal_fd, &msg, sizeof(msg))) < 0) {
+ ALOGE("debuggerd: failed to send message to signal sender: %s", strerror(errno));
+ errno = EHOSTUNREACH;
+ return false;
+ }
+
+ int response;
+ ssize_t rc = TEMP_FAILURE_RETRY(read(signal_fd, &response, sizeof(response)));
+ if (rc == 0) {
+ ALOGE("debuggerd: received EOF from signal sender");
+ errno = EHOSTUNREACH;
+ return false;
+ } else if (rc < 0) {
+ ALOGE("debuggerd: failed to receive response from signal sender: %s", strerror(errno));
+ errno = EHOSTUNREACH;
+ return false;
+ }
+
+ if (response == 0) {
+ return true;
+ }
+
+ errno = response;
+ return false;
+}
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/test/BacktraceMock.h
deleted file mode 100644
index f75534e..0000000
--- a/debuggerd/test/BacktraceMock.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H
-#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ucontext.h>
-
-#include <string>
-#include <vector>
-
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-class BacktraceMapMock : public BacktraceMap {
- public:
- BacktraceMapMock() : BacktraceMap(0) {}
- virtual ~BacktraceMapMock() {}
-
- void AddMap(backtrace_map_t& map) {
- maps_.push_back(map);
- }
-};
-
-
-class BacktraceMock : public Backtrace {
- public:
- BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) {
- if (map_ == nullptr) {
- abort();
- }
- }
- virtual ~BacktraceMock() {}
-
- virtual bool Unwind(size_t, ucontext_t*) { return false; }
- virtual bool ReadWord(uintptr_t, word_t*) { return false;}
-
- virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; }
-
- virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
- size_t offset = 0;
- if (last_read_addr_ > 0) {
- offset = addr - last_read_addr_;
- }
- size_t bytes_available = buffer_.size() - offset;
-
- if (do_partial_read_) {
- // Do a partial read.
- if (bytes > bytes_partial_read_) {
- bytes = bytes_partial_read_;
- }
- bytes_partial_read_ -= bytes;
- // Only support a single partial read.
- do_partial_read_ = false;
- } else if (bytes > bytes_available) {
- bytes = bytes_available;
- }
-
- if (bytes > 0) {
- memcpy(buffer, buffer_.data() + offset, bytes);
- }
-
- last_read_addr_ = addr;
- return bytes;
- }
-
- void SetReadData(uint8_t* buffer, size_t bytes) {
- buffer_.resize(bytes);
- memcpy(buffer_.data(), buffer, bytes);
- bytes_partial_read_ = 0;
- do_partial_read_ = false;
- last_read_addr_ = 0;
- }
-
- void SetPartialReadAmount(size_t bytes) {
- if (bytes > buffer_.size()) {
- abort();
- }
- bytes_partial_read_ = bytes;
- do_partial_read_ = true;
- }
-
- private:
- std::vector<uint8_t> buffer_;
- size_t bytes_partial_read_ = 0;
- uintptr_t last_read_addr_ = 0;
- bool do_partial_read_ = false;
-};
-
-#endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp
deleted file mode 100644
index 75e7028..0000000
--- a/debuggerd/test/dump_memory_test.cpp
+++ /dev/null
@@ -1,740 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-
-#include <memory>
-#include <string>
-
-#include <gtest/gtest.h>
-#include <base/file.h>
-
-#include "BacktraceMock.h"
-#include "log_fake.h"
-#include "utility.h"
-
-const char g_expected_full_dump[] =
-"\nmemory near r1:\n"
-#if defined(__LP64__)
-" 0000000012345658 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" 0000000012345668 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" 0000000012345678 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" 0000000012345688 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" 0000000012345698 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 00000000123456a8 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" 00000000123456b8 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
-" 00000000123456c8 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
-" 00000000123456d8 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 00000000123456e8 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 00000000123456f8 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 0000000012345708 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 0000000012345718 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 0000000012345728 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 0000000012345738 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 0000000012345748 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
-#else
-" 12345658 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
-" 12345668 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
-" 12345678 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
-" 12345688 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
-" 12345698 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" 123456a8 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
-" 123456b8 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
-" 123456c8 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
-" 123456d8 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" 123456e8 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" 123456f8 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" 12345708 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" 12345718 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" 12345728 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" 12345738 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" 12345748 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
-#endif
-
-const char g_expected_partial_dump[] = \
-"\nmemory near pc:\n"
-#if defined(__LP64__)
-" 00000000123455e0 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" 00000000123455f0 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" 0000000012345600 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" 0000000012345610 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" 0000000012345620 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" 0000000012345640 6766656463626160 ---------------- `abcdefg........\n"
-" 0000000012345650 ---------------- ---------------- ................\n"
-" 0000000012345660 ---------------- ---------------- ................\n"
-" 0000000012345670 ---------------- ---------------- ................\n"
-" 0000000012345680 ---------------- ---------------- ................\n"
-" 0000000012345690 ---------------- ---------------- ................\n"
-" 00000000123456a0 ---------------- ---------------- ................\n"
-" 00000000123456b0 ---------------- ---------------- ................\n"
-" 00000000123456c0 ---------------- ---------------- ................\n"
-" 00000000123456d0 ---------------- ---------------- ................\n";
-#else
-" 123455e0 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
-" 123455f0 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
-" 12345600 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
-" 12345610 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
-" 12345620 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
-" 12345640 63626160 67666564 -------- -------- `abcdefg........\n"
-" 12345650 -------- -------- -------- -------- ................\n"
-" 12345660 -------- -------- -------- -------- ................\n"
-" 12345670 -------- -------- -------- -------- ................\n"
-" 12345680 -------- -------- -------- -------- ................\n"
-" 12345690 -------- -------- -------- -------- ................\n"
-" 123456a0 -------- -------- -------- -------- ................\n"
-" 123456b0 -------- -------- -------- -------- ................\n"
-" 123456c0 -------- -------- -------- -------- ................\n"
-" 123456d0 -------- -------- -------- -------- ................\n";
-#endif
-
-class DumpMemoryTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- map_mock_.reset(new BacktraceMapMock());
- backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
-
- char tmp_file[256];
- const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
- memcpy(tmp_file, data_template, sizeof(data_template));
- int tombstone_fd = mkstemp(tmp_file);
- if (tombstone_fd == -1) {
- const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
- memcpy(tmp_file, tmp_template, sizeof(tmp_template));
- tombstone_fd = mkstemp(tmp_file);
- if (tombstone_fd == -1) {
- abort();
- }
- }
- if (unlink(tmp_file) == -1) {
- abort();
- }
-
- log_.tfd = tombstone_fd;
- log_.amfd = -1;
- log_.crashed_tid = 12;
- log_.current_tid = 12;
- log_.should_retrieve_logcat = false;
-
- resetLogs();
- }
-
- virtual void TearDown() {
- if (log_.tfd >= 0) {
- close(log_.tfd);
- }
- }
-
- std::unique_ptr<BacktraceMapMock> map_mock_;
- std::unique_ptr<BacktraceMock> backtrace_mock_;
-
- log_t log_;
-};
-
-TEST_F(DumpMemoryTest, aligned_addr) {
- uint8_t buffer[256];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-
- dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, partial_read) {
- uint8_t buffer[256];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(96);
-
- dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, unaligned_addr) {
- uint8_t buffer[256];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-
- dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_unreadable) {
- dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near pc:\n"
-#if defined(__LP64__)
-" 00000000a2345658 ---------------- ---------------- ................\n"
-" 00000000a2345668 ---------------- ---------------- ................\n"
-" 00000000a2345678 ---------------- ---------------- ................\n"
-" 00000000a2345688 ---------------- ---------------- ................\n"
-" 00000000a2345698 ---------------- ---------------- ................\n"
-" 00000000a23456a8 ---------------- ---------------- ................\n"
-" 00000000a23456b8 ---------------- ---------------- ................\n"
-" 00000000a23456c8 ---------------- ---------------- ................\n"
-" 00000000a23456d8 ---------------- ---------------- ................\n"
-" 00000000a23456e8 ---------------- ---------------- ................\n"
-" 00000000a23456f8 ---------------- ---------------- ................\n"
-" 00000000a2345708 ---------------- ---------------- ................\n"
-" 00000000a2345718 ---------------- ---------------- ................\n"
-" 00000000a2345728 ---------------- ---------------- ................\n"
-" 00000000a2345738 ---------------- ---------------- ................\n"
-" 00000000a2345748 ---------------- ---------------- ................\n";
-#else
-" a2345658 -------- -------- -------- -------- ................\n"
-" a2345668 -------- -------- -------- -------- ................\n"
-" a2345678 -------- -------- -------- -------- ................\n"
-" a2345688 -------- -------- -------- -------- ................\n"
-" a2345698 -------- -------- -------- -------- ................\n"
-" a23456a8 -------- -------- -------- -------- ................\n"
-" a23456b8 -------- -------- -------- -------- ................\n"
-" a23456c8 -------- -------- -------- -------- ................\n"
-" a23456d8 -------- -------- -------- -------- ................\n"
-" a23456e8 -------- -------- -------- -------- ................\n"
-" a23456f8 -------- -------- -------- -------- ................\n"
-" a2345708 -------- -------- -------- -------- ................\n"
-" a2345718 -------- -------- -------- -------- ................\n"
-" a2345728 -------- -------- -------- -------- ................\n"
-" a2345738 -------- -------- -------- -------- ................\n"
-" a2345748 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_partially_unreadable) {
- uint8_t buffer[104];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-
- dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) {
- uint8_t buffer[104];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(102);
-
- dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
-
-#if defined(__LP64__)
- ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
-#else
- ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
-#endif
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) {
- uint8_t buffer[106];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(45);
-
- dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
-
-#if defined(__LP64__)
- ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 8\n"
- "6 DEBUG Bytes after second read 106, is not a multiple of 8\n",
- getFakeLogPrint().c_str());
-#else
- ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 4\n"
- "6 DEBUG Bytes after second read 106, is not a multiple of 4\n",
- getFakeLogPrint().c_str());
-#endif
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
-}
-
-TEST_F(DumpMemoryTest, address_low_fence) {
- uint8_t buffer[256];
- memset(buffer, 0, sizeof(buffer));
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-
- dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r1:\n"
-#if defined(__LP64__)
-" 0000000000001000 0000000000000000 0000000000000000 ................\n"
-" 0000000000001010 0000000000000000 0000000000000000 ................\n"
-" 0000000000001020 0000000000000000 0000000000000000 ................\n"
-" 0000000000001030 0000000000000000 0000000000000000 ................\n"
-" 0000000000001040 0000000000000000 0000000000000000 ................\n"
-" 0000000000001050 0000000000000000 0000000000000000 ................\n"
-" 0000000000001060 0000000000000000 0000000000000000 ................\n"
-" 0000000000001070 0000000000000000 0000000000000000 ................\n"
-" 0000000000001080 0000000000000000 0000000000000000 ................\n"
-" 0000000000001090 0000000000000000 0000000000000000 ................\n"
-" 00000000000010a0 0000000000000000 0000000000000000 ................\n"
-" 00000000000010b0 0000000000000000 0000000000000000 ................\n"
-" 00000000000010c0 0000000000000000 0000000000000000 ................\n"
-" 00000000000010d0 0000000000000000 0000000000000000 ................\n"
-" 00000000000010e0 0000000000000000 0000000000000000 ................\n"
-" 00000000000010f0 0000000000000000 0000000000000000 ................\n";
-#else
-" 00001000 00000000 00000000 00000000 00000000 ................\n"
-" 00001010 00000000 00000000 00000000 00000000 ................\n"
-" 00001020 00000000 00000000 00000000 00000000 ................\n"
-" 00001030 00000000 00000000 00000000 00000000 ................\n"
-" 00001040 00000000 00000000 00000000 00000000 ................\n"
-" 00001050 00000000 00000000 00000000 00000000 ................\n"
-" 00001060 00000000 00000000 00000000 00000000 ................\n"
-" 00001070 00000000 00000000 00000000 00000000 ................\n"
-" 00001080 00000000 00000000 00000000 00000000 ................\n"
-" 00001090 00000000 00000000 00000000 00000000 ................\n"
-" 000010a0 00000000 00000000 00000000 00000000 ................\n"
-" 000010b0 00000000 00000000 00000000 00000000 ................\n"
-" 000010c0 00000000 00000000 00000000 00000000 ................\n"
-" 000010d0 00000000 00000000 00000000 00000000 ................\n"
-" 000010e0 00000000 00000000 00000000 00000000 ................\n"
-" 000010f0 00000000 00000000 00000000 00000000 ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_address_too_low) {
- uint8_t buffer[256];
- memset(buffer, 0, sizeof(buffer));
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-
- dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_address_too_high) {
- uint8_t buffer[256];
- memset(buffer, 0, sizeof(buffer));
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-
-#if defined(__LP64__)
- dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
- dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
- dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
-#else
- dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
- dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
- dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
-#endif
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_address_would_overflow) {
- uint8_t buffer[256];
- memset(buffer, 0, sizeof(buffer));
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-
-#if defined(__LP64__)
- dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
-#else
- dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
-#endif
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_address_nearly_too_high) {
- uint8_t buffer[256];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-
-#if defined(__LP64__)
- dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
-#else
- dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
-#endif
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 3fffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" 3fffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" 3fffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" 3fffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" 3fffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 3fffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" 3fffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
-" 3fffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
-" 3fffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 3fffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 3fffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 3fffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 3fffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 3fffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 3fffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 3ffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
-#else
-" fffeff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
-" fffeff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
-" fffeff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
-" fffeff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
-" fffeff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" fffeff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
-" fffeff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
-" fffeff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
-" fffeff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" fffeff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" fffeffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" fffeffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" fffeffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" fffeffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" fffeffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" fffefff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, first_read_empty) {
- uint8_t buffer[256];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(0);
-
- size_t page_size = sysconf(_SC_PAGE_SIZE);
- uintptr_t addr = 0x10000020 + page_size - 120;
- dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 0000000010000f88 ---------------- ---------------- ................\n"
-" 0000000010000f98 ---------------- ---------------- ................\n"
-" 0000000010000fa8 ---------------- ---------------- ................\n"
-" 0000000010000fb8 ---------------- ---------------- ................\n"
-" 0000000010000fc8 ---------------- ---------------- ................\n"
-" 0000000010000fd8 ---------------- ---------------- ................\n"
-" 0000000010000fe8 ---------------- ---------------- ................\n"
-" 0000000010000ff8 ---------------- 7f7e7d7c7b7a7978 ........xyz{|}~.\n"
-" 0000000010001008 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 0000000010001018 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
-#else
-" 10000f88 -------- -------- -------- -------- ................\n"
-" 10000f98 -------- -------- -------- -------- ................\n"
-" 10000fa8 -------- -------- -------- -------- ................\n"
-" 10000fb8 -------- -------- -------- -------- ................\n"
-" 10000fc8 -------- -------- -------- -------- ................\n"
-" 10000fd8 -------- -------- -------- -------- ................\n"
-" 10000fe8 -------- -------- -------- -------- ................\n"
-" 10000ff8 -------- -------- 7b7a7978 7f7e7d7c ........xyz{|}~.\n"
-" 10001008 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" 10001018 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" 10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" 10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" 10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" 10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" 10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" 10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) {
- uint8_t buffer[224];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(0);
-
- size_t page_size = sysconf(_SC_PAGE_SIZE);
- uintptr_t addr = 0x10000020 + page_size - 192;
- dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 0000000010000f40 ---------------- ---------------- ................\n"
-" 0000000010000f50 ---------------- ---------------- ................\n"
-" 0000000010000f60 ---------------- ---------------- ................\n"
-" 0000000010000f70 ---------------- ---------------- ................\n"
-" 0000000010000f80 ---------------- ---------------- ................\n"
-" 0000000010000f90 ---------------- ---------------- ................\n"
-" 0000000010000fa0 ---------------- ---------------- ................\n"
-" 0000000010000fb0 ---------------- ---------------- ................\n"
-" 0000000010000fc0 ---------------- ---------------- ................\n"
-" 0000000010000fd0 ---------------- ---------------- ................\n"
-" 0000000010000fe0 ---------------- ---------------- ................\n"
-" 0000000010000ff0 ---------------- ---------------- ................\n"
-" 0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 0000000010001020 ---------------- ---------------- ................\n"
-" 0000000010001030 ---------------- ---------------- ................\n";
-#else
-" 10000f40 -------- -------- -------- -------- ................\n"
-" 10000f50 -------- -------- -------- -------- ................\n"
-" 10000f60 -------- -------- -------- -------- ................\n"
-" 10000f70 -------- -------- -------- -------- ................\n"
-" 10000f80 -------- -------- -------- -------- ................\n"
-" 10000f90 -------- -------- -------- -------- ................\n"
-" 10000fa0 -------- -------- -------- -------- ................\n"
-" 10000fb0 -------- -------- -------- -------- ................\n"
-" 10000fc0 -------- -------- -------- -------- ................\n"
-" 10000fd0 -------- -------- -------- -------- ................\n"
-" 10000fe0 -------- -------- -------- -------- ................\n"
-" 10000ff0 -------- -------- -------- -------- ................\n"
-" 10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" 10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" 10001020 -------- -------- -------- -------- ................\n"
-" 10001030 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) {
- uint8_t buffer[256];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(0);
-
- uintptr_t addr = 0x10000020;
- dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 0000000010000000 ---------------- ---------------- ................\n"
-" 0000000010000010 ---------------- ---------------- ................\n"
-" 0000000010000020 ---------------- ---------------- ................\n"
-" 0000000010000030 ---------------- ---------------- ................\n"
-" 0000000010000040 ---------------- ---------------- ................\n"
-" 0000000010000050 ---------------- ---------------- ................\n"
-" 0000000010000060 ---------------- ---------------- ................\n"
-" 0000000010000070 ---------------- ---------------- ................\n"
-" 0000000010000080 ---------------- ---------------- ................\n"
-" 0000000010000090 ---------------- ---------------- ................\n"
-" 00000000100000a0 ---------------- ---------------- ................\n"
-" 00000000100000b0 ---------------- ---------------- ................\n"
-" 00000000100000c0 ---------------- ---------------- ................\n"
-" 00000000100000d0 ---------------- ---------------- ................\n"
-" 00000000100000e0 ---------------- ---------------- ................\n"
-" 00000000100000f0 ---------------- ---------------- ................\n";
-#else
-" 10000000 -------- -------- -------- -------- ................\n"
-" 10000010 -------- -------- -------- -------- ................\n"
-" 10000020 -------- -------- -------- -------- ................\n"
-" 10000030 -------- -------- -------- -------- ................\n"
-" 10000040 -------- -------- -------- -------- ................\n"
-" 10000050 -------- -------- -------- -------- ................\n"
-" 10000060 -------- -------- -------- -------- ................\n"
-" 10000070 -------- -------- -------- -------- ................\n"
-" 10000080 -------- -------- -------- -------- ................\n"
-" 10000090 -------- -------- -------- -------- ................\n"
-" 100000a0 -------- -------- -------- -------- ................\n"
-" 100000b0 -------- -------- -------- -------- ................\n"
-" 100000c0 -------- -------- -------- -------- ................\n"
-" 100000d0 -------- -------- -------- -------- ................\n"
-" 100000e0 -------- -------- -------- -------- ................\n"
-" 100000f0 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) {
- uint8_t buffer[256];
- for (size_t i = 0; i < sizeof(buffer); i++) {
- buffer[i] = i;
- }
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(0);
-
- size_t page_size = sysconf(_SC_PAGE_SIZE);
- uintptr_t addr = 0x10000020 + page_size - 256;
-
- dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 0000000010000f00 ---------------- ---------------- ................\n"
-" 0000000010000f10 ---------------- ---------------- ................\n"
-" 0000000010000f20 ---------------- ---------------- ................\n"
-" 0000000010000f30 ---------------- ---------------- ................\n"
-" 0000000010000f40 ---------------- ---------------- ................\n"
-" 0000000010000f50 ---------------- ---------------- ................\n"
-" 0000000010000f60 ---------------- ---------------- ................\n"
-" 0000000010000f70 ---------------- ---------------- ................\n"
-" 0000000010000f80 ---------------- ---------------- ................\n"
-" 0000000010000f90 ---------------- ---------------- ................\n"
-" 0000000010000fa0 ---------------- ---------------- ................\n"
-" 0000000010000fb0 ---------------- ---------------- ................\n"
-" 0000000010000fc0 ---------------- ---------------- ................\n"
-" 0000000010000fd0 ---------------- ---------------- ................\n"
-" 0000000010000fe0 ---------------- ---------------- ................\n"
-" 0000000010000ff0 ---------------- ---------------- ................\n";
-#else
-" 10000f00 -------- -------- -------- -------- ................\n"
-" 10000f10 -------- -------- -------- -------- ................\n"
-" 10000f20 -------- -------- -------- -------- ................\n"
-" 10000f30 -------- -------- -------- -------- ................\n"
-" 10000f40 -------- -------- -------- -------- ................\n"
-" 10000f50 -------- -------- -------- -------- ................\n"
-" 10000f60 -------- -------- -------- -------- ................\n"
-" 10000f70 -------- -------- -------- -------- ................\n"
-" 10000f80 -------- -------- -------- -------- ................\n"
-" 10000f90 -------- -------- -------- -------- ................\n"
-" 10000fa0 -------- -------- -------- -------- ................\n"
-" 10000fb0 -------- -------- -------- -------- ................\n"
-" 10000fc0 -------- -------- -------- -------- ................\n"
-" 10000fd0 -------- -------- -------- -------- ................\n"
-" 10000fe0 -------- -------- -------- -------- ................\n"
-" 10000ff0 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
diff --git a/debuggerd/test/host_signal_fixup.h b/debuggerd/test/host_signal_fixup.h
deleted file mode 100644
index c7796ef..0000000
--- a/debuggerd/test/host_signal_fixup.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
-#define _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
-
-#include <signal.h>
-
-#if !defined(__BIONIC__)
-
-// In order to compile parts of debuggerd for the host, we need to
-// define these values.
-
-#if !defined(NSIGILL)
-#define NSIGILL ILL_BADSTK
-#endif
-
-#if !defined(BUS_MCEERR_AR)
-#define BUS_MCEERR_AR 4
-#endif
-#if !defined(BUS_MCEERR_AO)
-#define BUS_MCEERR_AO 5
-#endif
-#if !defined(NSIGBUS)
-#define NSIGBUS BUS_MCEERR_AO
-#endif
-
-#if !defined(NSIGFPE)
-#define NSIGFPE FPE_FLTSUB
-#endif
-
-#if !defined(NSIGSEGV)
-#define NSIGSEGV SEGV_ACCERR
-#endif
-
-#if !defined(TRAP_BRANCH)
-#define TRAP_BRANCH 3
-#endif
-#if !defined(TRAP_HWBKPT)
-#define TRAP_HWBKPT 4
-#endif
-#if !defined(NSIGTRAP)
-#define NSIGTRAP TRAP_HWBKPT
-#endif
-
-#if !defined(SI_DETHREAD)
-#define SI_DETHREAD -7
-#endif
-
-#endif
-
-#endif // _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp
deleted file mode 100644
index d584a5e..0000000
--- a/debuggerd/test/log_fake.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <stdarg.h>
-
-#include <string>
-
-#include <base/stringprintf.h>
-#include <log/log.h>
-#include <log/logger.h>
-
-// Forward declarations.
-class Backtrace;
-struct EventTagMap;
-struct AndroidLogEntry;
-
-std::string g_fake_log_buf;
-
-std::string g_fake_log_print;
-
-void resetLogs() {
- g_fake_log_buf = "";
- g_fake_log_print = "";
-}
-
-std::string getFakeLogBuf() {
- return g_fake_log_buf;
-}
-
-std::string getFakeLogPrint() {
- return g_fake_log_print;
-}
-
-extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
- g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
- g_fake_log_buf += tag;
- g_fake_log_buf += ' ';
- g_fake_log_buf += msg;
- return 1;
-}
-
-extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
- g_fake_log_print += std::to_string(prio) + ' ';
- g_fake_log_print += tag;
- g_fake_log_print += ' ';
-
- va_list ap;
- va_start(ap, fmt);
- android::base::StringAppendV(&g_fake_log_print, fmt, ap);
- va_end(ap);
-
- g_fake_log_print += '\n';
-
- return 1;
-}
-
-extern "C" log_id_t android_name_to_log_id(const char*) {
- return LOG_ID_SYSTEM;
-}
-
-extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
- errno = EACCES;
- return nullptr;
-}
-
-extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
- return 0;
-}
-
-extern "C" EventTagMap* android_openEventTagMap(const char*) {
- return nullptr;
-}
-
-extern "C" int android_log_processBinaryLogBuffer(
- struct logger_entry*,
- AndroidLogEntry*, const EventTagMap*, char*, int) {
- return 0;
-}
-
-extern "C" void android_logger_list_free(struct logger_list*) {
-}
diff --git a/debuggerd/test/selinux_fake.cpp b/debuggerd/test/selinux_fake.cpp
deleted file mode 100644
index acdd0a9..0000000
--- a/debuggerd/test/selinux_fake.cpp
+++ /dev/null
@@ -1,19 +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.
- */
-
-extern "C" int selinux_android_restorecon(const char*, unsigned int) {
- return 0;
-}
diff --git a/debuggerd/test/tombstone_test.cpp b/debuggerd/test/tombstone_test.cpp
deleted file mode 100644
index d945d27..0000000
--- a/debuggerd/test/tombstone_test.cpp
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-
-#include <memory>
-#include <string>
-
-#include <gtest/gtest.h>
-#include <base/file.h>
-
-#include "utility.h"
-
-#include "BacktraceMock.h"
-#include "elf_fake.h"
-#include "host_signal_fixup.h"
-#include "log_fake.h"
-#include "ptrace_fake.h"
-
-// In order to test this code, we need to include the tombstone.cpp code.
-// Including it, also allows us to override the ptrace function.
-#define ptrace ptrace_fake
-
-#include "tombstone.cpp"
-
-void dump_registers(log_t*, pid_t) {
-}
-
-void dump_memory_and_code(log_t*, Backtrace*) {
-}
-
-void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
-}
-
-class TombstoneTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- map_mock_.reset(new BacktraceMapMock());
- backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
-
- char tmp_file[256];
- const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
- memcpy(tmp_file, data_template, sizeof(data_template));
- int tombstone_fd = mkstemp(tmp_file);
- if (tombstone_fd == -1) {
- const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
- memcpy(tmp_file, tmp_template, sizeof(tmp_template));
- tombstone_fd = mkstemp(tmp_file);
- if (tombstone_fd == -1) {
- abort();
- }
- }
- if (unlink(tmp_file) == -1) {
- abort();
- }
-
- log_.tfd = tombstone_fd;
- log_.amfd = -1;
- log_.crashed_tid = 12;
- log_.current_tid = 12;
- log_.should_retrieve_logcat = false;
-
- resetLogs();
- elf_set_fake_build_id("");
- siginfo_t si;
- si.si_signo = SIGABRT;
- ptrace_set_fake_getsiginfo(si);
- }
-
- virtual void TearDown() {
- if (log_.tfd >= 0) {
- close(log_.tfd);
- }
- }
-
- std::unique_ptr<BacktraceMapMock> map_mock_;
- std::unique_ptr<BacktraceMock> backtrace_mock_;
-
- log_t log_;
-};
-
-TEST_F(TombstoneTest, single_map) {
- backtrace_map_t map;
-#if defined(__LP64__)
- map.start = 0x123456789abcd000UL;
- map.end = 0x123456789abdf000UL;
-#else
- map.start = 0x1234000;
- map.end = 0x1235000;
-#endif
- map_mock_->AddMap(map);
-
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map:\n"
-#if defined(__LP64__)
-" 12345678'9abcd000-12345678'9abdefff --- 0 12000\n";
-#else
-" 01234000-01234fff --- 0 1000\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, single_map_elf_build_id) {
- backtrace_map_t map;
-#if defined(__LP64__)
- map.start = 0x123456789abcd000UL;
- map.end = 0x123456789abdf000UL;
-#else
- map.start = 0x1234000;
- map.end = 0x1235000;
-#endif
- map.flags = PROT_READ;
- map.name = "/system/lib/libfake.so";
- map_mock_->AddMap(map);
-
- elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map:\n"
-#if defined(__LP64__)
-" 12345678'9abcd000-12345678'9abdefff r-- 0 12000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
-#else
-" 01234000-01234fff r-- 0 1000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-// Even though build id is present, it should not be printed in either of
-// these cases.
-TEST_F(TombstoneTest, single_map_no_build_id) {
- backtrace_map_t map;
-#if defined(__LP64__)
- map.start = 0x123456789abcd000UL;
- map.end = 0x123456789abdf000UL;
-#else
- map.start = 0x1234000;
- map.end = 0x1235000;
-#endif
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.name = "/system/lib/libfake.so";
- map_mock_->AddMap(map);
-
- elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map:\n"
-#if defined(__LP64__)
-" 12345678'9abcd000-12345678'9abdefff -w- 0 12000\n"
-" 12345678'9abcd000-12345678'9abdefff -w- 0 12000 /system/lib/libfake.so\n";
-#else
-" 01234000-01234fff -w- 0 1000\n"
-" 01234000-01234fff -w- 0 1000 /system/lib/libfake.so\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps) {
- backtrace_map_t map;
-
- map.start = 0xa234000;
- map.end = 0xa235000;
- map_mock_->AddMap(map);
-
- map.start = 0xa334000;
- map.end = 0xa335000;
- map.offset = 0xf000;
- map.flags = PROT_READ;
- map_mock_->AddMap(map);
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_base = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_base = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_base = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
-
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map:\n"
-#if defined(__LP64__)
-" 00000000'0a234000-00000000'0a234fff --- 0 1000\n"
-" 00000000'0a334000-00000000'0a334fff r-- f000 1000\n"
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
-#else
-" 0a234000-0a234fff --- 0 1000\n"
-" 0a334000-0a334fff r-- f000 1000\n"
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
- backtrace_map_t map;
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_base = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_base = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_base = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
-
- siginfo_t si;
- si.si_signo = SIGBUS;
- si.si_addr = reinterpret_cast<void*>(0x1000);
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
-#if defined(__LP64__)
-"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
-#else
-"--->Fault address falls at 00001000 before any mapped regions\n"
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
- backtrace_map_t map;
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_base = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_base = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_base = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
-
- siginfo_t si;
- si.si_signo = SIGBUS;
- si.si_addr = reinterpret_cast<void*>(0xa533000);
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
-#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->Fault address falls at 00000000'0a533000 between mapped regions\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
-#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->Fault address falls at 0a533000 between mapped regions\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
- backtrace_map_t map;
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_base = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_base = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_base = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
-
- siginfo_t si;
- si.si_signo = SIGBUS;
- si.si_addr = reinterpret_cast<void*>(0xa534040);
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
-#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
-#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
- backtrace_map_t map;
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_base = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_base = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_base = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
-
- siginfo_t si;
- si.si_signo = SIGBUS;
-#if defined(__LP64__)
- si.si_addr = reinterpret_cast<void*>(0x12345a534040UL);
-#else
- si.si_addr = reinterpret_cast<void*>(0xf534040UL);
-#endif
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
-#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"
-"--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
-#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"
-"--->Fault address falls at 0f534040 after any mapped regions\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
- backtrace_map_t map;
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_base = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- siginfo_t si;
- si.si_signo = 0;
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map:\n"
-#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n";
-#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
- backtrace_map_t map;
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- for (int i = 1; i < 255; i++) {
- ASSERT_TRUE(ftruncate(log_.tfd, 0) == 0);
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-
- siginfo_t si;
- si.si_signo = i;
- si.si_addr = reinterpret_cast<void*>(0x1000);
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- bool has_addr = false;
- switch (si.si_signo) {
- case SIGBUS:
- case SIGFPE:
- case SIGILL:
- case SIGSEGV:
- case SIGTRAP:
- has_addr = true;
- break;
- }
-
- const char* expected_addr_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
-#if defined(__LP64__)
-"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
-" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
-#else
-"--->Fault address falls at 00001000 before any mapped regions\n"
-" 0a434000-0a434fff -w- 0 1000\n";
-#endif
- const char* expected_dump = \
-"\nmemory map:\n"
-#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
-#else
-" 0a434000-0a434fff -w- 0 1000\n";
-#endif
- if (has_addr) {
- ASSERT_STREQ(expected_addr_dump, tombstone_contents.c_str())
- << "Signal " << si.si_signo << " expected to include an address.";
- } else {
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str())
- << "Signal " << si.si_signo << " is not expected to include an address.";
- }
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
- }
-}
-
-TEST_F(TombstoneTest, dump_signal_info_error) {
- siginfo_t si;
- si.si_signo = 0;
- ptrace_set_fake_getsiginfo(si);
-
- dump_signal_info(&log_, 123, SIGSEGV, 10);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, dump_log_file_error) {
- log_.should_retrieve_logcat = true;
- dump_log_file(&log_, 123, "/fake/filename", 10);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
- getFakeLogPrint().c_str());
-}
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
deleted file mode 100644
index e283923..0000000
--- a/debuggerd/tombstone.cpp
+++ /dev/null
@@ -1,825 +0,0 @@
-/*
- * Copyright (C) 2012-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <arpa/inet.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <signal.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <sys/ptrace.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-
-#include <memory>
-#include <string>
-
-#include <private/android_filesystem_config.h>
-
-#include <base/stringprintf.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-#include <log/logger.h>
-#include <log/logprint.h>
-
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-#include <selinux/android.h>
-
-#include "backtrace.h"
-#include "elf_utils.h"
-#include "machine.h"
-#include "tombstone.h"
-
-#define STACK_WORDS 16
-
-#define MAX_TOMBSTONES 10
-#define TOMBSTONE_DIR "/data/tombstones"
-#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d")
-
-// Must match the path defined in NativeCrashListener.java
-#define NCRASH_SOCKET_PATH "/data/system/ndebugsocket"
-
-static bool signal_has_si_addr(int sig) {
- switch (sig) {
- case SIGBUS:
- case SIGFPE:
- case SIGILL:
- case SIGSEGV:
- case SIGTRAP:
- return true;
- default:
- return false;
- }
-}
-
-static const char* get_signame(int sig) {
- switch(sig) {
- case SIGABRT: return "SIGABRT";
- case SIGBUS: return "SIGBUS";
- case SIGFPE: return "SIGFPE";
- case SIGILL: return "SIGILL";
- case SIGSEGV: return "SIGSEGV";
-#if defined(SIGSTKFLT)
- case SIGSTKFLT: return "SIGSTKFLT";
-#endif
- case SIGSTOP: return "SIGSTOP";
- case SIGTRAP: return "SIGTRAP";
- default: return "?";
- }
-}
-
-static const char* get_sigcode(int signo, int code) {
- // Try the signal-specific codes...
- switch (signo) {
- case SIGILL:
- switch (code) {
- case ILL_ILLOPC: return "ILL_ILLOPC";
- case ILL_ILLOPN: return "ILL_ILLOPN";
- case ILL_ILLADR: return "ILL_ILLADR";
- case ILL_ILLTRP: return "ILL_ILLTRP";
- case ILL_PRVOPC: return "ILL_PRVOPC";
- case ILL_PRVREG: return "ILL_PRVREG";
- case ILL_COPROC: return "ILL_COPROC";
- case ILL_BADSTK: return "ILL_BADSTK";
- }
- static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
- break;
- case SIGBUS:
- switch (code) {
- case BUS_ADRALN: return "BUS_ADRALN";
- case BUS_ADRERR: return "BUS_ADRERR";
- case BUS_OBJERR: return "BUS_OBJERR";
- case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
- case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
- }
- static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
- break;
- case SIGFPE:
- switch (code) {
- case FPE_INTDIV: return "FPE_INTDIV";
- case FPE_INTOVF: return "FPE_INTOVF";
- case FPE_FLTDIV: return "FPE_FLTDIV";
- case FPE_FLTOVF: return "FPE_FLTOVF";
- case FPE_FLTUND: return "FPE_FLTUND";
- case FPE_FLTRES: return "FPE_FLTRES";
- case FPE_FLTINV: return "FPE_FLTINV";
- case FPE_FLTSUB: return "FPE_FLTSUB";
- }
- static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
- break;
- case SIGSEGV:
- switch (code) {
- case SEGV_MAPERR: return "SEGV_MAPERR";
- case SEGV_ACCERR: return "SEGV_ACCERR";
- }
- static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
- break;
- case SIGTRAP:
- switch (code) {
- case TRAP_BRKPT: return "TRAP_BRKPT";
- case TRAP_TRACE: return "TRAP_TRACE";
- case TRAP_BRANCH: return "TRAP_BRANCH";
- case TRAP_HWBKPT: return "TRAP_HWBKPT";
- }
- static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
- break;
- }
- // Then the other codes...
- switch (code) {
- case SI_USER: return "SI_USER";
- case SI_KERNEL: return "SI_KERNEL";
- case SI_QUEUE: return "SI_QUEUE";
- case SI_TIMER: return "SI_TIMER";
- case SI_MESGQ: return "SI_MESGQ";
- case SI_ASYNCIO: return "SI_ASYNCIO";
- case SI_SIGIO: return "SI_SIGIO";
- case SI_TKILL: return "SI_TKILL";
- case SI_DETHREAD: return "SI_DETHREAD";
- }
- // Then give up...
- return "?";
-}
-
-static void dump_header_info(log_t* log) {
- char fingerprint[PROPERTY_VALUE_MAX];
- char revision[PROPERTY_VALUE_MAX];
-
- property_get("ro.build.fingerprint", fingerprint, "unknown");
- property_get("ro.revision", revision, "unknown");
-
- _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint);
- _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision);
- _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
-}
-
-static void dump_signal_info(log_t* log, pid_t tid, int signal, int si_code) {
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
- ALOGE("cannot get siginfo: %s\n", strerror(errno));
- return;
- }
-
- // bionic has to re-raise some signals, which overwrites the si_code with SI_TKILL.
- si.si_code = si_code;
-
- char addr_desc[32]; // ", fault addr 0x1234"
- if (signal_has_si_addr(signal)) {
- snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
- } else {
- snprintf(addr_desc, sizeof(addr_desc), "--------");
- }
-
- _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n",
- signal, get_signame(signal), si.si_code, get_sigcode(signal, si.si_code), addr_desc);
-}
-
-static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) {
- char path[64];
- char threadnamebuf[1024];
- char* threadname = NULL;
- FILE *fp;
-
- snprintf(path, sizeof(path), "/proc/%d/comm", tid);
- if ((fp = fopen(path, "r"))) {
- threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
- fclose(fp);
- if (threadname) {
- size_t len = strlen(threadname);
- if (len && threadname[len - 1] == '\n') {
- threadname[len - 1] = '\0';
- }
- }
- }
- // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
- static const char logd[] = "logd";
- if (!strncmp(threadname, logd, sizeof(logd) - 1)
- && (!threadname[sizeof(logd) - 1] || (threadname[sizeof(logd) - 1] == '.'))) {
- log->should_retrieve_logcat = false;
- }
-
- char procnamebuf[1024];
- char* procname = NULL;
-
- snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
- if ((fp = fopen(path, "r"))) {
- procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
- fclose(fp);
- }
-
- _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid,
- threadname ? threadname : "UNKNOWN", procname ? procname : "UNKNOWN");
-}
-
-static void dump_stack_segment(
- Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) {
- // Read the data all at once.
- word_t stack_data[words];
- size_t bytes_read = backtrace->Read(*sp, reinterpret_cast<uint8_t*>(&stack_data[0]), sizeof(word_t) * words);
- words = bytes_read / sizeof(word_t);
- std::string line;
- for (size_t i = 0; i < words; i++) {
- line = " ";
- if (i == 0 && label >= 0) {
- // Print the label once.
- line += android::base::StringPrintf("#%02d ", label);
- } else {
- line += " ";
- }
- line += android::base::StringPrintf("%" PRIPTR " %" PRIPTR, *sp, stack_data[i]);
-
- backtrace_map_t map;
- backtrace->FillInMap(stack_data[i], &map);
- if (BacktraceMap::IsValid(map) && !map.name.empty()) {
- line += " " + map.name;
- uintptr_t offset = 0;
- std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset));
- if (!func_name.empty()) {
- line += " (" + func_name;
- if (offset) {
- line += android::base::StringPrintf("+%" PRIuPTR, offset);
- }
- line += ')';
- }
- }
- _LOG(log, logtype::STACK, "%s\n", line.c_str());
-
- *sp += sizeof(word_t);
- }
-}
-
-static void dump_stack(Backtrace* backtrace, log_t* log) {
- size_t first = 0, last;
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
- if (frame->sp) {
- if (!first) {
- first = i+1;
- }
- last = i;
- }
- }
- if (!first) {
- return;
- }
- first--;
-
- // Dump a few words before the first frame.
- word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t);
- dump_stack_segment(backtrace, log, &sp, STACK_WORDS, -1);
-
- // Dump a few words from all successive frames.
- // Only log the first 3 frames, put the rest in the tombstone.
- for (size_t i = first; i <= last; i++) {
- const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
- if (sp != frame->sp) {
- _LOG(log, logtype::STACK, " ........ ........\n");
- sp = frame->sp;
- }
- if (i == last) {
- dump_stack_segment(backtrace, log, &sp, STACK_WORDS, i);
- if (sp < frame->sp + frame->stack_size) {
- _LOG(log, logtype::STACK, " ........ ........\n");
- }
- } else {
- size_t words = frame->stack_size / sizeof(word_t);
- if (words == 0) {
- words = 1;
- } else if (words > STACK_WORDS) {
- words = STACK_WORDS;
- }
- dump_stack_segment(backtrace, log, &sp, words, i);
- }
- }
-}
-
-static std::string get_addr_string(uintptr_t addr) {
- std::string addr_str;
-#if defined(__LP64__)
- addr_str = android::base::StringPrintf("%08x'%08x",
- static_cast<uint32_t>(addr >> 32),
- static_cast<uint32_t>(addr & 0xffffffff));
-#else
- addr_str = android::base::StringPrintf("%08x", addr);
-#endif
- return addr_str;
-}
-
-static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
- bool print_fault_address_marker = false;
- uintptr_t addr = 0;
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) {
- print_fault_address_marker = signal_has_si_addr(si.si_signo);
- addr = reinterpret_cast<uintptr_t>(si.si_addr);
- } else {
- ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
- }
-
- _LOG(log, logtype::MAPS, "\n");
- if (!print_fault_address_marker) {
- _LOG(log, logtype::MAPS, "memory map:\n");
- } else {
- _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n");
- if (map->begin() != map->end() && addr < map->begin()->start) {
- _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n",
- get_addr_string(addr).c_str());
- print_fault_address_marker = false;
- }
- }
-
- std::string line;
- for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
- line = " ";
- if (print_fault_address_marker) {
- if (addr < it->start) {
- _LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
- get_addr_string(addr).c_str());
- print_fault_address_marker = false;
- } else if (addr >= it->start && addr < it->end) {
- line = "--->";
- print_fault_address_marker = false;
- }
- }
- line += get_addr_string(it->start) + '-' + get_addr_string(it->end - 1) + ' ';
- if (it->flags & PROT_READ) {
- line += 'r';
- } else {
- line += '-';
- }
- if (it->flags & PROT_WRITE) {
- line += 'w';
- } else {
- line += '-';
- }
- if (it->flags & PROT_EXEC) {
- line += 'x';
- } else {
- line += '-';
- }
- line += android::base::StringPrintf(" %8" PRIxPTR " %8" PRIxPTR,
- it->offset, it->end - it->start);
- bool space_needed = true;
- if (it->name.length() > 0) {
- space_needed = false;
- line += " " + it->name;
- std::string build_id;
- if ((it->flags & PROT_READ) && elf_get_build_id(backtrace, it->start, &build_id)) {
- line += " (BuildId: " + build_id + ")";
- }
- }
- if (it->load_base != 0) {
- if (space_needed) {
- line += ' ';
- }
- line += android::base::StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
- }
- _LOG(log, logtype::MAPS, "%s\n", line.c_str());
- }
- if (print_fault_address_marker) {
- _LOG(log, logtype::MAPS, "--->Fault address falls at %s after any mapped regions\n",
- get_addr_string(addr).c_str());
- }
-}
-
-static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
- if (backtrace->NumFrames()) {
- _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
- dump_backtrace_to_log(backtrace, log, " ");
-
- _LOG(log, logtype::STACK, "\nstack:\n");
- dump_stack(backtrace, log);
- }
-}
-
-// Return true if some thread is not detached cleanly
-static bool dump_sibling_thread_report(
- log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, BacktraceMap* map) {
- char task_path[64];
-
- snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
-
- DIR* d = opendir(task_path);
- // Bail early if the task directory cannot be opened
- if (d == NULL) {
- ALOGE("Cannot open /proc/%d/task\n", pid);
- return false;
- }
-
- bool detach_failed = false;
- struct dirent* de;
- while ((de = readdir(d)) != NULL) {
- // Ignore "." and ".."
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
- continue;
- }
-
- // The main thread at fault has been handled individually
- char* end;
- pid_t new_tid = strtoul(de->d_name, &end, 10);
- if (*end || new_tid == tid) {
- continue;
- }
-
- // Skip this thread if cannot ptrace it
- if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
- ALOGE("ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
- continue;
- }
-
- if (wait_for_sigstop(new_tid, total_sleep_time_usec, &detach_failed) == -1) {
- continue;
- }
-
- log->current_tid = new_tid;
- _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
- dump_thread_info(log, pid, new_tid);
-
- dump_registers(log, new_tid);
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map));
- if (backtrace->Unwind(0)) {
- dump_backtrace_and_stack(backtrace.get(), log);
- } else {
- ALOGE("Unwind of sibling failed: pid = %d, tid = %d", pid, new_tid);
- }
-
- log->current_tid = log->crashed_tid;
-
- if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
- ALOGE("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
- detach_failed = true;
- }
- }
-
- closedir(d);
- return detach_failed;
-}
-
-// Reads the contents of the specified log device, filters out the entries
-// that don't match the specified pid, and writes them to the tombstone file.
-//
-// If "tail" is non-zero, log the last "tail" number of lines.
-static EventTagMap* g_eventTagMap = NULL;
-
-static void dump_log_file(
- log_t* log, pid_t pid, const char* filename, unsigned int tail) {
- bool first = true;
- struct logger_list* logger_list;
-
- if (!log->should_retrieve_logcat) {
- return;
- }
-
- logger_list = android_logger_list_open(
- android_name_to_log_id(filename), ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tail, pid);
-
- if (!logger_list) {
- ALOGE("Unable to open %s: %s\n", filename, strerror(errno));
- return;
- }
-
- struct log_msg log_entry;
-
- while (true) {
- ssize_t actual = android_logger_list_read(logger_list, &log_entry);
- struct logger_entry* entry;
-
- if (actual < 0) {
- if (actual == -EINTR) {
- // interrupted by signal, retry
- continue;
- } else if (actual == -EAGAIN) {
- // non-blocking EOF; we're done
- break;
- } else {
- ALOGE("Error while reading log: %s\n", strerror(-actual));
- break;
- }
- } else if (actual == 0) {
- ALOGE("Got zero bytes while reading log: %s\n", strerror(errno));
- break;
- }
-
- // NOTE: if you ALOGV something here, this will spin forever,
- // because you will be writing as fast as you're reading. Any
- // high-frequency debug diagnostics should just be written to
- // the tombstone file.
-
- entry = &log_entry.entry_v1;
-
- if (first) {
- _LOG(log, logtype::LOGS, "--------- %slog %s\n",
- tail ? "tail end of " : "", filename);
- first = false;
- }
-
- // Msg format is: <priority:1><tag:N>\0<message:N>\0
- //
- // We want to display it in the same format as "logcat -v threadtime"
- // (although in this case the pid is redundant).
- static const char* kPrioChars = "!.VDIWEFS";
- unsigned hdr_size = log_entry.entry.hdr_size;
- if (!hdr_size) {
- hdr_size = sizeof(log_entry.entry_v1);
- }
- char* msg = reinterpret_cast<char*>(log_entry.buf) + hdr_size;
-
- char timeBuf[32];
- time_t sec = static_cast<time_t>(entry->sec);
- struct tm tmBuf;
- struct tm* ptm;
- ptm = localtime_r(&sec, &tmBuf);
- strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
-
- if (log_entry.id() == LOG_ID_EVENTS) {
- if (!g_eventTagMap) {
- g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
- }
- AndroidLogEntry e;
- char buf[512];
- android_log_processBinaryLogBuffer(entry, &e, g_eventTagMap, buf, sizeof(buf));
- _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
- timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
- 'I', e.tag, e.message);
- continue;
- }
-
- unsigned char prio = msg[0];
- char* tag = msg + 1;
- msg = tag + strlen(tag) + 1;
-
- // consume any trailing newlines
- char* nl = msg + strlen(msg) - 1;
- while (nl >= msg && *nl == '\n') {
- *nl-- = '\0';
- }
-
- char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
-
- // Look for line breaks ('\n') and display each text line
- // on a separate line, prefixed with the header, like logcat does.
- do {
- nl = strchr(msg, '\n');
- if (nl) {
- *nl = '\0';
- ++nl;
- }
-
- _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
- timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
- prioChar, tag, msg);
- } while ((msg = nl));
- }
-
- android_logger_list_free(logger_list);
-}
-
-// Dumps the logs generated by the specified pid to the tombstone, from both
-// "system" and "main" log devices. Ideally we'd interleave the output.
-static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
- dump_log_file(log, pid, "system", tail);
- dump_log_file(log, pid, "main", tail);
-}
-
-static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
- if (address == 0) {
- return;
- }
-
- address += sizeof(size_t); // Skip the buffer length.
-
- char msg[512];
- memset(msg, 0, sizeof(msg));
- char* p = &msg[0];
- while (p < &msg[sizeof(msg)]) {
- word_t data;
- size_t len = sizeof(word_t);
- if (!backtrace->ReadWord(address, &data)) {
- break;
- }
- address += sizeof(word_t);
-
- while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0)
- len--;
- }
- msg[sizeof(msg) - 1] = '\0';
-
- _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
-}
-
-// Dumps all information about the specified pid to the tombstone.
-static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, int si_code,
- uintptr_t abort_msg_address, bool dump_sibling_threads,
- int* total_sleep_time_usec) {
- // don't copy log messages to tombstone unless this is a dev device
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.debuggable", value, "0");
- bool want_logs = (value[0] == '1');
-
- if (log->amfd >= 0) {
- // Activity Manager protocol: binary 32-bit network-byte-order ints for the
- // pid and signal number, followed by the raw text of the dump, culminating
- // in a zero byte that marks end-of-data.
- uint32_t datum = htonl(pid);
- TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) );
- datum = htonl(signal);
- TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) );
- }
-
- _LOG(log, logtype::HEADER,
- "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
- dump_header_info(log);
- dump_thread_info(log, pid, tid);
-
- if (signal) {
- dump_signal_info(log, tid, signal, si_code);
- }
-
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
- dump_abort_message(backtrace.get(), log, abort_msg_address);
- dump_registers(log, tid);
- if (backtrace->Unwind(0)) {
- dump_backtrace_and_stack(backtrace.get(), log);
- } else {
- ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
- }
- dump_memory_and_code(log, backtrace.get());
- if (map.get() != nullptr) {
- dump_all_maps(backtrace.get(), map.get(), log, tid);
- }
-
- if (want_logs) {
- dump_logs(log, pid, 5);
- }
-
- bool detach_failed = false;
- if (dump_sibling_threads) {
- detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map.get());
- }
-
- if (want_logs) {
- dump_logs(log, pid, 0);
- }
-
- // send EOD to the Activity Manager, then wait for its ack to avoid racing ahead
- // and killing the target out from under it
- if (log->amfd >= 0) {
- uint8_t eodMarker = 0;
- TEMP_FAILURE_RETRY( write(log->amfd, &eodMarker, 1) );
- // 3 sec timeout reading the ack; we're fine if that happens
- TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) );
- }
-
- return detach_failed;
-}
-
-// find_and_open_tombstone - find an available tombstone slot, if any, of the
-// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
-// file is available, we reuse the least-recently-modified file.
-//
-// Returns the path of the tombstone file, allocated using malloc(). Caller must free() it.
-static char* find_and_open_tombstone(int* fd) {
- // In a single pass, find an available slot and, in case none
- // exist, find and record the least-recently-modified file.
- char path[128];
- int oldest = -1;
- struct stat oldest_sb;
- for (int i = 0; i < MAX_TOMBSTONES; i++) {
- snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
-
- struct stat sb;
- if (!stat(path, &sb)) {
- if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
- oldest = i;
- oldest_sb.st_mtime = sb.st_mtime;
- }
- continue;
- }
- if (errno != ENOENT)
- continue;
-
- *fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
- if (*fd < 0)
- continue; // raced ?
-
- fchown(*fd, AID_SYSTEM, AID_SYSTEM);
- return strdup(path);
- }
-
- if (oldest < 0) {
- ALOGE("Failed to find a valid tombstone, default to using tombstone 0.\n");
- oldest = 0;
- }
-
- // we didn't find an available file, so we clobber the oldest one
- snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
- *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
- if (*fd < 0) {
- ALOGE("failed to open tombstone file '%s': %s\n", path, strerror(errno));
- return NULL;
- }
- fchown(*fd, AID_SYSTEM, AID_SYSTEM);
- return strdup(path);
-}
-
-static int activity_manager_connect() {
- int amfd = socket(PF_UNIX, SOCK_STREAM, 0);
- if (amfd >= 0) {
- struct sockaddr_un address;
- int err;
-
- memset(&address, 0, sizeof(address));
- address.sun_family = AF_UNIX;
- strncpy(address.sun_path, NCRASH_SOCKET_PATH, sizeof(address.sun_path));
- err = TEMP_FAILURE_RETRY(connect(
- amfd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)));
- if (!err) {
- struct timeval tv;
- memset(&tv, 0, sizeof(tv));
- tv.tv_sec = 1; // tight leash
- err = setsockopt(amfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
- if (!err) {
- tv.tv_sec = 3; // 3 seconds on handshake read
- err = setsockopt(amfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
- }
- }
- if (err) {
- close(amfd);
- amfd = -1;
- }
- }
-
- return amfd;
-}
-
-char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
- uintptr_t abort_msg_address, bool dump_sibling_threads,
- bool* detach_failed, int* total_sleep_time_usec) {
-
- log_t log;
- log.current_tid = tid;
- log.crashed_tid = tid;
-
- if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
- ALOGE("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
- }
-
- if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) {
- ALOGE("failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
- }
-
- int fd = -1;
- char* path = NULL;
- if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == 0) {
- path = find_and_open_tombstone(&fd);
- } else {
- ALOGE("Failed to restore security context, not writing tombstone.\n");
- }
-
- if (fd < 0) {
- ALOGE("Skipping tombstone write, nothing to do.\n");
- *detach_failed = false;
- return NULL;
- }
-
- log.tfd = fd;
- // Preserve amfd since it can be modified through the calls below without
- // being closed.
- int amfd = activity_manager_connect();
- log.amfd = amfd;
- *detach_failed = dump_crash(&log, pid, tid, signal, original_si_code, abort_msg_address,
- dump_sibling_threads, total_sleep_time_usec);
-
- _LOG(&log, logtype::BACKTRACE, "\nTombstone written to: %s\n", path);
-
- // Either of these file descriptors can be -1, any error is ignored.
- close(amfd);
- close(fd);
-
- return path;
-}
diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h
deleted file mode 100644
index 7e2b2fe..0000000
--- a/debuggerd/tombstone.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_TOMBSTONE_H
-#define _DEBUGGERD_TOMBSTONE_H
-
-#include <stddef.h>
-#include <stdbool.h>
-#include <sys/types.h>
-
-/* Creates a tombstone file and writes the crash dump to it.
- * Returns the path of the tombstone, which must be freed using free(). */
-char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
- uintptr_t abort_msg_address,
- bool dump_sibling_threads, bool* detach_failed,
- int* total_sleep_time_usec);
-
-#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
new file mode 100644
index 0000000..789260d
--- /dev/null
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "intercept_manager.h"
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+using android::base::unique_fd;
+
+static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
+ auto intercept = reinterpret_cast<Intercept*>(arg);
+ InterceptManager* intercept_manager = intercept->intercept_manager;
+
+ CHECK_EQ(sockfd, intercept->sockfd.get());
+
+ // If we can read, either we received unexpected data from the other side, or the other side
+ // closed their end of the socket. Either way, kill the intercept.
+
+ // Ownership of intercept differs based on whether we've registered it with InterceptManager.
+ if (!intercept->registered) {
+ delete intercept;
+ } else {
+ auto it = intercept_manager->intercepts.find(intercept->intercept_pid);
+ if (it == intercept_manager->intercepts.end()) {
+ LOG(FATAL) << "intercept close callback called after intercept was already removed?";
+ }
+ if (it->second.get() != intercept) {
+ LOG(FATAL) << "intercept close callback has different Intercept from InterceptManager?";
+ }
+
+ const char* reason;
+ if ((event & EV_TIMEOUT) != 0) {
+ reason = "due to timeout";
+ } else {
+ reason = "due to input";
+ }
+
+ LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason;
+ intercept_manager->intercepts.erase(it);
+ }
+}
+
+static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
+ auto intercept = reinterpret_cast<Intercept*>(arg);
+ InterceptManager* intercept_manager = intercept->intercept_manager;
+
+ CHECK_EQ(sockfd, intercept->sockfd.get());
+
+ if ((ev & EV_TIMEOUT) != 0) {
+ LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
+ goto fail;
+ } else if ((ev & EV_READ) == 0) {
+ LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
+ goto fail;
+ }
+
+ {
+ unique_fd rcv_fd;
+ InterceptRequest intercept_request;
+ ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+
+ if (result == -1) {
+ PLOG(WARNING) << "failed to read from intercept socket";
+ goto fail;
+ } else if (result != sizeof(intercept_request)) {
+ LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
+ << sizeof(intercept_request) << ")";
+ goto fail;
+ }
+
+ // Move the received FD to the upper half, in order to more easily notice FD leaks.
+ int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
+ if (moved_fd == -1) {
+ LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
+ goto fail;
+ }
+ rcv_fd.reset(moved_fd);
+
+ // We trust the other side, so only do minimal validity checking.
+ if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
+ InterceptResponse response = {};
+ snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
+ intercept_request.pid);
+ TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+ goto fail;
+ }
+
+ intercept->intercept_pid = intercept_request.pid;
+
+ // Register the intercept with the InterceptManager.
+ if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
+ InterceptResponse response = {};
+ snprintf(response.error_message, sizeof(response.error_message),
+ "pid %" PRId32 " already intercepted", intercept_request.pid);
+ TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+ LOG(WARNING) << response.error_message;
+ goto fail;
+ }
+
+ intercept->output_fd = std::move(rcv_fd);
+ intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
+ intercept->registered = true;
+
+ LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
+
+ // Register a different read event on the socket so that we can remove intercepts if the socket
+ // closes (e.g. if a user CTRL-C's the process that requested the intercept).
+ event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
+ intercept_close_cb, arg);
+
+ struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
+ event_add(intercept->intercept_event, &timeout);
+ }
+
+ return;
+
+fail:
+ delete intercept;
+}
+
+static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
+ void* arg) {
+ Intercept* intercept = new Intercept();
+ intercept->intercept_manager = static_cast<InterceptManager*>(arg);
+ intercept->sockfd.reset(sockfd);
+
+ struct timeval timeout = { 1, 0 };
+ event_base* base = evconnlistener_get_base(listener);
+ event* intercept_event =
+ event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
+ intercept->intercept_event = intercept_event;
+ event_add(intercept_event, &timeout);
+}
+
+InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
+ this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE,
+ intercept_socket);
+}
+
+bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
+ auto it = this->intercepts.find(pid);
+ if (it == this->intercepts.end()) {
+ return false;
+ }
+
+ auto intercept = std::move(it->second);
+ this->intercepts.erase(it);
+
+ LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
+ InterceptResponse response = {};
+ response.success = 1;
+ TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
+ *out_fd = std::move(intercept->output_fd);
+
+ return true;
+}
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
new file mode 100644
index 0000000..cb5db62
--- /dev/null
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include <android-base/unique_fd.h>
+
+struct InterceptManager;
+
+struct Intercept {
+ ~Intercept() {
+ event_free(intercept_event);
+ }
+
+ InterceptManager* intercept_manager = nullptr;
+ event* intercept_event = nullptr;
+ android::base::unique_fd sockfd;
+
+ pid_t intercept_pid = -1;
+ android::base::unique_fd output_fd;
+ bool registered = false;
+};
+
+struct InterceptManager {
+ event_base* base;
+ std::unordered_map<pid_t, std::unique_ptr<Intercept>> intercepts;
+ evconnlistener* listener = nullptr;
+
+ InterceptManager(event_base* _Nonnull base, int intercept_socket);
+ InterceptManager(InterceptManager& copy) = delete;
+ InterceptManager(InterceptManager&& move) = delete;
+
+ bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd);
+};
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
new file mode 100644
index 0000000..8705ece
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <array>
+#include <deque>
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+#include <event2/thread.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+#include "intercept_manager.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+static InterceptManager* intercept_manager;
+
+enum CrashStatus {
+ kCrashStatusRunning,
+ kCrashStatusQueued,
+};
+
+// Ownership of Crash is a bit messy.
+// It's either owned by an active event that must have a timeout, or owned by
+// queued_requests, in the case that multiple crashes come in at the same time.
+struct Crash {
+ ~Crash() {
+ event_free(crash_event);
+ }
+
+ unique_fd crash_fd;
+ pid_t crash_pid;
+ event* crash_event = nullptr;
+};
+
+static constexpr char kTombstoneDirectory[] = "/data/tombstones/";
+static constexpr size_t kTombstoneCount = 10;
+static int tombstone_directory_fd = -1;
+static int next_tombstone = 0;
+
+static constexpr size_t kMaxConcurrentDumps = 1;
+static size_t num_concurrent_dumps = 0;
+
+static std::deque<Crash*> queued_requests;
+
+// Forward declare the callbacks so they can be placed in a sensible order.
+static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
+static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);
+static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);
+
+static void find_oldest_tombstone() {
+ size_t oldest_tombstone = 0;
+ time_t oldest_time = std::numeric_limits<time_t>::max();
+
+ for (size_t i = 0; i < kTombstoneCount; ++i) {
+ std::string path = android::base::StringPrintf("%stombstone_%02zu", kTombstoneDirectory, i);
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0) {
+ if (errno == ENOENT) {
+ oldest_tombstone = i;
+ break;
+ } else {
+ PLOG(ERROR) << "failed to stat " << path;
+ continue;
+ }
+ }
+
+ if (st.st_mtime < oldest_time) {
+ oldest_tombstone = i;
+ oldest_time = st.st_mtime;
+ }
+ }
+
+ next_tombstone = oldest_tombstone;
+}
+
+static unique_fd get_tombstone_fd() {
+ // If kMaxConcurrentDumps is greater than 1, then theoretically the same
+ // filename could be handed out to multiple processes. Unlink and create the
+ // file, instead of using O_TRUNC, to avoid two processes interleaving their
+ // output.
+ unique_fd result;
+ char buf[PATH_MAX];
+ snprintf(buf, sizeof(buf), "tombstone_%02d", next_tombstone);
+ if (unlinkat(tombstone_directory_fd, buf, 0) != 0 && errno != ENOENT) {
+ PLOG(FATAL) << "failed to unlink tombstone at " << kTombstoneDirectory << buf;
+ }
+
+ result.reset(
+ openat(tombstone_directory_fd, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0700));
+ if (result == -1) {
+ PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << buf;
+ }
+
+ next_tombstone = (next_tombstone + 1) % kTombstoneCount;
+ return result;
+}
+
+static void dequeue_request(Crash* crash) {
+ ++num_concurrent_dumps;
+
+ unique_fd output_fd;
+ if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
+ output_fd = get_tombstone_fd();
+ }
+
+ TombstonedCrashPacket response = {
+ .packet_type = CrashPacketType::kPerformDump
+ };
+ ssize_t rc = send_fd(crash->crash_fd, &response, sizeof(response), std::move(output_fd));
+ if (rc == -1) {
+ PLOG(WARNING) << "failed to send response to CrashRequest";
+ goto fail;
+ } else if (rc != sizeof(response)) {
+ PLOG(WARNING) << "crash socket write returned short";
+ goto fail;
+ } else {
+ // TODO: Make this configurable by the interceptor?
+ struct timeval timeout = { 10, 0 };
+
+ event_base* base = event_get_base(crash->crash_event);
+ event_assign(crash->crash_event, base, crash->crash_fd, EV_TIMEOUT | EV_READ,
+ crash_completed_cb, crash);
+ event_add(crash->crash_event, &timeout);
+ }
+ return;
+
+fail:
+ delete crash;
+}
+
+static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
+ void*) {
+ event_base* base = evconnlistener_get_base(listener);
+ Crash* crash = new Crash();
+
+ struct timeval timeout = { 1, 0 };
+ event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
+ crash->crash_fd.reset(sockfd);
+ crash->crash_event = crash_event;
+ event_add(crash_event, &timeout);
+}
+
+static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
+ ssize_t rc;
+ Crash* crash = static_cast<Crash*>(arg);
+ TombstonedCrashPacket request = {};
+
+ if ((ev & EV_TIMEOUT) != 0) {
+ LOG(WARNING) << "crash request timed out";
+ goto fail;
+ } else if ((ev & EV_READ) == 0) {
+ LOG(WARNING) << "tombstoned received unexpected event from crash socket";
+ goto fail;
+ }
+
+ rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
+ if (rc == -1) {
+ PLOG(WARNING) << "failed to read from crash socket";
+ goto fail;
+ } else if (rc != sizeof(request)) {
+ LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
+ << sizeof(request) << ")";
+ goto fail;
+ }
+
+ if (request.packet_type != CrashPacketType::kDumpRequest) {
+ LOG(WARNING) << "unexpected crash packet type, expected kDumpRequest, received "
+ << StringPrintf("%#2hhX", request.packet_type);
+ goto fail;
+ }
+
+ crash->crash_pid = request.packet.dump_request.pid;
+ LOG(INFO) << "received crash request for pid " << crash->crash_pid;
+
+ if (num_concurrent_dumps == kMaxConcurrentDumps) {
+ LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
+ queued_requests.push_back(crash);
+ } else {
+ dequeue_request(crash);
+ }
+
+ return;
+
+fail:
+ delete crash;
+}
+
+static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) {
+ ssize_t rc;
+ Crash* crash = static_cast<Crash*>(arg);
+ TombstonedCrashPacket request = {};
+
+ --num_concurrent_dumps;
+
+ if ((ev & EV_READ) == 0) {
+ goto fail;
+ }
+
+ rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
+ if (rc == -1) {
+ PLOG(WARNING) << "failed to read from crash socket";
+ goto fail;
+ } else if (rc != sizeof(request)) {
+ LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
+ << sizeof(request) << ")";
+ goto fail;
+ }
+
+ if (request.packet_type != CrashPacketType::kCompletedDump) {
+ LOG(WARNING) << "unexpected crash packet type, expected kCompletedDump, received "
+ << uint32_t(request.packet_type);
+ goto fail;
+ }
+
+fail:
+ delete crash;
+
+ // If there's something queued up, let them proceed.
+ if (!queued_requests.empty()) {
+ Crash* next_crash = queued_requests.front();
+ queued_requests.pop_front();
+ dequeue_request(next_crash);
+ }
+}
+
+int main(int, char* []) {
+ tombstone_directory_fd = open(kTombstoneDirectory, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+ if (tombstone_directory_fd == -1) {
+ PLOG(FATAL) << "failed to open tombstone directory";
+ }
+
+ find_oldest_tombstone();
+
+ int intercept_socket = android_get_control_socket(kTombstonedInterceptSocketName);
+ int crash_socket = android_get_control_socket(kTombstonedCrashSocketName);
+
+ if (intercept_socket == -1 || crash_socket == -1) {
+ PLOG(FATAL) << "failed to get socket from init";
+ }
+
+ evutil_make_socket_nonblocking(intercept_socket);
+ evutil_make_socket_nonblocking(crash_socket);
+
+ event_base* base = event_base_new();
+ if (!base) {
+ LOG(FATAL) << "failed to create event_base";
+ }
+
+ intercept_manager = new InterceptManager(base, intercept_socket);
+
+ evconnlistener* listener =
+ evconnlistener_new(base, crash_accept_cb, nullptr, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+ if (!listener) {
+ LOG(FATAL) << "failed to create evconnlistener";
+ }
+
+ LOG(INFO) << "tombstoned successfully initialized";
+ event_base_dispatch(base);
+}
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
new file mode 100644
index 0000000..b8345ca
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -0,0 +1,10 @@
+service tombstoned /system/bin/tombstoned
+ user tombstoned
+ group system
+
+ # Don't start tombstoned until after the real /data is mounted.
+ class late_start
+
+ socket tombstoned_crash seqpacket 0666 system system
+ socket tombstoned_intercept seqpacket 0666 system system
+ writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
new file mode 100644
index 0000000..738abdf
--- /dev/null
+++ b/debuggerd/util.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "debuggerd/util.h"
+
+#include <sys/socket.h>
+
+#include <utility>
+
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+ssize_t send_fd(int sockfd, const void* data, size_t len, android::base::unique_fd fd) {
+ char cmsg_buf[CMSG_SPACE(sizeof(int))];
+
+ iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
+ msghdr msg = {
+ .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
+ };
+ auto cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
+
+ return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
+}
+
+ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
+ android::base::unique_fd* _Nullable out_fd) {
+ char cmsg_buf[CMSG_SPACE(sizeof(int))];
+
+ iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
+ msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsg_buf,
+ .msg_controllen = sizeof(cmsg_buf),
+ .msg_flags = 0,
+ };
+ auto cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+
+ ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
+ if (result == -1) {
+ return -1;
+ }
+
+ android::base::unique_fd fd;
+ bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
+ if (received_fd) {
+ fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
+ }
+
+ if ((msg.msg_flags & MSG_TRUNC) != 0) {
+ errno = EFBIG;
+ return -1;
+ } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ if (out_fd) {
+ *out_fd = std::move(fd);
+ } else if (received_fd) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ return result;
+}
+
+bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write) {
+ int pipefds[2];
+ if (pipe(pipefds) != 0) {
+ return false;
+ }
+ read->reset(pipefds[0]);
+ write->reset(pipefds[1]);
+ return true;
+}
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
deleted file mode 100644
index f5d6ec1..0000000
--- a/debuggerd/utility.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "utility.h"
-
-#include <errno.h>
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/ptrace.h>
-#include <sys/wait.h>
-
-#include <backtrace/Backtrace.h>
-#include <base/file.h>
-#include <base/stringprintf.h>
-#include <log/log.h>
-
-const int SLEEP_TIME_USEC = 50000; // 0.05 seconds
-const int MAX_TOTAL_SLEEP_USEC = 10000000; // 10 seconds
-
-// Whitelist output desired in the logcat output.
-bool is_allowed_in_logcat(enum logtype ltype) {
- if ((ltype == HEADER)
- || (ltype == REGISTERS)
- || (ltype == BACKTRACE)) {
- return true;
- }
- return false;
-}
-
-void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
- bool write_to_tombstone = (log->tfd != -1);
- bool write_to_logcat = is_allowed_in_logcat(ltype)
- && log->crashed_tid != -1
- && log->current_tid != -1
- && (log->crashed_tid == log->current_tid);
- bool write_to_activitymanager = (log->amfd != -1);
-
- char buf[512];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
-
- size_t len = strlen(buf);
- if (len <= 0) {
- return;
- }
-
- if (write_to_tombstone) {
- TEMP_FAILURE_RETRY(write(log->tfd, buf, len));
- }
-
- if (write_to_logcat) {
- __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, buf);
- if (write_to_activitymanager) {
- if (!android::base::WriteFully(log->amfd, buf, len)) {
- // timeout or other failure on write; stop informing the activity manager
- ALOGE("AM write failed: %s", strerror(errno));
- log->amfd = -1;
- }
- }
- }
-}
-
-int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed) {
- bool allow_dead_tid = false;
- for (;;) {
- int status;
- pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL | WNOHANG));
- if (n == -1) {
- ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
- break;
- } else if (n == tid) {
- if (WIFSTOPPED(status)) {
- return WSTOPSIG(status);
- } else {
- ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
- // This is the only circumstance under which we can allow a detach
- // to fail with ESRCH, which indicates the tid has exited.
- allow_dead_tid = true;
- break;
- }
- }
-
- if (*total_sleep_time_usec > MAX_TOTAL_SLEEP_USEC) {
- ALOGE("timed out waiting for stop signal: tid=%d", tid);
- break;
- }
-
- usleep(SLEEP_TIME_USEC);
- *total_sleep_time_usec += SLEEP_TIME_USEC;
- }
-
- if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
- if (allow_dead_tid && errno == ESRCH) {
- ALOGE("tid exited before attach completed: tid %d", tid);
- } else {
- *detach_failed = true;
- ALOGE("detach failed: tid %d, %s", tid, strerror(errno));
- }
- }
- return -1;
-}
-
-#define MEMORY_BYTES_TO_DUMP 256
-#define MEMORY_BYTES_PER_LINE 16
-
-void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) {
- std::string log_msg;
- va_list ap;
- va_start(ap, fmt);
- android::base::StringAppendV(&log_msg, fmt, ap);
- va_end(ap);
-
- // Align the address to sizeof(long) and start 32 bytes before the address.
- addr &= ~(sizeof(long) - 1);
- if (addr >= 4128) {
- addr -= 32;
- }
-
- // Don't bother if the address looks too low, or looks too high.
- if (addr < 4096 ||
-#if defined(__LP64__)
- addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) {
-#else
- addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) {
-#endif
- return;
- }
-
- _LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str());
-
- // Dump 256 bytes
- uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
- memset(data, 0, MEMORY_BYTES_TO_DUMP);
- size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
- if (bytes % sizeof(uintptr_t) != 0) {
- // This should never happen, but just in case.
- ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
- bytes &= ~(sizeof(uintptr_t) - 1);
- }
-
- uintptr_t start = 0;
- bool skip_2nd_read = false;
- if (bytes == 0) {
- // In this case, we might want to try another read at the beginning of
- // the next page only if it's within the amount of memory we would have
- // read.
- size_t page_size = sysconf(_SC_PAGE_SIZE);
- start = ((addr + (page_size - 1)) & ~(page_size - 1)) - addr;
- if (start == 0 || start >= MEMORY_BYTES_TO_DUMP) {
- skip_2nd_read = true;
- }
- }
-
- if (bytes < MEMORY_BYTES_TO_DUMP && !skip_2nd_read) {
- // Try to do one more read. This could happen if a read crosses a map,
- // but the maps do not have any break between them. Or it could happen
- // if reading from an unreadable map, but the read would cross back
- // into a readable map. Only requires one extra read because a map has
- // to contain at least one page, and the total number of bytes to dump
- // is smaller than a page.
- size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
- sizeof(data) - bytes - start);
- bytes += bytes2;
- if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
- // This should never happen, but we'll try and continue any way.
- ALOGE("Bytes after second read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
- bytes &= ~(sizeof(uintptr_t) - 1);
- }
- }
-
- // Dump the code around memory as:
- // addr contents ascii
- // 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q
- // 0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000 ......-..p......
- // On 32-bit machines, there are still 16 bytes per line but addresses and
- // words are of course presented differently.
- uintptr_t* data_ptr = data;
- size_t current = 0;
- size_t total_bytes = start + bytes;
- for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
- std::string logline;
- android::base::StringAppendF(&logline, " %" PRIPTR, addr);
-
- addr += MEMORY_BYTES_PER_LINE;
- std::string ascii;
- for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
- if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
- android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
-
- // Fill out the ascii string from the data.
- uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr);
- for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) {
- if (*ptr >= 0x20 && *ptr < 0x7f) {
- ascii += *ptr;
- } else {
- ascii += '.';
- }
- }
- data_ptr++;
- } else {
- logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
- ascii += std::string(sizeof(uintptr_t), '.');
- }
- current += sizeof(uintptr_t);
- }
- _LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str());
- }
-}
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
deleted file mode 100644
index 8bef192..0000000
--- a/debuggerd/utility.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/* system/debuggerd/utility.h
-**
-** Copyright 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.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef _DEBUGGERD_UTILITY_H
-#define _DEBUGGERD_UTILITY_H
-
-#include <stdbool.h>
-#include <sys/types.h>
-
-#include <backtrace/Backtrace.h>
-
-// Figure out the abi based on defined macros.
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#else
-#error "Unsupported ABI"
-#endif
-
-
-struct log_t{
- /* tombstone file descriptor */
- int tfd;
- /* Activity Manager socket file descriptor */
- int amfd;
- // The tid of the thread that crashed.
- pid_t crashed_tid;
- // The tid of the thread we are currently working with.
- pid_t current_tid;
- // logd daemon crash, can block asking for logcat data, allow suppression.
- bool should_retrieve_logcat;
-
- log_t()
- : tfd(-1), amfd(-1), crashed_tid(-1), current_tid(-1), should_retrieve_logcat(true) {}
-};
-
-// List of types of logs to simplify the logging decision in _LOG
-enum logtype {
- HEADER,
- THREAD,
- REGISTERS,
- FP_REGISTERS,
- BACKTRACE,
- MAPS,
- MEMORY,
- STACK,
- LOGS
-};
-
-// Log information onto the tombstone.
-void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
- __attribute__ ((format(printf, 3, 4)));
-
-int wait_for_sigstop(pid_t, int*, bool*);
-
-void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
-
-#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp
deleted file mode 100644
index fc86bc2..0000000
--- a/debuggerd/x86_64/machine.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "DEBUG"
-
-#include <errno.h>
-#include <stdint.h>
-#include <sys/ptrace.h>
-#include <string.h>
-#include <sys/user.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "machine.h"
-#include "utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
- struct user_regs_struct r;
- if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:");
-
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:");
-}
-
-void dump_registers(log_t* log, pid_t tid) {
- struct user_regs_struct r;
- if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n",
- r.rax, r.rbx, r.rcx, r.rdx);
- _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n",
- r.rsi, r.rdi);
- _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n",
- r.r8, r.r9, r.r10, r.r11);
- _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n",
- r.r12, r.r13, r.r14, r.r15);
- _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n",
- r.cs, r.ss);
- _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n",
- r.rip, r.rbp, r.rsp, r.eflags);
-}
diff --git a/fastboot/.clang-format b/fastboot/.clang-format
new file mode 100644
index 0000000..bcb8d8a
--- /dev/null
+++ b/fastboot/.clang-format
@@ -0,0 +1,15 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+ContinuationIndentWidth: 8
+ConstructorInitializerIndentWidth: 8
+AccessModifierOffset: -2
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index ce8e15f..5610cc0 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,70 +18,64 @@
include $(CLEAR_VARS)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
- $(LOCAL_PATH)/../../extras/ext4_utils \
- $(LOCAL_PATH)/../../extras/f2fs_utils
-LOCAL_SRC_FILES := protocol.cpp engine.cpp bootimg_utils.cpp fastboot.cpp util.cpp fs.cpp
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/../adb \
+ $(LOCAL_PATH)/../mkbootimg \
+ $(LOCAL_PATH)/../../extras/f2fs_utils \
+
+LOCAL_SRC_FILES := \
+ bootimg_utils.cpp \
+ engine.cpp \
+ fastboot.cpp \
+ fs.cpp\
+ protocol.cpp \
+ socket.cpp \
+ tcp.cpp \
+ udp.cpp \
+ util.cpp \
+
LOCAL_MODULE := fastboot
LOCAL_MODULE_TAGS := debug
-LOCAL_CONLYFLAGS += -std=gnu99
+LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
-ifeq ($(HOST_OS),linux)
- LOCAL_SRC_FILES += usb_linux.cpp util_linux.cpp
-endif
+LOCAL_SRC_FILES_linux := usb_linux.cpp
+LOCAL_STATIC_LIBRARIES_linux := libselinux
-ifeq ($(HOST_OS),darwin)
- LOCAL_SRC_FILES += usb_osx.cpp util_osx.cpp
- LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
- LOCAL_CFLAGS += -Wno-unused-parameter
-endif
+LOCAL_SRC_FILES_darwin := usb_osx.cpp
+LOCAL_STATIC_LIBRARIES_darwin := libselinux
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
-ifeq ($(HOST_OS),windows)
- LOCAL_SRC_FILES += usb_windows.cpp util_windows.cpp
- EXTRA_STATIC_LIBS := AdbWinApi
- ifneq ($(strip $(USE_CYGWIN)),)
- # Pure cygwin case
- LOCAL_LDLIBS += -lpthread
- endif
- ifneq ($(strip $(USE_MINGW)),)
- # MinGW under Linux case
- LOCAL_LDLIBS += -lws2_32
- USE_SYSDEPS_WIN32 := 1
- endif
- LOCAL_C_INCLUDES += development/host/windows/usb/api
-endif
+LOCAL_SRC_FILES_windows := usb_windows.cpp
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi
+LOCAL_LDLIBS_windows := -lws2_32
+LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
LOCAL_STATIC_LIBRARIES := \
- $(EXTRA_STATIC_LIBS) \
- libziparchive-host \
- libext4_utils_host \
- libsparse_host \
+ libziparchive \
+ libext4_utils \
+ libsparse \
libutils \
liblog \
libz \
- libbase
+ libdiagnose_usb \
+ libbase \
+ libcutils \
+ libgtest_host \
-ifneq ($(HOST_OS),windows)
-LOCAL_STATIC_LIBRARIES += libselinux
-endif # HOST_OS != windows
-
-ifeq ($(HOST_OS),linux)
# libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
-LOCAL_CFLAGS += -DUSE_F2FS
-LOCAL_LDFLAGS += -ldl -rdynamic -Wl,-rpath,.
-LOCAL_REQUIRED_MODULES := libf2fs_fmt_host_dyn
+LOCAL_CFLAGS_linux := -DUSE_F2FS
+LOCAL_LDFLAGS_linux := -ldl -rdynamic -Wl,-rpath,.
+LOCAL_REQUIRED_MODULES_linux := libf2fs_fmt_host_dyn
# The following libf2fs_* are from system/extras/f2fs_utils,
# and do not use code in external/f2fs-tools.
-LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
-endif
+LOCAL_STATIC_LIBRARIES_linux += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
-# libc++ not available on windows yet
-ifneq ($(HOST_OS),windows)
- LOCAL_CXX_STL := libc++_static
-endif
+LOCAL_CXX_STL := libc++_static
# Don't add anything here, we don't want additional shared dependencies
# on the host fastboot tool, and shared libraries that link against libc++
@@ -94,7 +88,11 @@
ifeq ($(HOST_OS),linux)
my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX)
endif
-$(call dist-for-goals,dist_files sdk,$(my_dist_files))
+$(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))
+endif
my_dist_files :=
ifeq ($(HOST_OS),linux)
@@ -102,9 +100,33 @@
LOCAL_SRC_FILES := usbtest.cpp usb_linux.cpp util.cpp
LOCAL_MODULE := usbtest
LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := libbase
include $(BUILD_HOST_EXECUTABLE)
endif
-ifeq ($(HOST_OS),windows)
-$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
-endif
+# fastboot_test
+# =========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fastboot_test
+LOCAL_MODULE_HOST_OS := darwin linux windows
+
+LOCAL_SRC_FILES := \
+ socket.cpp \
+ socket_mock.cpp \
+ socket_test.cpp \
+ tcp.cpp \
+ tcp_test.cpp \
+ udp.cpp \
+ udp_test.cpp \
+
+LOCAL_STATIC_LIBRARIES := libbase libcutils
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
+
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
+
+LOCAL_LDLIBS_windows := -lws2_32
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/fastboot/README.md b/fastboot/README.md
new file mode 100644
index 0000000..022d34b
--- /dev/null
+++ b/fastboot/README.md
@@ -0,0 +1,453 @@
+Fastboot
+--------
+
+The fastboot protocol is a mechanism for communicating with bootloaders
+over USB or ethernet. It is designed to be very straightforward to implement,
+to allow it to be used across a wide range of devices and from hosts running
+Linux, macOS, or Windows.
+
+
+## Basic Requirements
+
+* USB
+ * Two bulk endpoints (in, out) are required
+ * Max packet size must be 64 bytes for full-speed, 512 bytes for
+ high-speed and 1024 bytes for Super Speed USB.
+ * The protocol is entirely host-driven and synchronous (unlike the
+ multi-channel, bi-directional, asynchronous ADB protocol)
+
+* TCP or UDP
+ * Device must be reachable via IP.
+ * Device will act as the server, fastboot will be the client.
+ * Fastboot data is wrapped in a simple protocol; see below for details.
+
+
+## Transport and Framing
+
+1. Host sends a command, which is an ascii string in a single
+ packet no greater than 64 bytes.
+
+2. Client response with a single packet no greater than 64 bytes.
+ The first four bytes of the response are "OKAY", "FAIL", "DATA",
+ or "INFO". Additional bytes may contain an (ascii) informative
+ message.
+
+ a. INFO -> the remaining 60 bytes are an informative message
+ (providing progress or diagnostic messages). They should
+ be displayed and then step #2 repeats
+
+ b. FAIL -> the requested command failed. The remaining 60 bytes
+ of the response (if present) provide a textual failure message
+ to present to the user. Stop.
+
+ c. OKAY -> the requested command completed successfully. Go to #5
+
+ d. DATA -> the requested command is ready for the data phase.
+ A DATA response packet will be 12 bytes long, in the form of
+ DATA00000000 where the 8 digit hexadecimal number represents
+ the total data size to transfer.
+
+3. Data phase. Depending on the command, the host or client will
+ send the indicated amount of data. Short packets are always
+ acceptable and zero-length packets are ignored. This phase continues
+ until the client has sent or received the number of bytes indicated
+ in the "DATA" response above.
+
+4. Client responds with a single packet no greater than 64 bytes.
+ The first four bytes of the response are "OKAY", "FAIL", or "INFO".
+ Similar to #2:
+
+ a. INFO -> display the remaining 60 bytes and return to #4
+
+ b. FAIL -> display the remaining 60 bytes (if present) as a failure
+ reason and consider the command failed. Stop.
+
+ c. OKAY -> success. Go to #5
+
+5. Success. Stop.
+
+
+## Example Session
+
+ Host: "getvar:version" request version variable
+
+ Client: "OKAY0.4" return version "0.4"
+
+ Host: "getvar:nonexistant" request some undefined variable
+
+ Client: "FAILUnknown variable" getvar failure; see getvar details below
+
+ Host: "download:00001234" request to send 0x1234 bytes of data
+
+ Client: "DATA00001234" ready to accept data
+
+ Host: < 0x1234 bytes > send data
+
+ Client: "OKAY" success
+
+ Host: "flash:bootloader" request to flash the data to the bootloader
+
+ Client: "INFOerasing flash" indicate status / progress
+ "INFOwriting flash"
+ "OKAY" indicate success
+
+ Host: "powerdown" send a command
+
+ Client: "FAILunknown command" indicate failure
+
+
+## Command Reference
+
+* Command parameters are indicated by printf-style escape sequences.
+
+* Commands are ascii strings and sent without the quotes (which are
+ for illustration only here) and without a trailing 0 byte.
+
+* Commands that begin with a lowercase letter are reserved for this
+ specification. OEM-specific commands should not begin with a
+ lowercase letter, to prevent incompatibilities with future specs.
+
+The various currently defined commands are:
+
+ getvar:%s Read a config/version variable from the bootloader.
+ The variable contents will be returned after the
+ OKAY response. If the variable is unknown, the bootloader
+ should return a FAIL response, optionally with an error
+ message.
+
+ Previous versions of this document indicated that getvar
+ should return an empty OKAY response for unknown
+ variables, so older devices might exhibit this behavior,
+ but new implementations should return FAIL instead.
+
+ download:%08x Write data to memory which will be later used
+ by "boot", "ramdisk", "flash", etc. The client
+ will reply with "DATA%08x" if it has enough
+ space in RAM or "FAIL" if not. The size of
+ the download is remembered.
+
+ verify:%08x Send a digital signature to verify the downloaded
+ data. Required if the bootloader is "secure"
+ otherwise "flash" and "boot" will be ignored.
+
+ flash:%s Write the previously downloaded image to the
+ named partition (if possible).
+
+ erase:%s Erase the indicated partition (clear to 0xFFs)
+
+ boot The previously downloaded data is a boot.img
+ and should be booted according to the normal
+ procedure for a boot.img
+
+ continue Continue booting as normal (if possible)
+
+ reboot Reboot the device.
+
+ reboot-bootloader
+ Reboot back into the bootloader.
+ Useful for upgrade processes that require upgrading
+ the bootloader and then upgrading other partitions
+ using the new bootloader.
+
+ powerdown Power off the device.
+
+
+
+## Client Variables
+
+The "getvar:%s" command is used to read client variables which
+represent various information about the device and the software
+on it.
+
+The various currently defined names are:
+
+ version Version of FastBoot protocol supported.
+ It should be "0.4" for this document.
+
+ version-bootloader Version string for the Bootloader.
+
+ version-baseband Version string of the Baseband Software
+
+ product Name of the product
+
+ serialno Product serial number
+
+ secure If the value is "yes", this is a secure
+ bootloader requiring a signature before
+ it will install or boot images.
+
+Names starting with a lowercase character are reserved by this
+specification. OEM-specific names should not start with lowercase
+characters.
+
+
+## TCP Protocol v1
+
+The TCP protocol is designed to be a simple way to use the fastboot protocol
+over ethernet if USB is not available.
+
+The device will open a TCP server on port 5554 and wait for a fastboot client
+to connect.
+
+### Handshake
+Upon connecting, both sides will send a 4-byte handshake message to ensure they
+are speaking the same protocol. This consists of the ASCII characters "FB"
+followed by a 2-digit base-10 ASCII version number. For example, the version 1
+handshake message will be [FB01].
+
+If either side detects a malformed handshake, it should disconnect.
+
+The protocol version to use must be the minimum of the versions sent by each
+side; if either side cannot speak this protocol version, it should disconnect.
+
+### Fastboot Data
+Once the handshake is complete, fastboot data will be sent as follows:
+
+ [data_size][data]
+
+Where data\_size is an unsigned 8-byte big-endian binary value, and data is the
+fastboot packet. The 8-byte length is intended to provide future-proofing even
+though currently fastboot packets have a 4-byte maximum length.
+
+### Example
+In this example the fastboot host queries the device for two variables,
+"version" and "none".
+
+ Host <connect to the device on port 5555>
+ Host FB01
+ Device FB01
+ Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version
+ Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4
+ Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
+ Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x14]FAILUnknown variable
+ Host <disconnect>
+
+
+## UDP Protocol v1
+
+The UDP protocol is more complex than TCP since we must implement reliability
+to ensure no packets are lost, but the general concept of wrapping the fastboot
+protocol is the same.
+
+Overview:
+ 1. As with TCP, the device will listen on UDP port 5554.
+ 2. Maximum UDP packet size is negotiated during initialization.
+ 3. The host drives all communication; the device may only send a packet as a
+ response to a host packet.
+ 4. If the host does not receive a response in 500ms it will re-transmit.
+
+### UDP Packet format
+
+ +----------+----+-------+-------+--------------------+
+ | Byte # | 0 | 1 | 2 - 3 | 4+ |
+ +----------+----+-------+-------+--------------------+
+ | Contents | ID | Flags | Seq # | Data |
+ +----------+----+-------+-------+--------------------+
+
+ ID Packet ID:
+ 0x00: Error.
+ 0x01: Query.
+ 0x02: Initialization.
+ 0x03: Fastboot.
+
+ Packet types are described in more detail below.
+
+ Flags Packet flags: 0 0 0 0 0 0 0 C
+ C=1 indicates a continuation packet; the data is too large and will
+ continue in the next packet.
+
+ Remaining bits are reserved for future use and must be set to 0.
+
+ Seq # 2-byte packet sequence number (big-endian). The host will increment
+ this by 1 with each new packet, and the device must provide the
+ corresponding sequence number in the response packets.
+
+ Data Packet data, not present in all packets.
+
+### Packet Types
+
+ Query
+ The host sends a query packet once on startup to sync with the device.
+ The host will not know the current sequence number, so the device must
+ respond to all query packets regardless of sequence number.
+
+ The response data field should contain a 2-byte big-endian value
+ giving the next expected sequence number.
+
+ Init
+ The host sends an init packet once the query response is returned. The
+ device must abort any in-progress operation and prepare for a new
+ fastboot session. This message is meant to allow recovery if a
+ previous session failed, e.g. due to network error or user Ctrl+C.
+
+ The data field contains two big-endian 2-byte values, a protocol
+ version and the max UDP packet size (including the 4-byte header).
+ Both the host and device will send these values, and in each case
+ the minimum of the sent values must be used.
+
+ Fastboot
+ These packets wrap the fastboot protocol. To write, the host will
+ send a packet with fastboot data, and the device will reply with an
+ empty packet as an ACK. To read, the host will send an empty packet,
+ and the device will reply with fastboot data. The device may not give
+ any data in the ACK packet.
+
+ Error
+ The device may respond to any packet with an error packet to indicate
+ a UDP protocol error. The data field should contain an ASCII string
+ describing the error. This is the only case where a device is allowed
+ to return a packet ID other than the one sent by the host.
+
+### Packet Size
+The maximum packet size is negotiated by the host and device in the Init packet.
+Devices must support at least 512-byte packets, but packet size has a direct
+correlation with download speed, so devices are strongly suggested to support at
+least 1024-byte packets. On a local network with 0.5ms round-trip time this will
+provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
+less.
+
+Query and Initialization packets, which are sent before size negotiation is
+complete, must always be 512 bytes or less.
+
+### Packet Re-Transmission
+The host will re-transmit any packet that does not receive a response. The
+requirement of exactly one device response packet per host packet is how we
+achieve reliability and in-order delivery of packets.
+
+For simplicity of implementation, there is no windowing of multiple
+unacknowledged packets in this version of the protocol. The host will continue
+to send the same packet until a response is received. Windowing functionality
+may be implemented in future versions if necessary to increase performance.
+
+The first Query packet will only be attempted a small number of times, but
+subsequent packets will attempt to retransmit for at least 1 minute before
+giving up. This means a device may safely ignore host UDP packets for up to 1
+minute during long operations, e.g. writing to flash.
+
+### Continuation Packets
+Any packet may set the continuation flag to indicate that the data is
+incomplete. Large data such as downloading an image may require many
+continuation packets. The receiver should respond to a continuation packet with
+an empty packet to acknowledge receipt. See examples below.
+
+### Summary
+The host starts with a Query packet, then an Initialization packet, after
+which only Fastboot packets are sent. Fastboot packets may contain data from
+the host for writes, or from the device for reads, but not both.
+
+Given a next expected sequence number S and a received packet P, the device
+behavior should be:
+
+ if P is a Query packet:
+ * respond with a Query packet with S in the data field
+ else if P has sequence == S:
+ * process P and take any required action
+ * create a response packet R with the same ID and sequence as P, containing
+ any response data required.
+ * transmit R and save it in case of re-transmission
+ * increment S
+ else if P has sequence == S - 1:
+ * re-transmit the saved response packet R from above
+ else:
+ * ignore the packet
+
+### Examples
+
+In the examples below, S indicates the starting client sequence number.
+
+ Host Client
+ ======================================================================
+ [Initialization, S = 0x55AA]
+ [Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
+ [Resulting values to use: version = 1, max packet size = 1024]
+ ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x01 0x00 0x00 0x00
+ 0x01 0x00 0x00 0x00 0x55 0xAA
+ 0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
+ 0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
+
+ ----------------------------------------------------------------------
+ [fastboot "getvar" commands, S = 0x0001]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x01 getvar:version
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x02 OKAY0.4
+ 0x03 0x00 0x00 0x03 getvar:none
+ 0x03 0x00 0x00 0x03
+ 0x03 0x00 0x00 0x04
+ 0x03 0x00 0x00 0x04 FAILUnknown var
+
+ ----------------------------------------------------------------------
+ [fastboot "INFO" responses, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x00 <command>
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 INFOWait1
+ 0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x02 INFOWait2
+ 0x03 0x00 0x00 0x03
+ 0x03 0x00 0x00 0x03 OKAY
+
+ ----------------------------------------------------------------------
+ [Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
+ ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0xFF 0xFF download:0000834
+ 0x03 0x00 0xFF 0xFF
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x00 DATA0000834
+ 0x03 0x01 0x00 0x01 <1020 bytes>
+ 0x03 0x00 0x00 0x01
+ 0x03 0x01 0x00 0x02 <1020 bytes>
+ 0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x03 <60 bytes>
+ 0x03 0x00 0x00 0x03
+ 0x03 0x00 0x00 0x04
+ 0x03 0x00 0x00 0x04 OKAY
+
+ ----------------------------------------------------------------------
+ [Unknown ID error, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x10 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 <error message>
+
+ ----------------------------------------------------------------------
+ [Host packet loss and retransmission, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x00 getvar:version [lost]
+ 0x03 0x00 0x00 0x00 getvar:version [lost]
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+
+ ----------------------------------------------------------------------
+ [Client packet loss and retransmission, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00 [lost]
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00 [lost]
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+
+ ----------------------------------------------------------------------
+ [Host packet delayed, S = 0x0000]
+ ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+ ----------------------------------------------------------------------
+ 0x03 0x00 0x00 0x00 getvar:version [delayed]
+ 0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+ 0x03 0x00 0x00 0x00 getvar:version [arrives late with old seq#, is ignored]
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index d8905a6..c1028ef 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -32,32 +32,27 @@
#include <stdlib.h>
#include <string.h>
-void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline)
+void bootimg_set_cmdline(boot_img_hdr* h, const char* cmdline)
{
strcpy((char*) h->cmdline, cmdline);
}
-boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
- void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
- void *second, unsigned second_size, unsigned second_offset,
- unsigned page_size, unsigned base, unsigned tags_offset,
- unsigned *bootimg_size)
+boot_img_hdr* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset,
+ void* ramdisk, int64_t ramdisk_size, off_t ramdisk_offset,
+ void* second, int64_t second_size, off_t second_offset,
+ size_t page_size, size_t base, off_t tags_offset,
+ int64_t* bootimg_size)
{
- unsigned kernel_actual;
- unsigned ramdisk_actual;
- unsigned second_actual;
- unsigned page_mask;
+ size_t page_mask = page_size - 1;
- page_mask = page_size - 1;
-
- kernel_actual = (kernel_size + page_mask) & (~page_mask);
- ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
- second_actual = (second_size + page_mask) & (~page_mask);
+ int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
+ int64_t ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
+ int64_t second_actual = (second_size + page_mask) & (~page_mask);
*bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual;
boot_img_hdr* hdr = reinterpret_cast<boot_img_hdr*>(calloc(*bootimg_size, 1));
- if (hdr == 0) {
+ if (hdr == nullptr) {
return hdr;
}
@@ -74,12 +69,9 @@
hdr->page_size = page_size;
+ memcpy(hdr->magic + page_size, kernel, kernel_size);
+ memcpy(hdr->magic + page_size + kernel_actual, ramdisk, ramdisk_size);
+ memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual, second, second_size);
- memcpy(hdr->magic + page_size,
- kernel, kernel_size);
- memcpy(hdr->magic + page_size + kernel_actual,
- ramdisk, ramdisk_size);
- memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual,
- second, second_size);
return hdr;
}
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index b1a86cd..fcc8662 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -30,20 +30,14 @@
#define _FASTBOOT_BOOTIMG_UTILS_H_
#include <bootimg.h>
+#include <inttypes.h>
+#include <sys/types.h>
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline);
-boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
- void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
- void *second, unsigned second_size, unsigned second_offset,
- unsigned page_size, unsigned base, unsigned tags_offset,
- unsigned *bootimg_size);
-
-#if defined(__cplusplus)
-}
-#endif
+void bootimg_set_cmdline(boot_img_hdr* h, const char* cmdline);
+boot_img_hdr* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset,
+ void* ramdisk, int64_t ramdisk_size, off_t ramdisk_offset,
+ void* second, int64_t second_size, off_t second_offset,
+ size_t page_size, size_t base, off_t tags_offset,
+ int64_t* bootimg_size);
#endif
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 66b8140..728dcb8 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -31,7 +31,6 @@
#include <errno.h>
#include <stdarg.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -39,14 +38,6 @@
#include <sys/types.h>
#include <unistd.h>
-#ifdef USE_MINGW
-#include <fcntl.h>
-#else
-#include <sys/mman.h>
-#endif
-
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
-
#define OP_DOWNLOAD 1
#define OP_COMMAND 2
#define OP_QUERY 3
@@ -58,15 +49,17 @@
#define CMD_SIZE 64
-struct Action
-{
+struct Action {
unsigned op;
- Action *next;
+ Action* next;
char cmd[CMD_SIZE];
- const char *prod;
- void *data;
- unsigned size;
+ const char* prod;
+ void* data;
+
+ // The protocol only supports 32-bit sizes, so you'll have to break
+ // anything larger into chunks.
+ uint32_t size;
const char *msg;
int (*func)(Action* a, int status, const char* resp);
@@ -80,41 +73,17 @@
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
-{
- char cmd[CMD_SIZE] = "getvar:";
- int getvar_len = strlen(cmd);
- va_list args;
+bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
+ std::string cmd = "getvar:";
+ cmd += key;
- response[FB_RESPONSE_SZ] = '\0';
- va_start(args, fmt);
- vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args);
- va_end(args);
- cmd[CMD_SIZE - 1] = '\0';
- return fb_command_response(usb, cmd, response);
-}
-
-
-/* Return true if this partition is supported by the fastboot format command.
- * It is also used to determine if we should first erase a partition before
- * flashing it with an ext4 filesystem. See needs_erase()
- *
- * Not all devices report the filesystem type, so don't report any errors,
- * just return false.
- */
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override)
-{
- char fs_type[FB_RESPONSE_SZ + 1] = {0,};
- int status;
-
- if (type_override) {
- return !!fs_get_generator(type_override);
+ char buf[FB_RESPONSE_SZ + 1];
+ memset(buf, 0, sizeof(buf));
+ if (fb_command_response(transport, cmd.c_str(), buf)) {
+ return false;
}
- status = fb_getvar(usb, fs_type, "partition-type:%s", partition);
- if (status) {
- return 0;
- }
- return !!fs_get_generator(fs_type);
+ *value = buf;
+ return true;
}
static int cb_default(Action* a, int status, const char* resp) {
@@ -159,6 +128,13 @@
return a;
}
+void fb_set_active(const char *slot)
+{
+ Action *a;
+ a = queue_action(OP_COMMAND, "set_active:%s", slot);
+ a->msg = mkmsg("Setting current slot to '%s'", slot);
+}
+
void fb_queue_erase(const char *ptn)
{
Action *a;
@@ -179,17 +155,17 @@
a->msg = mkmsg("writing '%s'", ptn);
}
-void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz)
-{
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, unsigned sz, size_t current,
+ size_t total) {
Action *a;
a = queue_action(OP_DOWNLOAD_SPARSE, "");
a->data = s;
a->size = 0;
- a->msg = mkmsg("sending sparse '%s' (%d KB)", ptn, sz / 1024);
+ a->msg = mkmsg("sending sparse '%s' %zu/%zu (%d KB)", ptn, current, total, sz / 1024);
a = queue_action(OP_COMMAND, "flash:%s", ptn);
- a->msg = mkmsg("writing '%s'", ptn);
+ a->msg = mkmsg("writing '%s' %zu/%zu", ptn, current, total);
}
static int match(const char* str, const char** value, unsigned count) {
@@ -267,7 +243,7 @@
}
void fb_queue_require(const char *prod, const char *var,
- int invert, unsigned nvalues, const char **value)
+ bool invert, size_t nvalues, const char **value)
{
Action *a;
a = queue_action(OP_QUERY, "getvar:%s", var);
@@ -352,7 +328,7 @@
queue_action(OP_WAIT_FOR_DISCONNECT, "");
}
-int fb_execute_queue(usb_handle *usb)
+int fb_execute_queue(Transport* transport)
{
Action *a;
char resp[FB_RESPONSE_SZ+1];
@@ -372,25 +348,25 @@
fprintf(stderr,"%s...\n",a->msg);
}
if (a->op == OP_DOWNLOAD) {
- status = fb_download_data(usb, a->data, a->size);
- status = a->func(a, status, status ? fb_get_error() : "");
+ status = fb_download_data(transport, a->data, a->size);
+ status = a->func(a, status, status ? fb_get_error().c_str() : "");
if (status) break;
} else if (a->op == OP_COMMAND) {
- status = fb_command(usb, a->cmd);
- status = a->func(a, status, status ? fb_get_error() : "");
+ status = fb_command(transport, a->cmd);
+ status = a->func(a, status, status ? fb_get_error().c_str() : "");
if (status) break;
} else if (a->op == OP_QUERY) {
- status = fb_command_response(usb, a->cmd, resp);
- status = a->func(a, status, status ? fb_get_error() : resp);
+ status = fb_command_response(transport, a->cmd, resp);
+ status = a->func(a, status, status ? fb_get_error().c_str() : resp);
if (status) break;
} else if (a->op == OP_NOTICE) {
fprintf(stderr,"%s\n",(char*)a->data);
} else if (a->op == OP_DOWNLOAD_SPARSE) {
- status = fb_download_data_sparse(usb, reinterpret_cast<sparse_file*>(a->data));
- status = a->func(a, status, status ? fb_get_error() : "");
+ status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
+ status = a->func(a, status, status ? fb_get_error().c_str() : "");
if (status) break;
} else if (a->op == OP_WAIT_FOR_DISCONNECT) {
- usb_wait_for_disconnect(usb);
+ transport->WaitForDisconnect();
} else {
die("bogus action");
}
@@ -399,8 +375,3 @@
fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
return status;
}
-
-int fb_queue_is_empty(void)
-{
- return (action_list == nullptr);
-}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5d7b151..e7f1a07 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -34,7 +34,6 @@
#include <getopt.h>
#include <inttypes.h>
#include <limits.h>
-#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -44,35 +43,52 @@
#include <sys/types.h>
#include <unistd.h>
+#include <chrono>
+#include <functional>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/parseint.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
#include "bootimg_utils.h"
+#include "diagnose_usb.h"
#include "fastboot.h"
#include "fs.h"
+#include "tcp.h"
+#include "transport.h"
+#include "udp.h"
+#include "usb.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
-
char cur_product[FB_RESPONSE_SZ + 1];
-static const char *serial = 0;
-static const char *product = 0;
-static const char *cmdline = 0;
+static const char* serial = nullptr;
+static const char* product = nullptr;
+static const char* cmdline = nullptr;
static unsigned short vendor_id = 0;
static int long_listing = 0;
static int64_t sparse_limit = -1;
static int64_t target_sparse_limit = -1;
-unsigned page_size = 2048;
-unsigned base_addr = 0x10000000;
-unsigned kernel_offset = 0x00008000;
-unsigned ramdisk_offset = 0x01000000;
-unsigned second_offset = 0x00f00000;
-unsigned tags_offset = 0x00000100;
+static unsigned page_size = 2048;
+static unsigned base_addr = 0x10000000;
+static unsigned kernel_offset = 0x00008000;
+static unsigned ramdisk_offset = 0x01000000;
+static unsigned second_offset = 0x00f00000;
+static unsigned tags_offset = 0x00000100;
+
+static const std::string convert_fbe_marker_filename("convert_fbe");
enum fb_buffer_type {
FB_BUFFER,
@@ -81,29 +97,46 @@
struct fastboot_buffer {
enum fb_buffer_type type;
- void *data;
- unsigned int sz;
+ void* data;
+ int64_t sz;
};
static struct {
- char img_name[13];
- char sig_name[13];
+ char img_name[17];
+ char sig_name[17];
char part_name[9];
bool is_optional;
+ bool is_secondary;
} images[] = {
- {"boot.img", "boot.sig", "boot", false},
- {"recovery.img", "recovery.sig", "recovery", true},
- {"system.img", "system.sig", "system", false},
- {"vendor.img", "vendor.sig", "vendor", true},
+ {"boot.img", "boot.sig", "boot", false, false},
+ {"boot_other.img", "boot.sig", "boot", true, true},
+ {"recovery.img", "recovery.sig", "recovery", true, false},
+ {"system.img", "system.sig", "system", false, false},
+ {"system_other.img", "system.sig", "system", true, true},
+ {"vendor.img", "vendor.sig", "vendor", true, false},
+ {"vendor_other.img", "vendor.sig", "vendor", true, true},
};
-char *find_item(const char *item, const char *product)
-{
- char *dir;
- const char *fn;
- char path[PATH_MAX + 128];
+static std::string find_item_given_name(const char* img_name, const char* product) {
+ if(product) {
+ std::string path = android::base::GetExecutablePath();
+ path.erase(path.find_last_of('/'));
+ return android::base::StringPrintf("%s/../../../target/product/%s/%s",
+ path.c_str(), product, img_name);
+ }
- if(!strcmp(item,"boot")) {
+ char *dir = getenv("ANDROID_PRODUCT_OUT");
+ if (dir == nullptr || dir[0] == '\0') {
+ die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
+ }
+
+ return android::base::StringPrintf("%s/%s", dir, img_name);
+}
+
+std::string find_item(const char* item, const char* product) {
+ const char *fn;
+
+ if (!strcmp(item,"boot")) {
fn = "boot.img";
} else if(!strcmp(item,"recovery")) {
fn = "recovery.img";
@@ -119,56 +152,32 @@
fn = "android-info.txt";
} else {
fprintf(stderr,"unknown partition '%s'\n", item);
- return 0;
+ return "";
}
- if(product) {
- get_my_path(path);
- sprintf(path + strlen(path),
- "../../../target/product/%s/%s", product, fn);
- return strdup(path);
- }
-
- dir = getenv("ANDROID_PRODUCT_OUT");
- if((dir == 0) || (dir[0] == 0)) {
- die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
- return 0;
- }
-
- sprintf(path, "%s/%s", dir, fn);
- return strdup(path);
+ return find_item_given_name(fn, product);
}
-static int64_t file_size(int fd)
-{
- struct stat st;
- int ret;
-
- ret = fstat(fd, &st);
-
- return ret ? -1 : st.st_size;
+static int64_t get_file_size(int fd) {
+ struct stat sb;
+ return fstat(fd, &sb) == -1 ? -1 : sb.st_size;
}
-static void *load_fd(int fd, unsigned *_sz)
-{
- char *data;
- int sz;
+static void* load_fd(int fd, int64_t* sz) {
int errno_tmp;
+ char* data = nullptr;
- data = 0;
-
- sz = file_size(fd);
- if (sz < 0) {
+ *sz = get_file_size(fd);
+ if (*sz < 0) {
goto oops;
}
- data = (char*) malloc(sz);
- if(data == 0) goto oops;
+ data = (char*) malloc(*sz);
+ if (data == nullptr) goto oops;
- if(read(fd, data, sz) != sz) goto oops;
+ if(read(fd, data, *sz) != *sz) goto oops;
close(fd);
- if(_sz) *_sz = sz;
return data;
oops:
@@ -179,19 +188,15 @@
return 0;
}
-static void *load_file(const char *fn, unsigned *_sz)
-{
- int fd;
-
- fd = open(fn, O_RDONLY | O_BINARY);
- if(fd < 0) return 0;
-
- return load_fd(fd, _sz);
+static void* load_file(const std::string& path, int64_t* sz) {
+ int fd = open(path.c_str(), O_RDONLY | O_BINARY);
+ if (fd == -1) return nullptr;
+ return load_fd(fd, sz);
}
-int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
+static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
// Require a matching vendor id if the user specified one with -i.
- if (vendor_id != 0 && info->dev_vendor != vendor_id) {
+ if (vendor_id != 0 && info->dev_vendor != vendor_id) {
return -1;
}
@@ -206,69 +211,123 @@
return 0;
}
-int match_fastboot(usb_ifc_info *info)
-{
+static int match_fastboot(usb_ifc_info* info) {
return match_fastboot_with_serial(info, serial);
}
-int list_devices_callback(usb_ifc_info *info)
-{
- if (match_fastboot_with_serial(info, NULL) == 0) {
- const char* serial = info->serial_number;
+static int list_devices_callback(usb_ifc_info* info) {
+ if (match_fastboot_with_serial(info, nullptr) == 0) {
+ std::string serial = info->serial_number;
if (!info->writable) {
- serial = "no permissions"; // like "adb devices"
+ serial = UsbNoPermissionsShortHelpText();
}
if (!serial[0]) {
serial = "????????????";
}
// output compatible with "adb devices"
if (!long_listing) {
- printf("%s\tfastboot\n", serial);
- } else if (strcmp("", info->device_path) == 0) {
- printf("%-22s fastboot\n", serial);
+ printf("%s\tfastboot", serial.c_str());
} else {
- printf("%-22s fastboot %s\n", serial, info->device_path);
+ printf("%-22s fastboot", serial.c_str());
+ if (strlen(info->device_path) > 0) printf(" %s", info->device_path);
}
+ putchar('\n');
}
return -1;
}
-usb_handle *open_device(void)
-{
- static usb_handle *usb = 0;
- int announce = 1;
+// Opens a new Transport connected to a device. If |serial| is non-null it will be used to identify
+// a specific device, otherwise the first USB device found will be used.
+//
+// If |serial| is non-null but invalid, this prints an error message to stderr and returns nullptr.
+// Otherwise it blocks until the target is available.
+//
+// The returned Transport is a singleton, so multiple calls to this function will return the same
+// object, and the caller should not attempt to delete the returned Transport.
+static Transport* open_device() {
+ static Transport* transport = nullptr;
+ bool announce = true;
- if(usb) return usb;
+ if (transport != nullptr) {
+ return transport;
+ }
- for(;;) {
- usb = usb_open(match_fastboot);
- if(usb) return usb;
- if(announce) {
- announce = 0;
- fprintf(stderr,"< waiting for device >\n");
+ Socket::Protocol protocol = Socket::Protocol::kTcp;
+ std::string host;
+ int port = 0;
+ if (serial != nullptr) {
+ const char* net_address = nullptr;
+
+ if (android::base::StartsWith(serial, "tcp:")) {
+ protocol = Socket::Protocol::kTcp;
+ port = tcp::kDefaultPort;
+ net_address = serial + strlen("tcp:");
+ } else if (android::base::StartsWith(serial, "udp:")) {
+ protocol = Socket::Protocol::kUdp;
+ port = udp::kDefaultPort;
+ net_address = serial + strlen("udp:");
}
- usleep(1000);
+
+ if (net_address != nullptr) {
+ std::string error;
+ if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
+ fprintf(stderr, "error: Invalid network address '%s': %s\n", net_address,
+ error.c_str());
+ return nullptr;
+ }
+ }
+ }
+
+ while (true) {
+ if (!host.empty()) {
+ std::string error;
+ if (protocol == Socket::Protocol::kTcp) {
+ transport = tcp::Connect(host, port, &error).release();
+ } else if (protocol == Socket::Protocol::kUdp) {
+ transport = udp::Connect(host, port, &error).release();
+ }
+
+ if (transport == nullptr && announce) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ }
+ } else {
+ transport = usb_open(match_fastboot);
+ }
+
+ if (transport != nullptr) {
+ return transport;
+ }
+
+ if (announce) {
+ announce = false;
+ fprintf(stderr, "< waiting for %s >\n", serial ? serial : "any device");
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
-void list_devices(void) {
+static void list_devices() {
// We don't actually open a USB device here,
// just getting our callback called so we can
// list all the connected devices.
usb_open(list_devices_callback);
}
-void usage(void)
-{
+static void usage() {
fprintf(stderr,
/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
"usage: fastboot [ <option> ] <command>\n"
"\n"
"commands:\n"
" update <filename> Reflash device from update.zip.\n"
+ " Sets the flashed slot as active.\n"
" flashall Flash boot, system, vendor, and --\n"
- " if found -- recovery.\n"
+ " if found -- recovery. If the device\n"
+ " supports slots, the slot that has\n"
+ " been flashed to is set as active.\n"
+ " Secondary images may be flashed to\n"
+ " an inactive slot.\n"
" flash <partition> [ <filename> ] Write a file to a flash partition.\n"
" flashing lock Locks the device. Prevents flashing.\n"
" flashing unlock Unlocks the device. Allows flashing\n"
@@ -280,19 +339,26 @@
" partitions.\n"
" flashing get_unlock_ability Queries bootloader to see if the\n"
" device is unlocked.\n"
+ " flashing get_unlock_bootloader_nonce Queries the bootloader to get the\n"
+ " unlock nonce.\n"
+ " flashing unlock_bootloader <request> Issue unlock bootloader using request.\n"
+ " flashing lock_bootloader Locks the bootloader to prevent\n"
+ " bootloader version rollback.\n"
" erase <partition> Erase a flash partition.\n"
" format[:[<fs type>][:[<size>]] <partition>\n"
" Format a flash partition. Can\n"
" override the fs type and/or size\n"
" the bootloader reports.\n"
" getvar <variable> Display a bootloader variable.\n"
+ " set_active <slot> Sets the active slot. If slots are\n"
+ " not supported, this does nothing.\n"
" boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
" flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
" Create bootimage and flash it.\n"
" devices [-l] List all connected devices [with\n"
" device paths].\n"
" continue Continue with autoboot.\n"
- " reboot [bootloader] Reboot device [into bootloader].\n"
+ " reboot [bootloader|emergency] Reboot device [into bootloader or emergency mode].\n"
" reboot-bootloader Reboot device into bootloader.\n"
" help Show this help message.\n"
"\n"
@@ -301,45 +367,77 @@
" if supported by partition type).\n"
" -u Do not erase partition before\n"
" formatting.\n"
- " -s <specific device> Specify device serial number\n"
- " or path to device port.\n"
+ " -s <specific device> Specify a device. For USB, provide either\n"
+ " a serial number or path to device port.\n"
+ " For ethernet, provide an address in the\n"
+ " form <protocol>:<hostname>[:port] where\n"
+ " <protocol> is either tcp or udp.\n"
" -p <product> Specify product name.\n"
" -c <cmdline> Override kernel commandline.\n"
" -i <vendor id> Specify a custom USB vendor id.\n"
- " -b <base_addr> Specify a custom kernel base\n"
+ " -b, --base <base_addr> Specify a custom kernel base\n"
" address (default: 0x10000000).\n"
- " -n <page size> Specify the nand page size\n"
+ " --kernel-offset Specify a custom kernel offset.\n"
+ " (default: 0x00008000)\n"
+ " --ramdisk-offset Specify a custom ramdisk offset.\n"
+ " (default: 0x01000000)\n"
+ " --tags-offset Specify a custom tags offset.\n"
+ " (default: 0x00000100)\n"
+ " -n, --page-size <page size> Specify the nand page size\n"
" (default: 2048).\n"
" -S <size>[K|M|G] Automatically sparse files greater\n"
" than 'size'. 0 to disable.\n"
+ " --slot <slot> Specify slot name to be used if the\n"
+ " device supports slots. All operations\n"
+ " on partitions that support slots will\n"
+ " be done on the slot specified.\n"
+ " 'all' can be given to refer to all slots.\n"
+ " 'other' can be given to refer to a\n"
+ " non-current slot. If this flag is not\n"
+ " used, slotted partitions will default\n"
+ " to the current active slot.\n"
+ " -a, --set-active[=<slot>] Sets the active slot. If no slot is\n"
+ " provided, this will default to the value\n"
+ " given by --slot. If slots are not\n"
+ " supported, this does nothing. This will\n"
+ " run after all non-reboot commands.\n"
+ " --skip-secondary Will not flash secondary slots when\n"
+ " performing a flashall or update. This\n"
+ " will preserve data on other slots.\n"
+ " --skip-reboot Will not reboot the device when\n"
+ " performing commands that normally\n"
+ " trigger a reboot.\n"
+#if !defined(_WIN32)
+ " --wipe-and-use-fbe On devices which support it,\n"
+ " erase userdata and cache, and\n"
+ " enable file-based encryption\n"
+#endif
+ " --unbuffered Do not buffer input or output.\n"
+ " --version Display version.\n"
+ " -h, --help show this message.\n"
);
}
-void *load_bootable_image(const char *kernel, const char *ramdisk,
- const char *secondstage, unsigned *sz,
- const char *cmdline)
-{
- void *kdata = 0, *rdata = 0, *sdata = 0;
- unsigned ksize = 0, rsize = 0, ssize = 0;
- void *bdata;
- unsigned bsize;
-
- if(kernel == 0) {
+static void* load_bootable_image(const char* kernel, const char* ramdisk,
+ const char* secondstage, int64_t* sz,
+ const char* cmdline) {
+ if (kernel == nullptr) {
fprintf(stderr, "no image specified\n");
return 0;
}
- kdata = load_file(kernel, &ksize);
- if(kdata == 0) {
+ int64_t ksize;
+ void* kdata = load_file(kernel, &ksize);
+ if (kdata == nullptr) {
fprintf(stderr, "cannot load '%s': %s\n", kernel, strerror(errno));
return 0;
}
- /* is this actually a boot image? */
+ // Is this actually a boot image?
if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
- if(cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
+ if (cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
- if(ramdisk) {
+ if (ramdisk) {
fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
return 0;
}
@@ -348,39 +446,44 @@
return kdata;
}
- if(ramdisk) {
+ void* rdata = nullptr;
+ int64_t rsize = 0;
+ if (ramdisk) {
rdata = load_file(ramdisk, &rsize);
- if(rdata == 0) {
+ if (rdata == nullptr) {
fprintf(stderr,"cannot load '%s': %s\n", ramdisk, strerror(errno));
return 0;
}
}
+ void* sdata = nullptr;
+ int64_t ssize = 0;
if (secondstage) {
sdata = load_file(secondstage, &ssize);
- if(sdata == 0) {
+ if (sdata == nullptr) {
fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno));
return 0;
}
}
fprintf(stderr,"creating boot image...\n");
- bdata = mkbootimg(kdata, ksize, kernel_offset,
+ int64_t bsize = 0;
+ void* bdata = mkbootimg(kdata, ksize, kernel_offset,
rdata, rsize, ramdisk_offset,
sdata, ssize, second_offset,
page_size, base_addr, tags_offset, &bsize);
- if(bdata == 0) {
+ if (bdata == nullptr) {
fprintf(stderr,"failed to create boot.img\n");
return 0;
}
- if(cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
- fprintf(stderr,"creating boot image - %d bytes\n", bsize);
+ if (cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
+ fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
*sz = bsize;
return bdata;
}
-static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, unsigned* sz)
+static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz)
{
ZipString zip_entry_name(entry_name);
ZipEntry zip_entry;
@@ -392,8 +495,8 @@
*sz = zip_entry.uncompressed_length;
uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
- if (data == NULL) {
- fprintf(stderr, "failed to allocate %u bytes for '%s'\n", *sz, entry_name);
+ if (data == nullptr) {
+ fprintf(stderr, "failed to allocate %" PRId64 " bytes for '%s'\n", *sz, entry_name);
return 0;
}
@@ -434,11 +537,63 @@
#define tmpfile win32_tmpfile
+static std::string make_temporary_directory() {
+ fprintf(stderr, "make_temporary_directory not supported under Windows, sorry!");
+ return "";
+}
+
+#else
+
+static std::string make_temporary_directory() {
+ const char *tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) {
+ tmpdir = P_tmpdir;
+ }
+ std::string result = std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
+ if (mkdtemp(&result[0]) == NULL) {
+ fprintf(stderr, "Unable to create temporary directory: %s\n",
+ strerror(errno));
+ return "";
+ }
+ return result;
+}
+
#endif
+static std::string create_fbemarker_tmpdir() {
+ std::string dir = make_temporary_directory();
+ if (dir.empty()) {
+ fprintf(stderr, "Unable to create local temp directory for FBE marker\n");
+ return "";
+ }
+ std::string marker_file = dir + "/" + convert_fbe_marker_filename;
+ int fd = open(marker_file.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0666);
+ if (fd == -1) {
+ fprintf(stderr, "Unable to create FBE marker file %s locally: %d, %s\n",
+ marker_file.c_str(), errno, strerror(errno));
+ return "";
+ }
+ close(fd);
+ return dir;
+}
+
+static void delete_fbemarker_tmpdir(const std::string& dir) {
+ std::string marker_file = dir + "/" + convert_fbe_marker_filename;
+ if (unlink(marker_file.c_str()) == -1) {
+ fprintf(stderr, "Unable to delete FBE marker file %s locally: %d, %s\n",
+ marker_file.c_str(), errno, strerror(errno));
+ return;
+ }
+ if (rmdir(dir.c_str()) == -1) {
+ fprintf(stderr, "Unable to delete FBE marker directory %s locally: %d, %s\n",
+ dir.c_str(), errno, strerror(errno));
+ return;
+ }
+}
+
static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
FILE* fp = tmpfile();
- if (fp == NULL) {
+ if (fp == nullptr) {
fprintf(stderr, "failed to create temporary file for '%s': %s\n",
entry_name, strerror(errno));
return -1;
@@ -448,6 +603,7 @@
ZipEntry zip_entry;
if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
+ fclose(fp);
return -1;
}
@@ -455,10 +611,12 @@
int error = ExtractEntryToFile(zip, &zip_entry, fd);
if (error != 0) {
fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
+ fclose(fp);
return -1;
}
lseek(fd, 0, SEEK_SET);
+ // TODO: We're leaking 'fp' here.
return fd;
}
@@ -478,7 +636,7 @@
static int setup_requirement_line(char *name)
{
char *val[MAX_OPTIONS];
- char *prod = NULL;
+ char *prod = nullptr;
unsigned n, count;
char *x;
int invert = 0;
@@ -539,13 +697,10 @@
return 0;
}
-static void setup_requirements(char *data, unsigned sz)
-{
- char *s;
-
- s = data;
+static void setup_requirements(char* data, int64_t sz) {
+ char* s = data;
while (sz-- > 0) {
- if(*s == '\n') {
+ if (*s == '\n') {
*s++ = 0;
if (setup_requirement_line(data)) {
die("out of memory");
@@ -557,8 +712,7 @@
}
}
-void queue_info_dump(void)
-{
+static void queue_info_dump() {
fb_queue_notice("--------------------------------------------");
fb_queue_display("version-bootloader", "Bootloader Version...");
fb_queue_display("version-baseband", "Baseband Version.....");
@@ -573,7 +727,7 @@
die("cannot sparse read file\n");
}
- int files = sparse_file_resparse(s, max_size, NULL, 0);
+ int files = sparse_file_resparse(s, max_size, nullptr, 0);
if (files < 0) {
die("Failed to resparse\n");
}
@@ -591,25 +745,29 @@
return out_s;
}
-static int64_t get_target_sparse_limit(struct usb_handle *usb)
-{
- int64_t limit = 0;
- char response[FB_RESPONSE_SZ + 1];
- int status = fb_getvar(usb, response, "max-download-size");
-
- if (!status) {
- limit = strtoul(response, NULL, 0);
- if (limit > 0) {
- fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n",
- limit);
- }
+static int64_t get_target_sparse_limit(Transport* transport) {
+ std::string max_download_size;
+ if (!fb_getvar(transport, "max-download-size", &max_download_size) ||
+ max_download_size.empty()) {
+ fprintf(stderr, "target didn't report max-download-size\n");
+ return 0;
}
+ // Some bootloaders (angler, for example) send spurious whitespace too.
+ max_download_size = android::base::Trim(max_download_size);
+
+ uint64_t limit;
+ if (!android::base::ParseUint(max_download_size, &limit)) {
+ fprintf(stderr, "couldn't parse max-download-size '%s'\n", max_download_size.c_str());
+ return 0;
+ }
+ if (limit > 0) {
+ fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n", limit);
+ }
return limit;
}
-static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
-{
+static int64_t get_sparse_limit(Transport* transport, int64_t size) {
int64_t limit;
if (sparse_limit == 0) {
@@ -618,7 +776,7 @@
limit = sparse_limit;
} else {
if (target_sparse_limit == -1) {
- target_sparse_limit = get_target_sparse_limit(usb);
+ target_sparse_limit = get_target_sparse_limit(transport);
}
if (target_sparse_limit > 0) {
limit = target_sparse_limit;
@@ -634,63 +792,49 @@
return 0;
}
-/* Until we get lazy inode table init working in make_ext4fs, we need to
- * erase partitions of type ext4 before flashing a filesystem so no stale
- * inodes are left lying around. Otherwise, e2fsck gets very upset.
- */
-static int needs_erase(usb_handle* usb, const char *part)
-{
- /* The function fb_format_supported() currently returns the value
- * we want, so just call it.
- */
- return fb_format_supported(usb, part, NULL);
+// Until we get lazy inode table init working in make_ext4fs, we need to
+// erase partitions of type ext4 before flashing a filesystem so no stale
+// inodes are left lying around. Otherwise, e2fsck gets very upset.
+static bool needs_erase(Transport* transport, const char* partition) {
+ std::string partition_type;
+ if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
+ return false;
+ }
+ return partition_type == "ext4";
}
-static int load_buf_fd(usb_handle *usb, int fd,
- struct fastboot_buffer *buf)
-{
- int64_t sz64;
- void *data;
- int64_t limit;
-
-
- sz64 = file_size(fd);
- if (sz64 < 0) {
- return -1;
+static bool load_buf_fd(Transport* transport, int fd, struct fastboot_buffer* buf) {
+ int64_t sz = get_file_size(fd);
+ if (sz == -1) {
+ return false;
}
- lseek(fd, 0, SEEK_SET);
- limit = get_sparse_limit(usb, sz64);
+ lseek64(fd, 0, SEEK_SET);
+ int64_t limit = get_sparse_limit(transport, sz);
if (limit) {
- struct sparse_file **s = load_sparse_files(fd, limit);
- if (s == NULL) {
- return -1;
+ sparse_file** s = load_sparse_files(fd, limit);
+ if (s == nullptr) {
+ return false;
}
buf->type = FB_BUFFER_SPARSE;
buf->data = s;
} else {
- unsigned int sz;
- data = load_fd(fd, &sz);
- if (data == 0) return -1;
+ void* data = load_fd(fd, &sz);
+ if (data == nullptr) return -1;
buf->type = FB_BUFFER;
buf->data = data;
buf->sz = sz;
}
- return 0;
+ return true;
}
-static int load_buf(usb_handle *usb, const char *fname,
- struct fastboot_buffer *buf)
-{
- int fd;
-
- fd = open(fname, O_RDONLY | O_BINARY);
- if (fd < 0) {
- return -1;
+static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
+ int fd = open(fname, O_RDONLY | O_BINARY);
+ if (fd == -1) {
+ return false;
}
-
- return load_buf_fd(usb, fd, buf);
+ return load_buf_fd(transport, fd, buf);
}
static void flash_buf(const char *pname, struct fastboot_buffer *buf)
@@ -698,13 +842,22 @@
sparse_file** s;
switch (buf->type) {
- case FB_BUFFER_SPARSE:
+ case FB_BUFFER_SPARSE: {
+ std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
s = reinterpret_cast<sparse_file**>(buf->data);
while (*s) {
- int64_t sz64 = sparse_file_len(*s, true, false);
- fb_queue_flash_sparse(pname, *s++, sz64);
+ int64_t sz = sparse_file_len(*s, true, false);
+ sparse_files.emplace_back(*s, sz);
+ ++s;
+ }
+
+ for (size_t i = 0; i < sparse_files.size(); ++i) {
+ const auto& pair = sparse_files[i];
+ fb_queue_flash_sparse(pname, pair.first, pair.second, i + 1, sparse_files.size());
}
break;
+ }
+
case FB_BUFFER:
fb_queue_flash(pname, buf->data, buf->sz);
break;
@@ -713,27 +866,207 @@
}
}
-void do_flash(usb_handle *usb, const char *pname, const char *fname)
+static std::string get_current_slot(Transport* transport)
{
+ std::string current_slot;
+ if (fb_getvar(transport, "current-slot", ¤t_slot)) {
+ if (current_slot == "_a") return "a"; // Legacy support
+ if (current_slot == "_b") return "b"; // Legacy support
+ return current_slot;
+ }
+ return "";
+}
+
+// Legacy support
+static std::vector<std::string> get_suffixes_obsolete(Transport* transport) {
+ std::vector<std::string> suffixes;
+ std::string suffix_list;
+ if (!fb_getvar(transport, "slot-suffixes", &suffix_list)) {
+ return suffixes;
+ }
+ suffixes = android::base::Split(suffix_list, ",");
+ // Unfortunately some devices will return an error message in the
+ // guise of a valid value. If we only see only one suffix, it's probably
+ // not real.
+ if (suffixes.size() == 1) {
+ suffixes.clear();
+ }
+ return suffixes;
+}
+
+// Legacy support
+static bool supports_AB_obsolete(Transport* transport) {
+ return !get_suffixes_obsolete(transport).empty();
+}
+
+static int get_slot_count(Transport* transport) {
+ std::string var;
+ int count;
+ if (!fb_getvar(transport, "slot-count", &var)) {
+ if (supports_AB_obsolete(transport)) return 2; // Legacy support
+ }
+ if (!android::base::ParseInt(var, &count)) return 0;
+ return count;
+}
+
+static bool supports_AB(Transport* transport) {
+ return get_slot_count(transport) >= 2;
+}
+
+// Given a current slot, this returns what the 'other' slot is.
+static std::string get_other_slot(const std::string& current_slot, int count) {
+ if (count == 0) return "";
+
+ char next = (current_slot[0] - 'a' + 1)%count + 'a';
+ return std::string(1, next);
+}
+
+static std::string get_other_slot(Transport* transport, const std::string& current_slot) {
+ return get_other_slot(current_slot, get_slot_count(transport));
+}
+
+static std::string get_other_slot(Transport* transport, int count) {
+ return get_other_slot(get_current_slot(transport), count);
+}
+
+static std::string get_other_slot(Transport* transport) {
+ return get_other_slot(get_current_slot(transport), get_slot_count(transport));
+}
+
+static std::string verify_slot(Transport* transport, const std::string& slot_name, bool allow_all) {
+ std::string slot = slot_name;
+ if (slot == "_a") slot = "a"; // Legacy support
+ if (slot == "_b") slot = "b"; // Legacy support
+ if (slot == "all") {
+ if (allow_all) {
+ return "all";
+ } else {
+ int count = get_slot_count(transport);
+ if (count > 0) {
+ return "a";
+ } else {
+ die("No known slots.");
+ }
+ }
+ }
+
+ int count = get_slot_count(transport);
+ if (count == 0) die("Device does not support slots.\n");
+
+ if (slot == "other") {
+ std::string other = get_other_slot(transport, count);
+ if (other == "") {
+ die("No known slots.");
+ }
+ return other;
+ }
+
+ if (slot.size() == 1 && (slot[0]-'a' >= 0 && slot[0]-'a' < count)) return slot;
+
+ fprintf(stderr, "Slot %s does not exist. supported slots are:\n", slot.c_str());
+ for (int i=0; i<count; i++) {
+ fprintf(stderr, "%c\n", (char)(i + 'a'));
+ }
+
+ exit(1);
+}
+
+static std::string verify_slot(Transport* transport, const std::string& slot) {
+ return verify_slot(transport, slot, true);
+}
+
+static void do_for_partition(Transport* transport, const std::string& part, const std::string& slot,
+ const std::function<void(const std::string&)>& func, bool force_slot) {
+ std::string has_slot;
+ std::string current_slot;
+
+ if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
+ /* If has-slot is not supported, the answer is no. */
+ has_slot = "no";
+ }
+ if (has_slot == "yes") {
+ if (slot == "") {
+ current_slot = get_current_slot(transport);
+ if (current_slot == "") {
+ die("Failed to identify current slot.\n");
+ }
+ func(part + "_" + current_slot);
+ } else {
+ func(part + '_' + slot);
+ }
+ } else {
+ if (force_slot && slot != "") {
+ fprintf(stderr, "Warning: %s does not support slots, and slot %s was requested.\n",
+ part.c_str(), slot.c_str());
+ }
+ func(part);
+ }
+}
+
+/* This function will find the real partition name given a base name, and a slot. If slot is NULL or
+ * empty, it will use the current slot. If slot is "all", it will return a list of all possible
+ * partition names. If force_slot is true, it will fail if a slot is specified, and the given
+ * partition does not support slots.
+ */
+static void do_for_partitions(Transport* transport, const std::string& part, const std::string& slot,
+ const std::function<void(const std::string&)>& func, bool force_slot) {
+ std::string has_slot;
+
+ if (slot == "all") {
+ if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
+ die("Could not check if partition %s has slot.", part.c_str());
+ }
+ if (has_slot == "yes") {
+ for (int i=0; i < get_slot_count(transport); i++) {
+ do_for_partition(transport, part, std::string(1, (char)(i + 'a')), func, force_slot);
+ }
+ } else {
+ do_for_partition(transport, part, "", func, force_slot);
+ }
+ } else {
+ do_for_partition(transport, part, slot, func, force_slot);
+ }
+}
+
+static void do_flash(Transport* transport, const char* pname, const char* fname) {
struct fastboot_buffer buf;
- if (load_buf(usb, fname, &buf)) {
- die("cannot load '%s'", fname);
+ if (!load_buf(transport, fname, &buf)) {
+ die("cannot load '%s': %s", fname, strerror(errno));
}
flash_buf(pname, &buf);
}
-void do_update_signature(ZipArchiveHandle zip, char *fn)
-{
- unsigned sz;
+static void do_update_signature(ZipArchiveHandle zip, char* fn) {
+ int64_t sz;
void* data = unzip_file(zip, fn, &sz);
- if (data == 0) return;
+ if (data == nullptr) return;
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
}
-void do_update(usb_handle *usb, const char *filename, int erase_first)
-{
+// Sets slot_override as the active slot. If slot_override is blank,
+// set current slot as active instead. This clears slot-unbootable.
+static void set_active(Transport* transport, const std::string& slot_override) {
+ std::string separator = "";
+ if (!supports_AB(transport)) {
+ if (supports_AB_obsolete(transport)) {
+ separator = "_"; // Legacy support
+ } else {
+ return;
+ }
+ }
+ if (slot_override != "") {
+ fb_set_active((separator + slot_override).c_str());
+ } else {
+ std::string current_slot = get_current_slot(transport);
+ if (current_slot != "") {
+ fb_set_active((separator + current_slot).c_str());
+ }
+ }
+}
+
+static void do_update(Transport* transport, const char* filename, const std::string& slot_override, bool erase_first, bool skip_secondary) {
queue_info_dump();
fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -745,16 +1078,39 @@
die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
}
- unsigned sz;
+ int64_t sz;
void* data = unzip_file(zip, "android-info.txt", &sz);
- if (data == 0) {
+ if (data == nullptr) {
CloseArchive(zip);
die("update package '%s' has no android-info.txt", filename);
}
setup_requirements(reinterpret_cast<char*>(data), sz);
- for (size_t i = 0; i < ARRAY_SIZE(images); ++i) {
+ std::string secondary;
+ if (!skip_secondary) {
+ if (slot_override != "") {
+ secondary = get_other_slot(transport, slot_override);
+ } else {
+ secondary = get_other_slot(transport);
+ }
+ if (secondary == "") {
+ if (supports_AB(transport)) {
+ fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
+ }
+ skip_secondary = true;
+ }
+ }
+ for (size_t i = 0; i < arraysize(images); ++i) {
+ const char* slot = slot_override.c_str();
+ if (images[i].is_secondary) {
+ if (!skip_secondary) {
+ slot = secondary.c_str();
+ } else {
+ continue;
+ }
+ }
+
int fd = unzip_to_file(zip, images[i].img_name);
if (fd == -1) {
if (images[i].is_optional) {
@@ -764,75 +1120,132 @@
exit(1); // unzip_to_file already explained why.
}
fastboot_buffer buf;
- int rc = load_buf_fd(usb, fd, &buf);
- if (rc) die("cannot load %s from flash", images[i].img_name);
- do_update_signature(zip, images[i].sig_name);
- if (erase_first && needs_erase(usb, images[i].part_name)) {
- fb_queue_erase(images[i].part_name);
+ if (!load_buf_fd(transport, fd, &buf)) {
+ die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
}
- flash_buf(images[i].part_name, &buf);
- /* not closing the fd here since the sparse code keeps the fd around
- * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
- * program exits.
- */
+
+ auto update = [&](const std::string &partition) {
+ do_update_signature(zip, images[i].sig_name);
+ if (erase_first && needs_erase(transport, partition.c_str())) {
+ fb_queue_erase(partition.c_str());
+ }
+ flash_buf(partition.c_str(), &buf);
+ /* not closing the fd here since the sparse code keeps the fd around
+ * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
+ * program exits.
+ */
+ };
+ do_for_partitions(transport, images[i].part_name, slot, update, false);
}
CloseArchive(zip);
+ if (slot_override == "all") {
+ set_active(transport, "a");
+ } else {
+ set_active(transport, slot_override);
+ }
}
-void do_send_signature(char *fn)
-{
- void *data;
- unsigned sz;
- char *xtn;
+static void do_send_signature(const std::string& fn) {
+ std::size_t extension_loc = fn.find(".img");
+ if (extension_loc == std::string::npos) return;
- xtn = strrchr(fn, '.');
- if (!xtn) return;
- if (strcmp(xtn, ".img")) return;
+ std::string fs_sig = fn.substr(0, extension_loc) + ".sig";
- strcpy(xtn,".sig");
- data = load_file(fn, &sz);
- strcpy(xtn,".img");
- if (data == 0) return;
+ int64_t sz;
+ void* data = load_file(fs_sig.c_str(), &sz);
+ if (data == nullptr) return;
+
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
}
-void do_flashall(usb_handle *usb, int erase_first)
-{
+static void do_flashall(Transport* transport, const std::string& slot_override, int erase_first, bool skip_secondary) {
+ std::string fname;
queue_info_dump();
fb_queue_query_save("product", cur_product, sizeof(cur_product));
- char* fname = find_item("info", product);
- if (fname == 0) die("cannot find android-info.txt");
+ fname = find_item("info", product);
+ if (fname.empty()) die("cannot find android-info.txt");
- unsigned sz;
- void* data = load_file(fname, &sz);
- if (data == 0) die("could not load android-info.txt: %s", strerror(errno));
+ int64_t sz;
+ void* data = load_file(fname.c_str(), &sz);
+ if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
setup_requirements(reinterpret_cast<char*>(data), sz);
- for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
- fname = find_item(images[i].part_name, product);
+ std::string secondary;
+ if (!skip_secondary) {
+ if (slot_override != "") {
+ secondary = get_other_slot(transport, slot_override);
+ } else {
+ secondary = get_other_slot(transport);
+ }
+ if (secondary == "") {
+ if (supports_AB(transport)) {
+ fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
+ }
+ skip_secondary = true;
+ }
+ }
+
+ for (size_t i = 0; i < arraysize(images); i++) {
+ const char* slot = NULL;
+ if (images[i].is_secondary) {
+ if (!skip_secondary) slot = secondary.c_str();
+ } else {
+ slot = slot_override.c_str();
+ }
+ if (!slot) continue;
+ fname = find_item_given_name(images[i].img_name, product);
fastboot_buffer buf;
- if (load_buf(usb, fname, &buf)) {
- if (images[i].is_optional)
- continue;
- die("could not load %s\n", images[i].img_name);
+ if (!load_buf(transport, fname.c_str(), &buf)) {
+ if (images[i].is_optional) continue;
+ die("could not load '%s': %s\n", images[i].img_name, strerror(errno));
}
- do_send_signature(fname);
- if (erase_first && needs_erase(usb, images[i].part_name)) {
- fb_queue_erase(images[i].part_name);
- }
- flash_buf(images[i].part_name, &buf);
+
+ auto flashall = [&](const std::string &partition) {
+ do_send_signature(fname.c_str());
+ if (erase_first && needs_erase(transport, partition.c_str())) {
+ fb_queue_erase(partition.c_str());
+ }
+ flash_buf(partition.c_str(), &buf);
+ };
+ do_for_partitions(transport, images[i].part_name, slot, flashall, false);
+ }
+
+ if (slot_override == "all") {
+ set_active(transport, "a");
+ } else {
+ set_active(transport, slot_override);
}
}
#define skip(n) do { argc -= (n); argv += (n); } while (0)
#define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
-int do_oem_command(int argc, char **argv)
+static int do_bypass_unlock_command(int argc, char **argv)
+{
+ if (argc <= 2) return 0;
+ skip(2);
+
+ /*
+ * Process unlock_bootloader, we have to load the message file
+ * and send that to the remote device.
+ */
+ require(1);
+
+ int64_t sz;
+ void* data = load_file(*argv, &sz);
+ if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno));
+ fb_queue_download("unlock_message", data, sz);
+ fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
+ skip(1);
+ return 0;
+}
+
+static int do_oem_command(int argc, char **argv)
{
char command[256];
if (argc <= 1) return 0;
@@ -890,126 +1303,150 @@
return num;
}
-void fb_perform_format(usb_handle* usb,
- const char *partition, int skip_if_not_supported,
- const char *type_override, const char *size_override)
-{
- char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
- char *pType = pTypeBuff;
- char *pSize = pSizeBuff;
- unsigned int limit = INT_MAX;
+static void fb_perform_format(Transport* transport,
+ const char* partition, int skip_if_not_supported,
+ const char* type_override, const char* size_override,
+ const std::string& initial_dir) {
+ std::string partition_type, partition_size;
+
struct fastboot_buffer buf;
- const char *errMsg = NULL;
- const struct fs_generator *gen;
- uint64_t pSz;
- int status;
+ const char* errMsg = nullptr;
+ const struct fs_generator* gen = nullptr;
int fd;
- if (target_sparse_limit > 0 && target_sparse_limit < limit)
+ unsigned int limit = INT_MAX;
+ if (target_sparse_limit > 0 && target_sparse_limit < limit) {
limit = target_sparse_limit;
- if (sparse_limit > 0 && sparse_limit < limit)
+ }
+ if (sparse_limit > 0 && sparse_limit < limit) {
limit = sparse_limit;
+ }
- status = fb_getvar(usb, pType, "partition-type:%s", partition);
- if (status) {
+ if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
errMsg = "Can't determine partition type.\n";
goto failed;
}
if (type_override) {
- if (strcmp(type_override, pType)) {
- fprintf(stderr,
- "Warning: %s type is %s, but %s was requested for formating.\n",
- partition, pType, type_override);
+ if (partition_type != type_override) {
+ fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
+ partition, partition_type.c_str(), type_override);
}
- pType = (char *)type_override;
+ partition_type = type_override;
}
- status = fb_getvar(usb, pSize, "partition-size:%s", partition);
- if (status) {
+ if (!fb_getvar(transport, std::string("partition-size:") + partition, &partition_size)) {
errMsg = "Unable to get partition size\n";
goto failed;
}
if (size_override) {
- if (strcmp(size_override, pSize)) {
- fprintf(stderr,
- "Warning: %s size is %s, but %s was requested for formating.\n",
- partition, pSize, size_override);
+ if (partition_size != size_override) {
+ fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
+ partition, partition_size.c_str(), size_override);
}
- pSize = (char *)size_override;
+ partition_size = size_override;
}
+ // Some bootloaders (angler, for example), send spurious leading whitespace.
+ partition_size = android::base::Trim(partition_size);
+ // Some bootloaders (hammerhead, for example) use implicit hex.
+ // This code used to use strtol with base 16.
+ if (!android::base::StartsWith(partition_size, "0x")) partition_size = "0x" + partition_size;
- gen = fs_get_generator(pType);
+ gen = fs_get_generator(partition_type);
if (!gen) {
if (skip_if_not_supported) {
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
- fprintf(stderr, "File system type %s not supported.\n", pType);
+ fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
return;
}
- fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType);
+ fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
+ partition_type.c_str());
return;
}
- pSz = strtoll(pSize, (char **)NULL, 16);
+ int64_t size;
+ if (!android::base::ParseInt(partition_size, &size)) {
+ fprintf(stderr, "Couldn't parse partition size '%s'.\n", partition_size.c_str());
+ return;
+ }
fd = fileno(tmpfile());
- if (fs_generator_generate(gen, fd, pSz)) {
+ if (fs_generator_generate(gen, fd, size, initial_dir)) {
+ fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
close(fd);
- fprintf(stderr, "Cannot generate image.\n");
return;
}
- if (load_buf_fd(usb, fd, &buf)) {
- fprintf(stderr, "Cannot read image.\n");
+ if (!load_buf_fd(transport, fd, &buf)) {
+ fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
close(fd);
return;
}
flash_buf(partition, &buf);
-
return;
-
failed:
if (skip_if_not_supported) {
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
- if (errMsg)
- fprintf(stderr, "%s", errMsg);
+ if (errMsg) fprintf(stderr, "%s", errMsg);
}
- fprintf(stderr,"FAILED (%s)\n", fb_get_error());
+ fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
}
int main(int argc, char **argv)
{
- int wants_wipe = 0;
- int wants_reboot = 0;
- int wants_reboot_bootloader = 0;
- int erase_first = 1;
+ bool wants_wipe = false;
+ bool wants_reboot = false;
+ bool wants_reboot_bootloader = false;
+ bool wants_reboot_emergency = false;
+ bool skip_reboot = false;
+ bool wants_set_active = false;
+ bool skip_secondary = false;
+ bool erase_first = true;
+ bool set_fbe_marker = false;
void *data;
- unsigned sz;
- int status;
- int c;
+ int64_t sz;
int longindex;
+ std::string slot_override;
+ std::string next_active;
const struct option longopts[] = {
{"base", required_argument, 0, 'b'},
{"kernel_offset", required_argument, 0, 'k'},
+ {"kernel-offset", required_argument, 0, 'k'},
{"page_size", required_argument, 0, 'n'},
+ {"page-size", required_argument, 0, 'n'},
{"ramdisk_offset", required_argument, 0, 'r'},
+ {"ramdisk-offset", required_argument, 0, 'r'},
{"tags_offset", required_argument, 0, 't'},
+ {"tags-offset", required_argument, 0, 't'},
{"help", no_argument, 0, 'h'},
{"unbuffered", no_argument, 0, 0},
{"version", no_argument, 0, 0},
+ {"slot", required_argument, 0, 0},
+ {"set_active", optional_argument, 0, 'a'},
+ {"set-active", optional_argument, 0, 'a'},
+ {"skip-secondary", no_argument, 0, 0},
+ {"skip-reboot", no_argument, 0, 0},
+#if !defined(_WIN32)
+ {"wipe-and-use-fbe", no_argument, 0, 0},
+#endif
{0, 0, 0, 0}
};
serial = getenv("ANDROID_SERIAL");
while (1) {
- c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
+ int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:ha::", longopts, &longindex);
if (c < 0) {
break;
}
/* Alphabetical cases */
switch (c) {
+ case 'a':
+ wants_set_active = true;
+ if (optarg)
+ next_active = optarg;
+ break;
case 'b':
base_addr = strtoul(optarg, 0, 16);
break;
@@ -1020,7 +1457,7 @@
usage();
return 1;
case 'i': {
- char *endptr = NULL;
+ char *endptr = nullptr;
unsigned long val;
val = strtoul(optarg, &endptr, 0);
@@ -1036,7 +1473,7 @@
long_listing = 1;
break;
case 'n':
- page_size = (unsigned)strtoul(optarg, NULL, 0);
+ page_size = (unsigned)strtoul(optarg, nullptr, 0);
if (!page_size) die("invalid page size");
break;
case 'p':
@@ -1058,20 +1495,35 @@
}
break;
case 'u':
- erase_first = 0;
+ erase_first = false;
break;
case 'w':
- wants_wipe = 1;
+ wants_wipe = true;
break;
case '?':
return 1;
case 0:
if (strcmp("unbuffered", longopts[longindex].name) == 0) {
- setvbuf(stdout, NULL, _IONBF, 0);
- setvbuf(stderr, NULL, _IONBF, 0);
+ setvbuf(stdout, nullptr, _IONBF, 0);
+ setvbuf(stderr, nullptr, _IONBF, 0);
} else if (strcmp("version", longopts[longindex].name) == 0) {
fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
return 0;
+ } else if (strcmp("slot", longopts[longindex].name) == 0) {
+ slot_override = std::string(optarg);
+ } else if (strcmp("skip-secondary", longopts[longindex].name) == 0 ) {
+ skip_secondary = true;
+ } else if (strcmp("skip-reboot", longopts[longindex].name) == 0 ) {
+ skip_reboot = true;
+#if !defined(_WIN32)
+ } else if (strcmp("wipe-and-use-fbe", longopts[longindex].name) == 0) {
+ wants_wipe = true;
+ set_fbe_marker = true;
+#endif
+ } else {
+ fprintf(stderr, "Internal error in options processing for %s\n",
+ longopts[longindex].name);
+ return 1;
}
break;
default:
@@ -1082,7 +1534,7 @@
argc -= optind;
argv += optind;
- if (argc == 0 && !wants_wipe) {
+ if (argc == 0 && !wants_wipe && !wants_set_active) {
usage();
return 1;
}
@@ -1098,26 +1550,56 @@
return 0;
}
- usb_handle* usb = open_device();
+ Transport* transport = open_device();
+ if (transport == nullptr) {
+ return 1;
+ }
+
+ if (!supports_AB(transport) && supports_AB_obsolete(transport)) {
+ fprintf(stderr, "Warning: Device A/B support is outdated. Bootloader update required.\n");
+ }
+ if (slot_override != "") slot_override = verify_slot(transport, slot_override);
+ if (next_active != "") next_active = verify_slot(transport, next_active, false);
+
+ if (wants_set_active) {
+ if (next_active == "") {
+ if (slot_override == "") {
+ std::string current_slot;
+ if (fb_getvar(transport, "current-slot", ¤t_slot)) {
+ next_active = verify_slot(transport, current_slot, false);
+ } else {
+ wants_set_active = false;
+ }
+ } else {
+ next_active = verify_slot(transport, slot_override, false);
+ }
+ }
+ }
while (argc > 0) {
- if(!strcmp(*argv, "getvar")) {
+ if (!strcmp(*argv, "getvar")) {
require(2);
fb_queue_display(argv[1], argv[1]);
skip(2);
} else if(!strcmp(*argv, "erase")) {
require(2);
- if (fb_format_supported(usb, argv[1], NULL)) {
- fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
- }
+ auto erase = [&](const std::string &partition) {
+ std::string partition_type;
+ if (fb_getvar(transport, std::string("partition-type:") + argv[1], &partition_type) &&
+ fs_get_generator(partition_type) != nullptr) {
+ fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
+ partition_type.c_str());
+ }
- fb_queue_erase(argv[1]);
+ fb_queue_erase(partition.c_str());
+ };
+ do_for_partitions(transport, argv[1], slot_override, erase, true);
skip(2);
} else if(!strncmp(*argv, "format", strlen("format"))) {
char *overrides;
- char *type_override = NULL;
- char *size_override = NULL;
+ char *type_override = nullptr;
+ char *size_override = nullptr;
require(2);
/*
* Parsing for: "format[:[type][:[size]]]"
@@ -1138,34 +1620,43 @@
}
type_override = overrides;
}
- if (type_override && !type_override[0]) type_override = NULL;
- if (size_override && !size_override[0]) size_override = NULL;
- if (erase_first && needs_erase(usb, argv[1])) {
- fb_queue_erase(argv[1]);
- }
- fb_perform_format(usb, argv[1], 0, type_override, size_override);
+ if (type_override && !type_override[0]) type_override = nullptr;
+ if (size_override && !size_override[0]) size_override = nullptr;
+
+ auto format = [&](const std::string &partition) {
+ if (erase_first && needs_erase(transport, partition.c_str())) {
+ fb_queue_erase(partition.c_str());
+ }
+ fb_perform_format(transport, partition.c_str(), 0,
+ type_override, size_override, "");
+ };
+ do_for_partitions(transport, argv[1], slot_override, format, true);
skip(2);
} else if(!strcmp(*argv, "signature")) {
require(2);
data = load_file(argv[1], &sz);
- if (data == 0) die("could not load '%s': %s", argv[1], strerror(errno));
+ if (data == nullptr) die("could not load '%s': %s", argv[1], strerror(errno));
if (sz != 256) die("signature must be 256 bytes");
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
skip(2);
} else if(!strcmp(*argv, "reboot")) {
- wants_reboot = 1;
+ wants_reboot = true;
skip(1);
if (argc > 0) {
if (!strcmp(*argv, "bootloader")) {
- wants_reboot = 0;
- wants_reboot_bootloader = 1;
+ wants_reboot = false;
+ wants_reboot_bootloader = true;
+ skip(1);
+ } else if (!strcmp(*argv, "emergency")) {
+ wants_reboot = false;
+ wants_reboot_emergency = true;
skip(1);
}
}
require(0);
} else if(!strcmp(*argv, "reboot-bootloader")) {
- wants_reboot_bootloader = 1;
+ wants_reboot_bootloader = true;
skip(1);
} else if (!strcmp(*argv, "continue")) {
fb_queue_command("continue", "resuming boot");
@@ -1192,8 +1683,8 @@
fb_queue_download("boot.img", data, sz);
fb_queue_command("boot", "booting");
} else if(!strcmp(*argv, "flash")) {
- char *pname = argv[1];
- char *fname = 0;
+ char* pname = argv[1];
+ std::string fname;
require(2);
if (argc > 2) {
fname = argv[2];
@@ -1202,13 +1693,16 @@
fname = find_item(pname, product);
skip(2);
}
- if (fname == 0) die("cannot determine image filename for '%s'", pname);
- if (erase_first && needs_erase(usb, pname)) {
- fb_queue_erase(pname);
- }
- do_flash(usb, pname, fname);
+ if (fname.empty()) die("cannot determine image filename for '%s'", pname);
+
+ auto flash = [&](const std::string &partition) {
+ if (erase_first && needs_erase(transport, partition.c_str())) {
+ fb_queue_erase(partition.c_str());
+ }
+ do_flash(transport, partition.c_str(), fname.c_str());
+ };
+ do_for_partitions(transport, pname, slot_override, flash, true);
} else if(!strcmp(*argv, "flash:raw")) {
- char *pname = argv[1];
char *kname = argv[2];
char *rname = 0;
char *sname = 0;
@@ -1224,28 +1718,59 @@
}
data = load_bootable_image(kname, rname, sname, &sz, cmdline);
if (data == 0) die("cannot load bootable image");
- fb_queue_flash(pname, data, sz);
+ auto flashraw = [&](const std::string &partition) {
+ fb_queue_flash(partition.c_str(), data, sz);
+ };
+ do_for_partitions(transport, argv[1], slot_override, flashraw, true);
} else if(!strcmp(*argv, "flashall")) {
skip(1);
- do_flashall(usb, erase_first);
- wants_reboot = 1;
+ if (slot_override == "all") {
+ fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
+ do_flashall(transport, slot_override, erase_first, true);
+ } else {
+ do_flashall(transport, slot_override, erase_first, skip_secondary);
+ }
+ wants_reboot = true;
} else if(!strcmp(*argv, "update")) {
+ bool slot_all = (slot_override == "all");
+ if (slot_all) {
+ fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
+ }
if (argc > 1) {
- do_update(usb, argv[1], erase_first);
+ do_update(transport, argv[1], slot_override, erase_first, skip_secondary || slot_all);
skip(2);
} else {
- do_update(usb, "update.zip", erase_first);
+ do_update(transport, "update.zip", slot_override, erase_first, skip_secondary || slot_all);
skip(1);
}
- wants_reboot = 1;
+ wants_reboot = true;
+ } else if(!strcmp(*argv, "set_active")) {
+ require(2);
+ std::string slot = verify_slot(transport, std::string(argv[1]), false);
+ // Legacy support: verify_slot() removes leading underscores, we need to put them back
+ // in for old bootloaders. Legacy bootloaders do not have the slot-count variable but
+ // do have slot-suffixes.
+ std::string var;
+ if (!fb_getvar(transport, "slot-count", &var) &&
+ fb_getvar(transport, "slot-suffixes", &var)) {
+ slot = "_" + slot;
+ }
+ fb_set_active(slot.c_str());
+ skip(2);
} else if(!strcmp(*argv, "oem")) {
argc = do_oem_command(argc, argv);
- } else if(!strcmp(*argv, "flashing") && argc == 2) {
- if(!strcmp(*(argv+1), "unlock") || !strcmp(*(argv+1), "lock")
- || !strcmp(*(argv+1), "unlock_critical")
- || !strcmp(*(argv+1), "lock_critical")
- || !strcmp(*(argv+1), "get_unlock_ability")) {
- argc = do_oem_command(argc, argv);
+ } else if(!strcmp(*argv, "flashing")) {
+ if (argc == 2 && (!strcmp(*(argv+1), "unlock") ||
+ !strcmp(*(argv+1), "lock") ||
+ !strcmp(*(argv+1), "unlock_critical") ||
+ !strcmp(*(argv+1), "lock_critical") ||
+ !strcmp(*(argv+1), "get_unlock_ability") ||
+ !strcmp(*(argv+1), "get_unlock_bootloader_nonce") ||
+ !strcmp(*(argv+1), "lock_bootloader"))) {
+ argc = do_oem_command(argc, argv);
+ } else
+ if (argc == 3 && !strcmp(*(argv+1), "unlock_bootloader")) {
+ argc = do_bypass_unlock_command(argc, argv);
} else {
usage();
return 1;
@@ -1257,22 +1782,40 @@
}
if (wants_wipe) {
+ fprintf(stderr, "wiping userdata...\n");
fb_queue_erase("userdata");
- fb_perform_format(usb, "userdata", 1, NULL, NULL);
- fb_queue_erase("cache");
- fb_perform_format(usb, "cache", 1, NULL, NULL);
+ if (set_fbe_marker) {
+ fprintf(stderr, "setting FBE marker...\n");
+ std::string initial_userdata_dir = create_fbemarker_tmpdir();
+ if (initial_userdata_dir.empty()) {
+ return 1;
+ }
+ fb_perform_format(transport, "userdata", 1, nullptr, nullptr, initial_userdata_dir);
+ delete_fbemarker_tmpdir(initial_userdata_dir);
+ } else {
+ fb_perform_format(transport, "userdata", 1, nullptr, nullptr, "");
+ }
+
+ std::string cache_type;
+ if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
+ fprintf(stderr, "wiping cache...\n");
+ fb_queue_erase("cache");
+ fb_perform_format(transport, "cache", 1, nullptr, nullptr, "");
+ }
}
- if (wants_reboot) {
+ if (wants_set_active) {
+ fb_set_active(next_active.c_str());
+ }
+ if (wants_reboot && !skip_reboot) {
fb_queue_reboot();
fb_queue_wait_for_disconnect();
} else if (wants_reboot_bootloader) {
fb_queue_command("reboot-bootloader", "rebooting into bootloader");
fb_queue_wait_for_disconnect();
+ } else if (wants_reboot_emergency) {
+ fb_queue_command("reboot-emergency", "rebooting into emergency download (EDL) mode");
+ fb_queue_wait_for_disconnect();
}
- if (fb_queue_is_empty())
- return 0;
-
- status = fb_execute_queue(usb);
- return (status) ? 1 : 0;
+ return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 481c501..6699b6a 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -29,55 +29,50 @@
#ifndef _FASTBOOT_H_
#define _FASTBOOT_H_
-#include "usb.h"
+#include <inttypes.h>
+#include <stdlib.h>
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include <string>
+
+#include "transport.h"
struct sparse_file;
/* protocol.c - fastboot protocol */
-int fb_command(usb_handle *usb, const char *cmd);
-int fb_command_response(usb_handle *usb, const char *cmd, char *response);
-int fb_download_data(usb_handle *usb, const void *data, unsigned size);
-int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s);
-char *fb_get_error(void);
+int fb_command(Transport* transport, const char* cmd);
+int fb_command_response(Transport* transport, const char* cmd, char* response);
+int fb_download_data(Transport* transport, const void* data, uint32_t size);
+int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
+const std::string fb_get_error();
#define FB_COMMAND_SZ 64
#define FB_RESPONSE_SZ 64
/* engine.c - high level command queue engine */
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...);
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override);
-void fb_queue_flash(const char *ptn, void *data, unsigned sz);
-void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz);
+bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
+void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
+ size_t total);
void fb_queue_erase(const char *ptn);
-void fb_queue_format(const char *ptn, int skip_if_not_supported, unsigned int max_chunk_sz);
-void fb_queue_require(const char *prod, const char *var, int invert,
- unsigned nvalues, const char **value);
+void fb_queue_format(const char *ptn, int skip_if_not_supported, int32_t max_chunk_sz);
+void fb_queue_require(const char *prod, const char *var, bool invert,
+ size_t nvalues, const char **value);
void fb_queue_display(const char *var, const char *prettyname);
-void fb_queue_query_save(const char *var, char *dest, unsigned dest_size);
+void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size);
void fb_queue_reboot(void);
void fb_queue_command(const char *cmd, const char *msg);
-void fb_queue_download(const char *name, void *data, unsigned size);
+void fb_queue_download(const char *name, void *data, uint32_t size);
void fb_queue_notice(const char *notice);
void fb_queue_wait_for_disconnect(void);
-int fb_execute_queue(usb_handle *usb);
-int fb_queue_is_empty(void);
+int fb_execute_queue(Transport* transport);
+void fb_set_active(const char *slot);
/* util stuff */
double now();
char *mkmsg(const char *fmt, ...);
__attribute__((__noreturn__)) void die(const char *fmt, ...);
-void get_my_path(char *path);
-
/* Current product */
extern char cur_product[FB_RESPONSE_SZ + 1];
-#if defined(__cplusplus)
-}
-#endif
-
#endif
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
deleted file mode 100644
index 37b1959..0000000
--- a/fastboot/fastboot_protocol.txt
+++ /dev/null
@@ -1,173 +0,0 @@
-
-FastBoot Version 0.4
-----------------------
-
-The fastboot protocol is a mechanism for communicating with bootloaders
-over USB. It is designed to be very straightforward to implement, to
-allow it to be used across a wide range of devices and from hosts running
-Linux, Windows, or OSX.
-
-
-Basic Requirements
-------------------
-
-* Two bulk endpoints (in, out) are required
-* Max packet size must be 64 bytes for full-speed, 512 bytes for
- high-speed and 1024 bytes for Super Speed USB.
-* The protocol is entirely host-driven and synchronous (unlike the
- multi-channel, bi-directional, asynchronous ADB protocol)
-
-
-Transport and Framing
----------------------
-
-1. Host sends a command, which is an ascii string in a single
- packet no greater than 64 bytes.
-
-2. Client response with a single packet no greater than 64 bytes.
- The first four bytes of the response are "OKAY", "FAIL", "DATA",
- or "INFO". Additional bytes may contain an (ascii) informative
- message.
-
- a. INFO -> the remaining 60 bytes are an informative message
- (providing progress or diagnostic messages). They should
- be displayed and then step #2 repeats
-
- b. FAIL -> the requested command failed. The remaining 60 bytes
- of the response (if present) provide a textual failure message
- to present to the user. Stop.
-
- c. OKAY -> the requested command completed successfully. Go to #5
-
- d. DATA -> the requested command is ready for the data phase.
- A DATA response packet will be 12 bytes long, in the form of
- DATA00000000 where the 8 digit hexidecimal number represents
- the total data size to transfer.
-
-3. Data phase. Depending on the command, the host or client will
- send the indicated amount of data. Short packets are always
- acceptable and zero-length packets are ignored. This phase continues
- until the client has sent or received the number of bytes indicated
- in the "DATA" response above.
-
-4. Client responds with a single packet no greater than 64 bytes.
- The first four bytes of the response are "OKAY", "FAIL", or "INFO".
- Similar to #2:
-
- a. INFO -> display the remaining 60 bytes and return to #4
-
- b. FAIL -> display the remaining 60 bytes (if present) as a failure
- reason and consider the command failed. Stop.
-
- c. OKAY -> success. Go to #5
-
-5. Success. Stop.
-
-
-Example Session
----------------
-
-Host: "getvar:version" request version variable
-
-Client: "OKAY0.4" return version "0.4"
-
-Host: "getvar:nonexistant" request some undefined variable
-
-Client: "OKAY" return value ""
-
-Host: "download:00001234" request to send 0x1234 bytes of data
-
-Client: "DATA00001234" ready to accept data
-
-Host: < 0x1234 bytes > send data
-
-Client: "OKAY" success
-
-Host: "flash:bootloader" request to flash the data to the bootloader
-
-Client: "INFOerasing flash" indicate status / progress
- "INFOwriting flash"
- "OKAY" indicate success
-
-Host: "powerdown" send a command
-
-Client: "FAILunknown command" indicate failure
-
-
-Command Reference
------------------
-
-* Command parameters are indicated by printf-style escape sequences.
-
-* Commands are ascii strings and sent without the quotes (which are
- for illustration only here) and without a trailing 0 byte.
-
-* Commands that begin with a lowercase letter are reserved for this
- specification. OEM-specific commands should not begin with a
- lowercase letter, to prevent incompatibilities with future specs.
-
- "getvar:%s" Read a config/version variable from the bootloader.
- The variable contents will be returned after the
- OKAY response.
-
- "download:%08x" Write data to memory which will be later used
- by "boot", "ramdisk", "flash", etc. The client
- will reply with "DATA%08x" if it has enough
- space in RAM or "FAIL" if not. The size of
- the download is remembered.
-
- "verify:%08x" Send a digital signature to verify the downloaded
- data. Required if the bootloader is "secure"
- otherwise "flash" and "boot" will be ignored.
-
- "flash:%s" Write the previously downloaded image to the
- named partition (if possible).
-
- "erase:%s" Erase the indicated partition (clear to 0xFFs)
-
- "boot" The previously downloaded data is a boot.img
- and should be booted according to the normal
- procedure for a boot.img
-
- "continue" Continue booting as normal (if possible)
-
- "reboot" Reboot the device.
-
- "reboot-bootloader" Reboot back into the bootloader.
- Useful for upgrade processes that require upgrading
- the bootloader and then upgrading other partitions
- using the new bootloader.
-
- "powerdown" Power off the device.
-
-
-
-Client Variables
-----------------
-
-The "getvar:%s" command is used to read client variables which
-represent various information about the device and the software
-on it.
-
-The various currently defined names are:
-
- version Version of FastBoot protocol supported.
- It should be "0.3" for this document.
-
- version-bootloader Version string for the Bootloader.
-
- version-baseband Version string of the Baseband Software
-
- product Name of the product
-
- serialno Product serial number
-
- secure If the value is "yes", this is a secure
- bootloader requiring a signature before
- it will install or boot images.
-
-Names starting with a lowercase character are reserved by this
-specification. OEM-specific names should not start with lowercase
-characters.
-
-
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index d8f9e16..9b73165 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -1,45 +1,45 @@
-#include "fastboot.h"
-#include "make_ext4fs.h"
-#include "make_f2fs.h"
#include "fs.h"
+#include "fastboot.h"
+#include "make_f2fs.h"
+
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
-#include <stdbool.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sparse/sparse.h>
#include <unistd.h>
-#ifdef USE_MINGW
-#include <fcntl.h>
-#else
-#include <sys/mman.h>
-#endif
+#include <ext4_utils/make_ext4fs.h>
+#include <sparse/sparse.h>
-
-
-static int generate_ext4_image(int fd, long long partSize)
+static int generate_ext4_image(int fd, long long partSize, const std::string& initial_dir)
{
- make_ext4fs_sparse_fd(fd, partSize, NULL, NULL);
-
+ if (initial_dir.empty()) {
+ make_ext4fs_sparse_fd(fd, partSize, NULL, NULL);
+ } else {
+ make_ext4fs_sparse_fd_directory(fd, partSize, NULL, NULL, initial_dir.c_str());
+ }
return 0;
}
#ifdef USE_F2FS
-static int generate_f2fs_image(int fd, long long partSize)
+static int generate_f2fs_image(int fd, long long partSize, const std::string& initial_dir)
{
+ if (!initial_dir.empty()) {
+ fprintf(stderr, "Unable to set initial directory on F2FS filesystem\n");
+ return -1;
+ }
return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
}
#endif
static const struct fs_generator {
-
const char* fs_type; //must match what fastboot reports for partition type
- int (*generate)(int fd, long long partSize); //returns 0 or error value
+
+ //returns 0 or error value
+ int (*generate)(int fd, long long partSize, const std::string& initial_dir);
} generators[] = {
{ "ext4", generate_ext4_image},
@@ -48,18 +48,17 @@
#endif
};
-const struct fs_generator* fs_get_generator(const char *fs_type)
-{
- unsigned i;
-
- for (i = 0; i < sizeof(generators) / sizeof(*generators); i++)
- if (!strcmp(generators[i].fs_type, fs_type))
+const struct fs_generator* fs_get_generator(const std::string& fs_type) {
+ for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) {
+ if (fs_type == generators[i].fs_type) {
return generators + i;
-
- return NULL;
+ }
+ }
+ return nullptr;
}
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize)
+int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+ const std::string& initial_dir)
{
- return gen->generate(tmpFileNo, partSize);
+ return gen->generate(tmpFileNo, partSize, initial_dir);
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 307772b..0a68507 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -1,20 +1,13 @@
#ifndef _FS_H_
#define _FS_H_
+#include <string>
#include <stdint.h>
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
struct fs_generator;
-const struct fs_generator* fs_get_generator(const char *fs_type);
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
-
-#if defined(__cplusplus)
-}
-#endif
+const struct fs_generator* fs_get_generator(const std::string& fs_type);
+int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+ const std::string& initial_dir);
#endif
-
diff --git a/fastboot/genkey.sh b/fastboot/genkey.sh
deleted file mode 100755
index 011e902..0000000
--- a/fastboot/genkey.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-
-if [ $# -ne 2 ]
-then
- echo "Usage: $0 alias \"pass phrase\""
- exit -1
-fi
-
-# Generate a 2048 bit RSA key with public exponent 3.
-# Encrypt private key with provided password.
-openssl genrsa -3 -out $1.pem -passout pass:"$2" 2048
-
-# Create a self-signed cert for this key.
-openssl req -new -x509 -key $1.pem -passin pass:"$2" \
- -out $1-cert.pem \
- -batch -days 10000
-
-# Create a PKCS12 store containing the generated private key.
-# Protect the keystore and the private key with the provided password.
-openssl pkcs12 -export -in $1-cert.pem -inkey $1.pem -passin pass:"$2" \
- -out $1.p12 -name $1 -passout pass:"$2"
-
-rm $1.pem
-rm $1-cert.pem
-
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index 00c8a03..bfa83b0 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -26,8 +26,6 @@
* SUCH DAMAGE.
*/
-#define min(a, b) \
- ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
#define round_down(a, b) \
({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
@@ -36,221 +34,193 @@
#include <string.h>
#include <errno.h>
+#include <algorithm>
+
+#include <android-base/stringprintf.h>
#include <sparse/sparse.h>
#include "fastboot.h"
+#include "transport.h"
-static char ERROR[128];
+static std::string g_error;
-char *fb_get_error(void)
-{
- return ERROR;
+const std::string fb_get_error() {
+ return g_error;
}
-static int check_response(usb_handle *usb, unsigned int size, char *response)
-{
- unsigned char status[65];
- int r;
+static int check_response(Transport* transport, uint32_t size, char* response) {
+ char status[65];
- for(;;) {
- r = usb_read(usb, status, 64);
- if(r < 0) {
- sprintf(ERROR, "status read failed (%s)", strerror(errno));
- usb_close(usb);
+ while (true) {
+ int r = transport->Read(status, 64);
+ if (r < 0) {
+ g_error = android::base::StringPrintf("status read failed (%s)", strerror(errno));
+ transport->Close();
return -1;
}
status[r] = 0;
- if(r < 4) {
- sprintf(ERROR, "status malformed (%d bytes)", r);
- usb_close(usb);
+ if (r < 4) {
+ g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
+ transport->Close();
return -1;
}
- if(!memcmp(status, "INFO", 4)) {
+ if (!memcmp(status, "INFO", 4)) {
fprintf(stderr,"(bootloader) %s\n", status + 4);
continue;
}
- if(!memcmp(status, "OKAY", 4)) {
- if(response) {
+ if (!memcmp(status, "OKAY", 4)) {
+ if (response) {
strcpy(response, (char*) status + 4);
}
return 0;
}
- if(!memcmp(status, "FAIL", 4)) {
- if(r > 4) {
- sprintf(ERROR, "remote: %s", status + 4);
+ if (!memcmp(status, "FAIL", 4)) {
+ if (r > 4) {
+ g_error = android::base::StringPrintf("remote: %s", status + 4);
} else {
- strcpy(ERROR, "remote failure");
+ g_error = "remote failure";
}
return -1;
}
- if(!memcmp(status, "DATA", 4) && size > 0){
- unsigned dsize = strtoul((char*) status + 4, 0, 16);
- if(dsize > size) {
- strcpy(ERROR, "data size too large");
- usb_close(usb);
+ if (!memcmp(status, "DATA", 4) && size > 0){
+ uint32_t dsize = strtol(status + 4, 0, 16);
+ if (dsize > size) {
+ g_error = android::base::StringPrintf("data size too large (%d)", dsize);
+ transport->Close();
return -1;
}
return dsize;
}
- strcpy(ERROR,"unknown status code");
- usb_close(usb);
+ g_error = "unknown status code";
+ transport->Close();
break;
}
return -1;
}
-static int _command_start(usb_handle *usb, const char *cmd, unsigned size,
- char *response)
-{
- int cmdsize = strlen(cmd);
+static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
+ size_t cmdsize = strlen(cmd);
+ if (cmdsize > 64) {
+ g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
+ return -1;
+ }
- if(response) {
+ if (response) {
response[0] = 0;
}
- if(cmdsize > 64) {
- sprintf(ERROR,"command too large");
+ if (transport->Write(cmd, cmdsize) != static_cast<int>(cmdsize)) {
+ g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
+ transport->Close();
return -1;
}
- if(usb_write(usb, cmd, cmdsize) != cmdsize) {
- sprintf(ERROR,"command write failed (%s)", strerror(errno));
- usb_close(usb);
- return -1;
- }
-
- return check_response(usb, size, response);
+ return check_response(transport, size, response);
}
-static int _command_data(usb_handle *usb, const void *data, unsigned size)
-{
- int r;
-
- r = usb_write(usb, data, size);
- if(r < 0) {
- sprintf(ERROR, "data transfer failure (%s)", strerror(errno));
- usb_close(usb);
+static int _command_data(Transport* transport, const void* data, uint32_t size) {
+ int r = transport->Write(data, size);
+ if (r < 0) {
+ g_error = android::base::StringPrintf("data transfer failure (%s)", strerror(errno));
+ transport->Close();
return -1;
}
- if(r != ((int) size)) {
- sprintf(ERROR, "data transfer failure (short transfer)");
- usb_close(usb);
+ if (r != ((int) size)) {
+ g_error = "data transfer failure (short transfer)";
+ transport->Close();
return -1;
}
-
return r;
}
-static int _command_end(usb_handle *usb)
-{
- int r;
- r = check_response(usb, 0, 0);
- if(r < 0) {
- return -1;
- }
- return 0;
+static int _command_end(Transport* transport) {
+ return check_response(transport, 0, 0) < 0 ? -1 : 0;
}
-static int _command_send(usb_handle *usb, const char *cmd,
- const void *data, unsigned size,
- char *response)
-{
- int r;
+static int _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
+ char* response) {
if (size == 0) {
return -1;
}
- r = _command_start(usb, cmd, size, response);
+ int r = _command_start(transport, cmd, size, response);
if (r < 0) {
return -1;
}
- r = _command_data(usb, data, size);
+ r = _command_data(transport, data, size);
if (r < 0) {
return -1;
}
- r = _command_end(usb);
- if(r < 0) {
+ r = _command_end(transport);
+ if (r < 0) {
return -1;
}
return size;
}
-static int _command_send_no_data(usb_handle *usb, const char *cmd,
- char *response)
-{
- return _command_start(usb, cmd, 0, response);
+static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
+ return _command_start(transport, cmd, 0, response);
}
-int fb_command(usb_handle *usb, const char *cmd)
-{
- return _command_send_no_data(usb, cmd, 0);
+int fb_command(Transport* transport, const char* cmd) {
+ return _command_send_no_data(transport, cmd, 0);
}
-int fb_command_response(usb_handle *usb, const char *cmd, char *response)
-{
- return _command_send_no_data(usb, cmd, response);
+int fb_command_response(Transport* transport, const char* cmd, char* response) {
+ return _command_send_no_data(transport, cmd, response);
}
-int fb_download_data(usb_handle *usb, const void *data, unsigned size)
-{
+int fb_download_data(Transport* transport, const void* data, uint32_t size) {
char cmd[64];
- int r;
-
- sprintf(cmd, "download:%08x", size);
- r = _command_send(usb, cmd, data, size, 0);
-
- if(r < 0) {
- return -1;
- } else {
- return 0;
- }
+ snprintf(cmd, sizeof(cmd), "download:%08x", size);
+ return _command_send(transport, cmd, data, size, 0) < 0 ? -1 : 0;
}
-#define USB_BUF_SIZE 1024
-static char usb_buf[USB_BUF_SIZE];
-static int usb_buf_len;
+#define TRANSPORT_BUF_SIZE 1024
+static char transport_buf[TRANSPORT_BUF_SIZE];
+static int transport_buf_len;
static int fb_download_data_sparse_write(void *priv, const void *data, int len)
{
int r;
- usb_handle* usb = reinterpret_cast<usb_handle*>(priv);
+ Transport* transport = reinterpret_cast<Transport*>(priv);
int to_write;
const char* ptr = reinterpret_cast<const char*>(data);
- if (usb_buf_len) {
- to_write = min(USB_BUF_SIZE - usb_buf_len, len);
+ if (transport_buf_len) {
+ to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
- memcpy(usb_buf + usb_buf_len, ptr, to_write);
- usb_buf_len += to_write;
+ memcpy(transport_buf + transport_buf_len, ptr, to_write);
+ transport_buf_len += to_write;
ptr += to_write;
len -= to_write;
}
- if (usb_buf_len == USB_BUF_SIZE) {
- r = _command_data(usb, usb_buf, USB_BUF_SIZE);
- if (r != USB_BUF_SIZE) {
+ if (transport_buf_len == TRANSPORT_BUF_SIZE) {
+ r = _command_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
+ if (r != TRANSPORT_BUF_SIZE) {
return -1;
}
- usb_buf_len = 0;
+ transport_buf_len = 0;
}
- if (len > USB_BUF_SIZE) {
- if (usb_buf_len > 0) {
- sprintf(ERROR, "internal error: usb_buf not empty\n");
+ if (len > TRANSPORT_BUF_SIZE) {
+ if (transport_buf_len > 0) {
+ g_error = "internal error: transport_buf not empty";
return -1;
}
- to_write = round_down(len, USB_BUF_SIZE);
- r = _command_data(usb, ptr, to_write);
+ to_write = round_down(len, TRANSPORT_BUF_SIZE);
+ r = _command_data(transport, ptr, to_write);
if (r != to_write) {
return -1;
}
@@ -259,56 +229,49 @@
}
if (len > 0) {
- if (len > USB_BUF_SIZE) {
- sprintf(ERROR, "internal error: too much left for usb_buf\n");
+ if (len > TRANSPORT_BUF_SIZE) {
+ g_error = "internal error: too much left for transport_buf";
return -1;
}
- memcpy(usb_buf, ptr, len);
- usb_buf_len = len;
+ memcpy(transport_buf, ptr, len);
+ transport_buf_len = len;
}
return 0;
}
-static int fb_download_data_sparse_flush(usb_handle *usb)
-{
- int r;
-
- if (usb_buf_len > 0) {
- r = _command_data(usb, usb_buf, usb_buf_len);
- if (r != usb_buf_len) {
+static int fb_download_data_sparse_flush(Transport* transport) {
+ if (transport_buf_len > 0) {
+ if (_command_data(transport, transport_buf, transport_buf_len) != transport_buf_len) {
return -1;
}
- usb_buf_len = 0;
+ transport_buf_len = 0;
}
-
return 0;
}
-int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s)
-{
- char cmd[64];
- int r;
+int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
int size = sparse_file_len(s, true, false);
if (size <= 0) {
return -1;
}
- sprintf(cmd, "download:%08x", size);
- r = _command_start(usb, cmd, size, 0);
+ char cmd[64];
+ snprintf(cmd, sizeof(cmd), "download:%08x", size);
+ int r = _command_start(transport, cmd, size, 0);
if (r < 0) {
return -1;
}
- r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, usb);
+ r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, transport);
if (r < 0) {
return -1;
}
- r = fb_download_data_sparse_flush(usb);
+ r = fb_download_data_sparse_flush(transport);
if (r < 0) {
return -1;
}
- return _command_end(usb);
+ return _command_end(transport);
}
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
new file mode 100644
index 0000000..e56ffcf
--- /dev/null
+++ b/fastboot/socket.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2015 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 "socket.h"
+
+#include <android-base/errors.h>
+#include <android-base/stringprintf.h>
+
+Socket::Socket(cutils_socket_t sock) : sock_(sock) {}
+
+Socket::~Socket() {
+ Close();
+}
+
+int Socket::Close() {
+ int ret = 0;
+
+ if (sock_ != INVALID_SOCKET) {
+ ret = socket_close(sock_);
+ sock_ = INVALID_SOCKET;
+ }
+
+ return ret;
+}
+
+ssize_t Socket::ReceiveAll(void* data, size_t length, int timeout_ms) {
+ size_t total = 0;
+
+ while (total < length) {
+ ssize_t bytes = Receive(reinterpret_cast<char*>(data) + total, length - total, timeout_ms);
+
+ if (bytes == -1) {
+ if (total == 0) {
+ return -1;
+ }
+ break;
+ }
+ total += bytes;
+ }
+
+ return total;
+}
+
+int Socket::GetLocalPort() {
+ return socket_get_local_port(sock_);
+}
+
+// According to Windows setsockopt() documentation, if a Windows socket times out during send() or
+// recv() the state is indeterminate and should not be used. Our UDP protocol relies on being able
+// to re-send after a timeout, so we must use select() rather than SO_RCVTIMEO.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx.
+bool Socket::WaitForRecv(int timeout_ms) {
+ receive_timed_out_ = false;
+
+ // In our usage |timeout_ms| <= 0 means block forever, so just return true immediately and let
+ // the subsequent recv() do the blocking.
+ if (timeout_ms <= 0) {
+ return true;
+ }
+
+ // select() doesn't always check this case and will block for |timeout_ms| if we let it.
+ if (sock_ == INVALID_SOCKET) {
+ return false;
+ }
+
+ fd_set read_set;
+ FD_ZERO(&read_set);
+ FD_SET(sock_, &read_set);
+
+ timeval timeout;
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+
+ int result = TEMP_FAILURE_RETRY(select(sock_ + 1, &read_set, nullptr, nullptr, &timeout));
+
+ if (result == 0) {
+ receive_timed_out_ = true;
+ }
+ return result == 1;
+}
+
+// Implements the Socket interface for UDP.
+class UdpSocket : public Socket {
+ public:
+ enum class Type { kClient, kServer };
+
+ UdpSocket(Type type, cutils_socket_t sock);
+
+ bool Send(const void* data, size_t length) override;
+ bool Send(std::vector<cutils_socket_buffer_t> buffers) override;
+ ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+
+ private:
+ std::unique_ptr<sockaddr_storage> addr_;
+ socklen_t addr_size_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(UdpSocket);
+};
+
+UdpSocket::UdpSocket(Type type, cutils_socket_t sock) : Socket(sock) {
+ // Only servers need to remember addresses; clients are connected to a server in NewClient()
+ // so will send to that server without needing to specify the address again.
+ if (type == Type::kServer) {
+ addr_.reset(new sockaddr_storage);
+ addr_size_ = sizeof(*addr_);
+ memset(addr_.get(), 0, addr_size_);
+ }
+}
+
+bool UdpSocket::Send(const void* data, size_t length) {
+ return TEMP_FAILURE_RETRY(sendto(sock_, reinterpret_cast<const char*>(data), length, 0,
+ reinterpret_cast<sockaddr*>(addr_.get()), addr_size_)) ==
+ static_cast<ssize_t>(length);
+}
+
+bool UdpSocket::Send(std::vector<cutils_socket_buffer_t> buffers) {
+ size_t total_length = 0;
+ for (const auto& buffer : buffers) {
+ total_length += buffer.length;
+ }
+
+ return TEMP_FAILURE_RETRY(socket_send_buffers_function_(
+ sock_, buffers.data(), buffers.size())) == static_cast<ssize_t>(total_length);
+}
+
+ssize_t UdpSocket::Receive(void* data, size_t length, int timeout_ms) {
+ if (!WaitForRecv(timeout_ms)) {
+ return -1;
+ }
+
+ socklen_t* addr_size_ptr = nullptr;
+ if (addr_ != nullptr) {
+ // Reset addr_size as it may have been modified by previous recvfrom() calls.
+ addr_size_ = sizeof(*addr_);
+ addr_size_ptr = &addr_size_;
+ }
+
+ return TEMP_FAILURE_RETRY(recvfrom(sock_, reinterpret_cast<char*>(data), length, 0,
+ reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr));
+}
+
+// Implements the Socket interface for TCP.
+class TcpSocket : public Socket {
+ public:
+ explicit TcpSocket(cutils_socket_t sock) : Socket(sock) {}
+
+ bool Send(const void* data, size_t length) override;
+ bool Send(std::vector<cutils_socket_buffer_t> buffers) override;
+ ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+
+ std::unique_ptr<Socket> Accept() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TcpSocket);
+};
+
+bool TcpSocket::Send(const void* data, size_t length) {
+ while (length > 0) {
+ ssize_t sent =
+ TEMP_FAILURE_RETRY(send(sock_, reinterpret_cast<const char*>(data), length, 0));
+
+ if (sent == -1) {
+ return false;
+ }
+ length -= sent;
+ }
+
+ return true;
+}
+
+bool TcpSocket::Send(std::vector<cutils_socket_buffer_t> buffers) {
+ while (!buffers.empty()) {
+ ssize_t sent = TEMP_FAILURE_RETRY(
+ socket_send_buffers_function_(sock_, buffers.data(), buffers.size()));
+
+ if (sent == -1) {
+ return false;
+ }
+
+ // Adjust the buffers to skip past the bytes we've just sent.
+ auto iter = buffers.begin();
+ while (sent > 0) {
+ if (iter->length > static_cast<size_t>(sent)) {
+ // Incomplete buffer write; adjust the buffer to point to the next byte to send.
+ iter->length -= sent;
+ iter->data = reinterpret_cast<const char*>(iter->data) + sent;
+ break;
+ }
+
+ // Complete buffer write; move on to the next buffer.
+ sent -= iter->length;
+ ++iter;
+ }
+
+ // Shortcut the common case: we've written everything remaining.
+ if (iter == buffers.end()) {
+ break;
+ }
+ buffers.erase(buffers.begin(), iter);
+ }
+
+ return true;
+}
+
+ssize_t TcpSocket::Receive(void* data, size_t length, int timeout_ms) {
+ if (!WaitForRecv(timeout_ms)) {
+ return -1;
+ }
+
+ return TEMP_FAILURE_RETRY(recv(sock_, reinterpret_cast<char*>(data), length, 0));
+}
+
+std::unique_ptr<Socket> TcpSocket::Accept() {
+ cutils_socket_t handler = accept(sock_, nullptr, nullptr);
+ if (handler == INVALID_SOCKET) {
+ return nullptr;
+ }
+ return std::unique_ptr<TcpSocket>(new TcpSocket(handler));
+}
+
+std::unique_ptr<Socket> Socket::NewClient(Protocol protocol, const std::string& host, int port,
+ std::string* error) {
+ if (protocol == Protocol::kUdp) {
+ cutils_socket_t sock = socket_network_client(host.c_str(), port, SOCK_DGRAM);
+ if (sock != INVALID_SOCKET) {
+ return std::unique_ptr<UdpSocket>(new UdpSocket(UdpSocket::Type::kClient, sock));
+ }
+ } else {
+ cutils_socket_t sock = socket_network_client(host.c_str(), port, SOCK_STREAM);
+ if (sock != INVALID_SOCKET) {
+ return std::unique_ptr<TcpSocket>(new TcpSocket(sock));
+ }
+ }
+
+ if (error) {
+ *error = android::base::StringPrintf("Failed to connect to %s:%d", host.c_str(), port);
+ }
+ return nullptr;
+}
+
+// This functionality is currently only used by tests so we don't need any error messages.
+std::unique_ptr<Socket> Socket::NewServer(Protocol protocol, int port) {
+ if (protocol == Protocol::kUdp) {
+ cutils_socket_t sock = socket_inaddr_any_server(port, SOCK_DGRAM);
+ if (sock != INVALID_SOCKET) {
+ return std::unique_ptr<UdpSocket>(new UdpSocket(UdpSocket::Type::kServer, sock));
+ }
+ } else {
+ cutils_socket_t sock = socket_inaddr_any_server(port, SOCK_STREAM);
+ if (sock != INVALID_SOCKET) {
+ return std::unique_ptr<TcpSocket>(new TcpSocket(sock));
+ }
+ }
+
+ return nullptr;
+}
+
+std::string Socket::GetErrorMessage() {
+#if defined(_WIN32)
+ DWORD error_code = WSAGetLastError();
+#else
+ int error_code = errno;
+#endif
+ return android::base::SystemErrorCodeToString(error_code);
+}
diff --git a/fastboot/socket.h b/fastboot/socket.h
new file mode 100644
index 0000000..7eaa0ab
--- /dev/null
+++ b/fastboot/socket.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// This file provides a class interface for cross-platform socket functionality. The main fastboot
+// engine should not be using this interface directly, but instead should use a higher-level
+// interface that enforces the fastboot protocol.
+
+#ifndef SOCKET_H_
+#define SOCKET_H_
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <cutils/sockets.h>
+#include <gtest/gtest_prod.h>
+
+// Socket interface to be implemented for each platform.
+class Socket {
+ public:
+ enum class Protocol { kTcp, kUdp };
+
+ // Returns the socket error message. This must be called immediately after a socket failure
+ // before any other system calls are made.
+ static std::string GetErrorMessage();
+
+ // Creates a new client connection. Clients are connected to a specific hostname/port and can
+ // only send to that destination.
+ // On failure, |error| is filled (if non-null) and nullptr is returned.
+ static std::unique_ptr<Socket> NewClient(Protocol protocol, const std::string& hostname,
+ int port, std::string* error);
+
+ // Creates a new server bound to local |port|. This is only meant for testing, during normal
+ // fastboot operation the device acts as the server.
+ // A UDP server saves sender addresses in Receive(), and uses the most recent address during
+ // calls to Send().
+ static std::unique_ptr<Socket> NewServer(Protocol protocol, int port);
+
+ // Destructor closes the socket if it's open.
+ virtual ~Socket();
+
+ // Sends |length| bytes of |data|. For TCP sockets this will continue trying to send until all
+ // bytes are transmitted. Returns true on success.
+ virtual bool Send(const void* data, size_t length) = 0;
+
+ // Sends |buffers| using multi-buffer write, which can be significantly faster than making
+ // multiple calls. For UDP sockets |buffers| are all combined into a single datagram; for
+ // TCP sockets this will continue sending until all buffers are fully transmitted. Returns true
+ // on success.
+ //
+ // Note: This is non-functional for UDP server Sockets because it's not currently needed and
+ // would require an additional sendto() variation of multi-buffer write.
+ virtual bool Send(std::vector<cutils_socket_buffer_t> buffers) = 0;
+
+ // Waits up to |timeout_ms| to receive up to |length| bytes of data. |timout_ms| of 0 will
+ // block forever. Returns the number of bytes received or -1 on error/timeout; see
+ // ReceiveTimedOut() to distinguish between the two.
+ virtual ssize_t Receive(void* data, size_t length, int timeout_ms) = 0;
+
+ // Calls Receive() until exactly |length| bytes have been received or an error occurs.
+ virtual ssize_t ReceiveAll(void* data, size_t length, int timeout_ms);
+
+ // Returns true if the last Receive() call timed out normally and can be retried; fatal errors
+ // or successful reads will return false.
+ bool ReceiveTimedOut() { return receive_timed_out_; }
+
+ // Closes the socket. Returns 0 on success, -1 on error.
+ virtual int Close();
+
+ // Accepts an incoming TCP connection. No effect for UDP sockets. Returns a new Socket
+ // connected to the client on success, nullptr on failure.
+ virtual std::unique_ptr<Socket> Accept() { return nullptr; }
+
+ // Returns the local port the Socket is bound to or -1 on error.
+ int GetLocalPort();
+
+ protected:
+ // Protected constructor to force factory function use.
+ explicit Socket(cutils_socket_t sock);
+
+ // Blocks up to |timeout_ms| until a read is possible on |sock_|, and sets |receive_timed_out_|
+ // as appropriate to help distinguish between normal timeouts and fatal errors. Returns true if
+ // a subsequent recv() on |sock_| will complete without blocking or if |timeout_ms| <= 0.
+ bool WaitForRecv(int timeout_ms);
+
+ cutils_socket_t sock_ = INVALID_SOCKET;
+ bool receive_timed_out_ = false;
+
+ // Non-class functions we want to override during tests to verify functionality. Implementation
+ // should call this rather than using socket_send_buffers() directly.
+ std::function<ssize_t(cutils_socket_t, cutils_socket_buffer_t*, size_t)>
+ socket_send_buffers_function_ = &socket_send_buffers;
+
+ private:
+ FRIEND_TEST(SocketTest, TestTcpSendBuffers);
+ FRIEND_TEST(SocketTest, TestUdpSendBuffers);
+
+ DISALLOW_COPY_AND_ASSIGN(Socket);
+};
+
+#endif // SOCKET_H_
diff --git a/fastboot/socket_mock.cpp b/fastboot/socket_mock.cpp
new file mode 100644
index 0000000..2531b53
--- /dev/null
+++ b/fastboot/socket_mock.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 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 "socket_mock.h"
+
+#include <gtest/gtest.h>
+
+SocketMock::SocketMock() : Socket(INVALID_SOCKET) {}
+
+SocketMock::~SocketMock() {
+ if (!events_.empty()) {
+ ADD_FAILURE() << events_.size() << " event(s) were not handled";
+ }
+}
+
+bool SocketMock::Send(const void* data, size_t length) {
+ if (events_.empty()) {
+ ADD_FAILURE() << "Send() was called when no message was expected";
+ return false;
+ }
+
+ if (events_.front().type != EventType::kSend) {
+ ADD_FAILURE() << "Send() was called out-of-order";
+ return false;
+ }
+
+ std::string message(reinterpret_cast<const char*>(data), length);
+ if (events_.front().message != message) {
+ ADD_FAILURE() << "Send() expected " << events_.front().message << ", but got " << message;
+ return false;
+ }
+
+ bool return_value = events_.front().status;
+ events_.pop();
+ return return_value;
+}
+
+// Mock out multi-buffer send to be one large send, since that's what it should looks like from
+// the user's perspective.
+bool SocketMock::Send(std::vector<cutils_socket_buffer_t> buffers) {
+ std::string data;
+ for (const auto& buffer : buffers) {
+ data.append(reinterpret_cast<const char*>(buffer.data), buffer.length);
+ }
+ return Send(data.data(), data.size());
+}
+
+ssize_t SocketMock::Receive(void* data, size_t length, int /*timeout_ms*/) {
+ if (events_.empty()) {
+ ADD_FAILURE() << "Receive() was called when no message was ready";
+ return -1;
+ }
+
+ const Event& event = events_.front();
+ if (event.type != EventType::kReceive) {
+ ADD_FAILURE() << "Receive() was called out-of-order";
+ return -1;
+ }
+
+ const std::string& message = event.message;
+ if (message.length() > length) {
+ ADD_FAILURE() << "Receive(): not enough bytes (" << length << ") for " << message;
+ return -1;
+ }
+
+ receive_timed_out_ = event.status;
+ ssize_t return_value = message.length();
+
+ // Empty message indicates failure.
+ if (message.empty()) {
+ return_value = -1;
+ } else {
+ memcpy(data, message.data(), message.length());
+ }
+
+ events_.pop();
+ return return_value;
+}
+
+int SocketMock::Close() {
+ return 0;
+}
+
+std::unique_ptr<Socket> SocketMock::Accept() {
+ if (events_.empty()) {
+ ADD_FAILURE() << "Accept() was called when no socket was ready";
+ return nullptr;
+ }
+
+ if (events_.front().type != EventType::kAccept) {
+ ADD_FAILURE() << "Accept() was called out-of-order";
+ return nullptr;
+ }
+
+ std::unique_ptr<Socket> sock = std::move(events_.front().sock);
+ events_.pop();
+ return sock;
+}
+
+void SocketMock::ExpectSend(std::string message) {
+ events_.push(Event(EventType::kSend, std::move(message), true, nullptr));
+}
+
+void SocketMock::ExpectSendFailure(std::string message) {
+ events_.push(Event(EventType::kSend, std::move(message), false, nullptr));
+}
+
+void SocketMock::AddReceive(std::string message) {
+ events_.push(Event(EventType::kReceive, std::move(message), false, nullptr));
+}
+
+void SocketMock::AddReceiveTimeout() {
+ events_.push(Event(EventType::kReceive, "", true, nullptr));
+}
+
+void SocketMock::AddReceiveFailure() {
+ events_.push(Event(EventType::kReceive, "", false, nullptr));
+}
+
+void SocketMock::AddAccept(std::unique_ptr<Socket> sock) {
+ events_.push(Event(EventType::kAccept, "", false, std::move(sock)));
+}
+
+SocketMock::Event::Event(EventType _type, std::string _message, ssize_t _status,
+ std::unique_ptr<Socket> _sock)
+ : type(_type), message(_message), status(_status), sock(std::move(_sock)) {}
diff --git a/fastboot/socket_mock.h b/fastboot/socket_mock.h
new file mode 100644
index 0000000..eacd6bb
--- /dev/null
+++ b/fastboot/socket_mock.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef SOCKET_MOCK_H_
+#define SOCKET_MOCK_H_
+
+#include <memory>
+#include <queue>
+#include <string>
+
+#include <android-base/macros.h>
+
+#include "socket.h"
+
+// A mock Socket implementation to be used for testing. Tests can set expectations for messages
+// to be sent and provide messages to be received in order to verify protocol behavior.
+//
+// Example: testing sending "foo" and receiving "bar".
+// SocketMock mock;
+// mock.ExpectSend("foo");
+// mock.AddReceive("bar");
+// EXPECT_TRUE(DoFooBar(&mock));
+//
+// Example: testing sending "foo" and expecting "bar", but receiving "baz" instead.
+// SocketMock mock;
+// mock.ExpectSend("foo");
+// mock.AddReceive("baz");
+// EXPECT_FALSE(DoFooBar(&mock));
+class SocketMock : public Socket {
+ public:
+ SocketMock();
+ ~SocketMock() override;
+
+ bool Send(const void* data, size_t length) override;
+ bool Send(std::vector<cutils_socket_buffer_t> buffers) override;
+ ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+ int Close() override;
+ virtual std::unique_ptr<Socket> Accept();
+
+ // Adds an expectation for Send().
+ void ExpectSend(std::string message);
+
+ // Adds an expectation for Send() that returns false.
+ void ExpectSendFailure(std::string message);
+
+ // Adds data to provide for Receive().
+ void AddReceive(std::string message);
+
+ // Adds a Receive() timeout after which ReceiveTimedOut() will return true.
+ void AddReceiveTimeout();
+
+ // Adds a Receive() failure after which ReceiveTimedOut() will return false.
+ void AddReceiveFailure();
+
+ // Adds a Socket to return from Accept().
+ void AddAccept(std::unique_ptr<Socket> sock);
+
+ private:
+ enum class EventType { kSend, kReceive, kAccept };
+
+ struct Event {
+ Event(EventType _type, std::string _message, ssize_t _status,
+ std::unique_ptr<Socket> _sock);
+
+ EventType type;
+ std::string message;
+ bool status; // Return value for Send() or timeout status for Receive().
+ std::unique_ptr<Socket> sock;
+ };
+
+ std::queue<Event> events_;
+
+ DISALLOW_COPY_AND_ASSIGN(SocketMock);
+};
+
+#endif // SOCKET_MOCK_H_
diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp
new file mode 100644
index 0000000..373abc3
--- /dev/null
+++ b/fastboot/socket_test.cpp
@@ -0,0 +1,385 @@
+/*
+ * 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.
+ */
+
+// Tests socket functionality using loopback connections. The UDP tests assume that no packets are
+// lost, which should be the case for loopback communication, but is not guaranteed.
+//
+// Also tests our SocketMock class to make sure it works as expected and reports errors properly
+// if the mock expectations aren't met during a test.
+
+#include "socket.h"
+#include "socket_mock.h"
+
+#include <list>
+
+#include <gtest/gtest-spi.h>
+#include <gtest/gtest.h>
+
+static constexpr int kShortTimeoutMs = 10;
+static constexpr int kTestTimeoutMs = 3000;
+
+// Creates connected sockets |server| and |client|. Returns true on success.
+bool MakeConnectedSockets(Socket::Protocol protocol, std::unique_ptr<Socket>* server,
+ std::unique_ptr<Socket>* client,
+ const std::string& hostname = "localhost") {
+ *server = Socket::NewServer(protocol, 0);
+ if (*server == nullptr) {
+ ADD_FAILURE() << "Failed to create server.";
+ return false;
+ }
+
+ *client = Socket::NewClient(protocol, hostname, (*server)->GetLocalPort(), nullptr);
+ if (*client == nullptr) {
+ ADD_FAILURE() << "Failed to create client.";
+ return false;
+ }
+
+ // TCP passes the client off to a new socket.
+ if (protocol == Socket::Protocol::kTcp) {
+ *server = (*server)->Accept();
+ if (*server == nullptr) {
+ ADD_FAILURE() << "Failed to accept client connection.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Sends a string over a Socket. Returns true if the full string (without terminating char)
+// was sent.
+static bool SendString(Socket* sock, const std::string& message) {
+ return sock->Send(message.c_str(), message.length());
+}
+
+// Receives a string from a Socket. Returns true if the full string (without terminating char)
+// was received.
+static bool ReceiveString(Socket* sock, const std::string& message) {
+ std::string received(message.length(), '\0');
+ ssize_t bytes = sock->ReceiveAll(&received[0], received.length(), kTestTimeoutMs);
+ return static_cast<size_t>(bytes) == received.length() && received == message;
+}
+
+// Tests sending packets client -> server, then server -> client.
+TEST(SocketTest, TestSendAndReceive) {
+ std::unique_ptr<Socket> server, client;
+
+ for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+ ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+ EXPECT_TRUE(SendString(client.get(), "foo"));
+ EXPECT_TRUE(ReceiveString(server.get(), "foo"));
+
+ EXPECT_TRUE(SendString(server.get(), "bar baz"));
+ EXPECT_TRUE(ReceiveString(client.get(), "bar baz"));
+ }
+}
+
+TEST(SocketTest, TestReceiveTimeout) {
+ std::unique_ptr<Socket> server, client;
+ char buffer[16];
+
+ for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+ ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+ EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+ EXPECT_TRUE(server->ReceiveTimedOut());
+
+ EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+ EXPECT_TRUE(client->ReceiveTimedOut());
+ }
+
+ // UDP will wait for timeout if the other side closes.
+ ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kUdp, &server, &client));
+ EXPECT_EQ(0, server->Close());
+ EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+ EXPECT_TRUE(client->ReceiveTimedOut());
+}
+
+TEST(SocketTest, TestReceiveFailure) {
+ std::unique_ptr<Socket> server, client;
+ char buffer[16];
+
+ for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+ ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+ EXPECT_EQ(0, server->Close());
+ EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+ EXPECT_FALSE(server->ReceiveTimedOut());
+
+ EXPECT_EQ(0, client->Close());
+ EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+ EXPECT_FALSE(client->ReceiveTimedOut());
+ }
+
+ // TCP knows right away when the other side closes and returns 0 to indicate EOF.
+ ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kTcp, &server, &client));
+ EXPECT_EQ(0, server->Close());
+ EXPECT_EQ(0, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+ EXPECT_FALSE(client->ReceiveTimedOut());
+}
+
+// Tests sending and receiving large packets.
+TEST(SocketTest, TestLargePackets) {
+ std::string message(1024, '\0');
+ std::unique_ptr<Socket> server, client;
+
+ for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+ ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+ // Run through the test a few times.
+ for (int i = 0; i < 10; ++i) {
+ // Use a different message each iteration to prevent false positives.
+ for (size_t j = 0; j < message.length(); ++j) {
+ message[j] = static_cast<char>(i + j);
+ }
+
+ EXPECT_TRUE(SendString(client.get(), message));
+ EXPECT_TRUE(ReceiveString(server.get(), message));
+ }
+ }
+}
+
+// Tests UDP receive overflow when the UDP packet is larger than the receive buffer.
+TEST(SocketTest, TestUdpReceiveOverflow) {
+ std::unique_ptr<Socket> server, client;
+ ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kUdp, &server, &client));
+
+ EXPECT_TRUE(SendString(client.get(), "1234567890"));
+
+ // This behaves differently on different systems, either truncating the packet or returning -1.
+ char buffer[5];
+ ssize_t bytes = server->Receive(buffer, 5, kTestTimeoutMs);
+ if (bytes == 5) {
+ EXPECT_EQ(0, memcmp(buffer, "12345", 5));
+ } else {
+ EXPECT_EQ(-1, bytes);
+ }
+}
+
+// Tests UDP multi-buffer send.
+TEST(SocketTest, TestUdpSendBuffers) {
+ std::unique_ptr<Socket> sock = Socket::NewServer(Socket::Protocol::kUdp, 0);
+ std::vector<std::string> data{"foo", "bar", "12345"};
+ std::vector<cutils_socket_buffer_t> buffers{{data[0].data(), data[0].length()},
+ {data[1].data(), data[1].length()},
+ {data[2].data(), data[2].length()}};
+ ssize_t mock_return_value = 0;
+
+ // Mock out socket_send_buffers() to verify we're sending in the correct buffers and
+ // return |mock_return_value|.
+ sock->socket_send_buffers_function_ = [&buffers, &mock_return_value](
+ cutils_socket_t /*cutils_sock*/, cutils_socket_buffer_t* sent_buffers,
+ size_t num_sent_buffers) -> ssize_t {
+ EXPECT_EQ(buffers.size(), num_sent_buffers);
+ for (size_t i = 0; i < num_sent_buffers; ++i) {
+ EXPECT_EQ(buffers[i].data, sent_buffers[i].data);
+ EXPECT_EQ(buffers[i].length, sent_buffers[i].length);
+ }
+ return mock_return_value;
+ };
+
+ mock_return_value = strlen("foobar12345");
+ EXPECT_TRUE(sock->Send(buffers));
+
+ mock_return_value -= 1;
+ EXPECT_FALSE(sock->Send(buffers));
+
+ mock_return_value = 0;
+ EXPECT_FALSE(sock->Send(buffers));
+
+ mock_return_value = -1;
+ EXPECT_FALSE(sock->Send(buffers));
+}
+
+// Tests TCP re-sending until socket_send_buffers() sends all data. This is a little complicated,
+// but the general idea is that we intercept calls to socket_send_buffers() using a lambda mock
+// function that simulates partial writes.
+TEST(SocketTest, TestTcpSendBuffers) {
+ std::unique_ptr<Socket> sock = Socket::NewServer(Socket::Protocol::kTcp, 0);
+ std::vector<std::string> data{"foo", "bar", "12345"};
+ std::vector<cutils_socket_buffer_t> buffers{{data[0].data(), data[0].length()},
+ {data[1].data(), data[1].length()},
+ {data[2].data(), data[2].length()}};
+
+ // Test breaking up the buffered send at various points.
+ std::list<std::string> test_sends[] = {
+ // Successes.
+ {"foobar12345"},
+ {"f", "oob", "ar12345"},
+ {"fo", "obar12", "345"},
+ {"foo", "bar12345"},
+ {"foob", "ar123", "45"},
+ {"f", "o", "o", "b", "a", "r", "1", "2", "3", "4", "5"},
+
+ // Failures.
+ {},
+ {"f"},
+ {"foo", "bar"},
+ {"fo", "obar12"},
+ {"foobar1234"}
+ };
+
+ for (auto& test : test_sends) {
+ ssize_t bytes_sent = 0;
+ bool expect_success = true;
+
+ // Create a mock function for custom socket_send_buffers() behavior. This function will
+ // check to make sure the input buffers start at the next unsent byte, then return the
+ // number of bytes indicated by the next entry in |test|.
+ sock->socket_send_buffers_function_ = [&bytes_sent, &data, &expect_success, &test](
+ cutils_socket_t /*cutils_sock*/, cutils_socket_buffer_t* buffers,
+ size_t num_buffers) -> ssize_t {
+ EXPECT_TRUE(num_buffers > 0);
+
+ // Failure case - pretend we errored out before sending all the buffers.
+ if (test.empty()) {
+ expect_success = false;
+ return -1;
+ }
+
+ // Count the bytes we've sent to find where the next buffer should start and how many
+ // bytes should be left in it.
+ size_t byte_count = bytes_sent, data_index = 0;
+ while (data_index < data.size()) {
+ if (byte_count >= data[data_index].length()) {
+ byte_count -= data[data_index].length();
+ ++data_index;
+ } else {
+ break;
+ }
+ }
+ void* expected_next_byte = &data[data_index][byte_count];
+ size_t expected_next_size = data[data_index].length() - byte_count;
+
+ EXPECT_EQ(data.size() - data_index, num_buffers);
+ EXPECT_EQ(expected_next_byte, buffers[0].data);
+ EXPECT_EQ(expected_next_size, buffers[0].length);
+
+ std::string to_send = std::move(test.front());
+ test.pop_front();
+ bytes_sent += to_send.length();
+ return to_send.length();
+ };
+
+ EXPECT_EQ(expect_success, sock->Send(buffers));
+ EXPECT_TRUE(test.empty());
+ }
+}
+
+TEST(SocketMockTest, TestSendSuccess) {
+ SocketMock mock;
+
+ mock.ExpectSend("foo");
+ EXPECT_TRUE(SendString(&mock, "foo"));
+
+ mock.ExpectSend("abc");
+ mock.ExpectSend("123");
+ EXPECT_TRUE(SendString(&mock, "abc"));
+ EXPECT_TRUE(SendString(&mock, "123"));
+}
+
+TEST(SocketMockTest, TestSendFailure) {
+ SocketMock* mock = new SocketMock;
+
+ mock->ExpectSendFailure("foo");
+ EXPECT_FALSE(SendString(mock, "foo"));
+
+ EXPECT_NONFATAL_FAILURE(SendString(mock, "foo"), "no message was expected");
+
+ mock->ExpectSend("foo");
+ EXPECT_NONFATAL_FAILURE(SendString(mock, "bar"), "expected foo, but got bar");
+ EXPECT_TRUE(SendString(mock, "foo"));
+
+ mock->AddReceive("foo");
+ EXPECT_NONFATAL_FAILURE(SendString(mock, "foo"), "called out-of-order");
+ EXPECT_TRUE(ReceiveString(mock, "foo"));
+
+ mock->ExpectSend("foo");
+ EXPECT_NONFATAL_FAILURE(delete mock, "1 event(s) were not handled");
+}
+
+TEST(SocketMockTest, TestReceiveSuccess) {
+ SocketMock mock;
+
+ mock.AddReceive("foo");
+ EXPECT_TRUE(ReceiveString(&mock, "foo"));
+
+ mock.AddReceive("abc");
+ mock.AddReceive("123");
+ EXPECT_TRUE(ReceiveString(&mock, "abc"));
+ EXPECT_TRUE(ReceiveString(&mock, "123"));
+
+ // Make sure ReceiveAll() can piece together multiple receives.
+ mock.AddReceive("foo");
+ mock.AddReceive("bar");
+ mock.AddReceive("123");
+ EXPECT_TRUE(ReceiveString(&mock, "foobar123"));
+}
+
+TEST(SocketMockTest, TestReceiveFailure) {
+ SocketMock* mock = new SocketMock;
+
+ mock->AddReceiveFailure();
+ EXPECT_FALSE(ReceiveString(mock, "foo"));
+ EXPECT_FALSE(mock->ReceiveTimedOut());
+
+ mock->AddReceiveTimeout();
+ EXPECT_FALSE(ReceiveString(mock, "foo"));
+ EXPECT_TRUE(mock->ReceiveTimedOut());
+
+ mock->AddReceive("foo");
+ mock->AddReceiveFailure();
+ EXPECT_FALSE(ReceiveString(mock, "foobar"));
+
+ EXPECT_NONFATAL_FAILURE(ReceiveString(mock, "foo"), "no message was ready");
+
+ mock->ExpectSend("foo");
+ EXPECT_NONFATAL_FAILURE(ReceiveString(mock, "foo"), "called out-of-order");
+ EXPECT_TRUE(SendString(mock, "foo"));
+
+ char c;
+ mock->AddReceive("foo");
+ EXPECT_NONFATAL_FAILURE(mock->Receive(&c, 1, 0), "not enough bytes (1) for foo");
+ EXPECT_TRUE(ReceiveString(mock, "foo"));
+
+ mock->AddReceive("foo");
+ EXPECT_NONFATAL_FAILURE(delete mock, "1 event(s) were not handled");
+}
+
+TEST(SocketMockTest, TestAcceptSuccess) {
+ SocketMock mock;
+
+ SocketMock* mock_handler = new SocketMock;
+ mock.AddAccept(std::unique_ptr<SocketMock>(mock_handler));
+ EXPECT_EQ(mock_handler, mock.Accept().get());
+
+ mock.AddAccept(nullptr);
+ EXPECT_EQ(nullptr, mock.Accept().get());
+}
+
+TEST(SocketMockTest, TestAcceptFailure) {
+ SocketMock* mock = new SocketMock;
+
+ EXPECT_NONFATAL_FAILURE(mock->Accept(), "no socket was ready");
+
+ mock->ExpectSend("foo");
+ EXPECT_NONFATAL_FAILURE(mock->Accept(), "called out-of-order");
+ EXPECT_TRUE(SendString(mock, "foo"));
+
+ mock->AddAccept(nullptr);
+ EXPECT_NONFATAL_FAILURE(delete mock, "1 event(s) were not handled");
+}
diff --git a/fastboot/tcp.cpp b/fastboot/tcp.cpp
new file mode 100644
index 0000000..dd6fbf8
--- /dev/null
+++ b/fastboot/tcp.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 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 "tcp.h"
+
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+
+namespace tcp {
+
+static constexpr int kProtocolVersion = 1;
+static constexpr size_t kHandshakeLength = 4;
+static constexpr int kHandshakeTimeoutMs = 2000;
+
+// Extract the big-endian 8-byte message length into a 64-bit number.
+static uint64_t ExtractMessageLength(const void* buffer) {
+ uint64_t ret = 0;
+ for (int i = 0; i < 8; ++i) {
+ ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8);
+ }
+ return ret;
+}
+
+// Encode the 64-bit number into a big-endian 8-byte message length.
+static void EncodeMessageLength(uint64_t length, void* buffer) {
+ for (int i = 0; i < 8; ++i) {
+ reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8);
+ }
+}
+
+class TcpTransport : public Transport {
+ public:
+ // Factory function so we can return nullptr if initialization fails.
+ static std::unique_ptr<TcpTransport> NewTransport(std::unique_ptr<Socket> socket,
+ std::string* error);
+
+ ~TcpTransport() override = default;
+
+ ssize_t Read(void* data, size_t length) override;
+ ssize_t Write(const void* data, size_t length) override;
+ int Close() override;
+
+ private:
+ explicit TcpTransport(std::unique_ptr<Socket> sock) : socket_(std::move(sock)) {}
+
+ // Connects to the device and performs the initial handshake. Returns false and fills |error|
+ // on failure.
+ bool InitializeProtocol(std::string* error);
+
+ std::unique_ptr<Socket> socket_;
+ uint64_t message_bytes_left_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(TcpTransport);
+};
+
+std::unique_ptr<TcpTransport> TcpTransport::NewTransport(std::unique_ptr<Socket> socket,
+ std::string* error) {
+ std::unique_ptr<TcpTransport> transport(new TcpTransport(std::move(socket)));
+
+ if (!transport->InitializeProtocol(error)) {
+ return nullptr;
+ }
+
+ return transport;
+}
+
+// These error strings are checked in tcp_test.cpp and should be kept in sync.
+bool TcpTransport::InitializeProtocol(std::string* error) {
+ std::string handshake_message(android::base::StringPrintf("FB%02d", kProtocolVersion));
+
+ if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) {
+ *error = android::base::StringPrintf("Failed to send initialization message (%s)",
+ Socket::GetErrorMessage().c_str());
+ return false;
+ }
+
+ char buffer[kHandshakeLength + 1];
+ buffer[kHandshakeLength] = '\0';
+ if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) != kHandshakeLength) {
+ *error = android::base::StringPrintf(
+ "No initialization message received (%s). Target may not support TCP fastboot",
+ Socket::GetErrorMessage().c_str());
+ return false;
+ }
+
+ if (memcmp(buffer, "FB", 2) != 0) {
+ *error = "Unrecognized initialization message. Target may not support TCP fastboot";
+ return false;
+ }
+
+ int version = 0;
+ if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
+ *error = android::base::StringPrintf("Unknown TCP protocol version %s (host version %02d)",
+ buffer + 2, kProtocolVersion);
+ return false;
+ }
+
+ error->clear();
+ return true;
+}
+
+ssize_t TcpTransport::Read(void* data, size_t length) {
+ if (socket_ == nullptr) {
+ return -1;
+ }
+
+ // Unless we're mid-message, read the next 8-byte message length.
+ if (message_bytes_left_ == 0) {
+ char buffer[8];
+ if (socket_->ReceiveAll(buffer, 8, 0) != 8) {
+ Close();
+ return -1;
+ }
+ message_bytes_left_ = ExtractMessageLength(buffer);
+ }
+
+ // Now read the message (up to |length| bytes).
+ if (length > message_bytes_left_) {
+ length = message_bytes_left_;
+ }
+ ssize_t bytes_read = socket_->ReceiveAll(data, length, 0);
+ if (bytes_read == -1) {
+ Close();
+ } else {
+ message_bytes_left_ -= bytes_read;
+ }
+ return bytes_read;
+}
+
+ssize_t TcpTransport::Write(const void* data, size_t length) {
+ if (socket_ == nullptr) {
+ return -1;
+ }
+
+ // Use multi-buffer writes for better performance.
+ char header[8];
+ EncodeMessageLength(length, header);
+ if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, length}})) {
+ Close();
+ return -1;
+ }
+
+ return length;
+}
+
+int TcpTransport::Close() {
+ if (socket_ == nullptr) {
+ return 0;
+ }
+
+ int result = socket_->Close();
+ socket_.reset();
+ return result;
+}
+
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
+ return internal::Connect(Socket::NewClient(Socket::Protocol::kTcp, hostname, port, error),
+ error);
+}
+
+namespace internal {
+
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) {
+ if (sock == nullptr) {
+ // If Socket creation failed |error| is already set.
+ return nullptr;
+ }
+
+ return TcpTransport::NewTransport(std::move(sock), error);
+}
+
+} // namespace internal
+
+} // namespace tcp
diff --git a/fastboot/tcp.h b/fastboot/tcp.h
new file mode 100644
index 0000000..aa3ef13
--- /dev/null
+++ b/fastboot/tcp.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef TCP_H_
+#define TCP_H_
+
+#include <memory>
+#include <string>
+
+#include <android-base/macros.h>
+
+#include "socket.h"
+#include "transport.h"
+
+namespace tcp {
+
+constexpr int kDefaultPort = 5554;
+
+// Returns a newly allocated Transport object connected to |hostname|:|port|. On failure, |error| is
+// filled and nullptr is returned.
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error);
+
+// Internal namespace for test use only.
+namespace internal {
+
+// Creates a TCP Transport object but using a given Socket instead of connecting to a hostname.
+// Used for unit tests to create a Transport object that uses a SocketMock.
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error);
+
+} // namespace internal
+
+} // namespace tcp
+
+#endif // TCP_H_
diff --git a/fastboot/tcp_test.cpp b/fastboot/tcp_test.cpp
new file mode 100644
index 0000000..6e867ae
--- /dev/null
+++ b/fastboot/tcp_test.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2016 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 "tcp.h"
+
+#include <gtest/gtest.h>
+
+#include "socket_mock.h"
+
+TEST(TcpConnectTest, TestSuccess) {
+ std::unique_ptr<SocketMock> mock(new SocketMock);
+ mock->ExpectSend("FB01");
+ mock->AddReceive("FB01");
+
+ std::string error;
+ EXPECT_NE(nullptr, tcp::internal::Connect(std::move(mock), &error));
+ EXPECT_EQ("", error);
+}
+
+TEST(TcpConnectTest, TestNewerVersionSuccess) {
+ std::unique_ptr<SocketMock> mock(new SocketMock);
+ mock->ExpectSend("FB01");
+ mock->AddReceive("FB99");
+
+ std::string error;
+ EXPECT_NE(nullptr, tcp::internal::Connect(std::move(mock), &error));
+ EXPECT_EQ("", error);
+}
+
+TEST(TcpConnectTest, TestSendFailure) {
+ std::unique_ptr<SocketMock> mock(new SocketMock);
+ mock->ExpectSendFailure("FB01");
+
+ std::string error;
+ EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));
+ EXPECT_NE(std::string::npos, error.find("Failed to send initialization message"));
+}
+
+TEST(TcpConnectTest, TestNoResponseFailure) {
+ std::unique_ptr<SocketMock> mock(new SocketMock);
+ mock->ExpectSend("FB01");
+ mock->AddReceiveFailure();
+
+ std::string error;
+ EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));
+ EXPECT_NE(std::string::npos, error.find("No initialization message received"));
+}
+
+TEST(TcpConnectTest, TestBadResponseFailure) {
+ std::unique_ptr<SocketMock> mock(new SocketMock);
+ mock->ExpectSend("FB01");
+ mock->AddReceive("XX01");
+
+ std::string error;
+ EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));
+ EXPECT_NE(std::string::npos, error.find("Unrecognized initialization message"));
+}
+
+TEST(TcpConnectTest, TestUnknownVersionFailure) {
+ std::unique_ptr<SocketMock> mock(new SocketMock);
+ mock->ExpectSend("FB01");
+ mock->AddReceive("FB00");
+
+ std::string error;
+ EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));
+ EXPECT_EQ("Unknown TCP protocol version 00 (host version 01)", error);
+}
+
+// Fixture to configure a SocketMock for a successful TCP connection.
+class TcpTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ mock_ = new SocketMock;
+ mock_->ExpectSend("FB01");
+ mock_->AddReceive("FB01");
+
+ std::string error;
+ transport_ = tcp::internal::Connect(std::unique_ptr<Socket>(mock_), &error);
+ ASSERT_NE(nullptr, transport_);
+ ASSERT_EQ("", error);
+ };
+
+ // Writes |message| to |transport_|, returns true on success.
+ bool Write(const std::string& message) {
+ return transport_->Write(message.data(), message.length()) ==
+ static_cast<ssize_t>(message.length());
+ }
+
+ // Reads from |transport_|, returns true if it matches |message|.
+ bool Read(const std::string& message) {
+ std::string buffer(message.length(), '\0');
+ return transport_->Read(&buffer[0], buffer.length()) ==
+ static_cast<ssize_t>(message.length()) &&
+ buffer == message;
+ }
+
+ // Use a raw SocketMock* here because we pass ownership to the Transport object, but we still
+ // need access to configure mock expectations.
+ SocketMock* mock_ = nullptr;
+ std::unique_ptr<Transport> transport_;
+};
+
+TEST_F(TcpTest, TestWriteSuccess) {
+ mock_->ExpectSend(std::string{0, 0, 0, 0, 0, 0, 0, 3} + "foo");
+
+ EXPECT_TRUE(Write("foo"));
+}
+
+TEST_F(TcpTest, TestReadSuccess) {
+ mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 3});
+ mock_->AddReceive("foo");
+
+ EXPECT_TRUE(Read("foo"));
+}
+
+// Tests that fragmented TCP reads are handled properly.
+TEST_F(TcpTest, TestReadFragmentSuccess) {
+ mock_->AddReceive(std::string{0, 0, 0, 0});
+ mock_->AddReceive(std::string{0, 0, 0, 3});
+ mock_->AddReceive("f");
+ mock_->AddReceive("o");
+ mock_->AddReceive("o");
+
+ EXPECT_TRUE(Read("foo"));
+}
+
+TEST_F(TcpTest, TestLargeWriteSuccess) {
+ // 0x100000 = 1MiB.
+ std::string data(0x100000, '\0');
+ for (size_t i = 0; i < data.length(); ++i) {
+ data[i] = i;
+ }
+ mock_->ExpectSend(std::string{0, 0, 0, 0, 0, 0x10, 0, 0} + data);
+
+ EXPECT_TRUE(Write(data));
+}
+
+TEST_F(TcpTest, TestLargeReadSuccess) {
+ // 0x100000 = 1MiB.
+ std::string data(0x100000, '\0');
+ for (size_t i = 0; i < data.length(); ++i) {
+ data[i] = i;
+ }
+ mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0x10, 0, 0});
+ mock_->AddReceive(data);
+
+ EXPECT_TRUE(Read(data));
+}
+
+// Tests a few sample fastboot protocol commands.
+TEST_F(TcpTest, TestFastbootProtocolSuccess) {
+ mock_->ExpectSend(std::string{0, 0, 0, 0, 0, 0, 0, 14} + "getvar:version");
+ mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 7});
+ mock_->AddReceive("OKAY0.4");
+
+ mock_->ExpectSend(std::string{0, 0, 0, 0, 0, 0, 0, 10} + "getvar:all");
+ mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 16});
+ mock_->AddReceive("INFOversion: 0.4");
+ mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 12});
+ mock_->AddReceive("INFOfoo: bar");
+ mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 4});
+ mock_->AddReceive("OKAY");
+
+ EXPECT_TRUE(Write("getvar:version"));
+ EXPECT_TRUE(Read("OKAY0.4"));
+
+ EXPECT_TRUE(Write("getvar:all"));
+ EXPECT_TRUE(Read("INFOversion: 0.4"));
+ EXPECT_TRUE(Read("INFOfoo: bar"));
+ EXPECT_TRUE(Read("OKAY"));
+}
+
+TEST_F(TcpTest, TestReadLengthFailure) {
+ mock_->AddReceiveFailure();
+
+ char buffer[16];
+ EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));
+}
+
+TEST_F(TcpTest, TestReadDataFailure) {
+ mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 3});
+ mock_->AddReceiveFailure();
+
+ char buffer[16];
+ EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));
+}
+
+TEST_F(TcpTest, TestWriteFailure) {
+ mock_->ExpectSendFailure(std::string{0, 0, 0, 0, 0, 0, 0, 3} + "foo");
+
+ EXPECT_EQ(-1, transport_->Write("foo", 3));
+}
+
+TEST_F(TcpTest, TestTransportClose) {
+ EXPECT_EQ(0, transport_->Close());
+
+ // After closing, Transport Read()/Write() should return -1 without actually attempting any
+ // network operations.
+ char buffer[16];
+ EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));
+ EXPECT_EQ(-1, transport_->Write("foo", 3));
+}
diff --git a/fastboot/transport.h b/fastboot/transport.h
new file mode 100644
index 0000000..67d01f9
--- /dev/null
+++ b/fastboot/transport.h
@@ -0,0 +1,48 @@
+/*
+ * 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 TRANSPORT_H_
+#define TRANSPORT_H_
+
+#include <android-base/macros.h>
+
+// General interface to allow the fastboot protocol to be used over different
+// types of transports.
+class Transport {
+ public:
+ Transport() = default;
+ virtual ~Transport() = default;
+
+ // Reads |len| bytes into |data|. Returns the number of bytes actually
+ // read or -1 on error.
+ virtual ssize_t Read(void* data, size_t len) = 0;
+
+ // Writes |len| bytes from |data|. Returns the number of bytes actually
+ // written or -1 on error.
+ virtual ssize_t Write(const void* data, size_t len) = 0;
+
+ // Closes the underlying transport. Returns 0 on success.
+ virtual int Close() = 0;
+
+ // Blocks until the transport disconnects. Transports that don't support
+ // this will return immediately. Returns 0 on success.
+ virtual int WaitForDisconnect() { return 0; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Transport);
+};
+
+#endif // TRANSPORT_H_
diff --git a/fastboot/udp.cpp b/fastboot/udp.cpp
new file mode 100644
index 0000000..53fb347
--- /dev/null
+++ b/fastboot/udp.cpp
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// This file implements the fastboot UDP protocol; see fastboot_protocol.txt for documentation.
+
+#include "udp.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+
+#include "socket.h"
+
+namespace udp {
+
+using namespace internal;
+
+constexpr size_t kMinPacketSize = 512;
+constexpr size_t kHeaderSize = 4;
+
+enum Index {
+ kIndexId = 0,
+ kIndexFlags = 1,
+ kIndexSeqH = 2,
+ kIndexSeqL = 3,
+};
+
+// Extracts a big-endian uint16_t from a byte array.
+static uint16_t ExtractUint16(const uint8_t* bytes) {
+ return (static_cast<uint16_t>(bytes[0]) << 8) | bytes[1];
+}
+
+// Packet header handling.
+class Header {
+ public:
+ Header();
+ ~Header() = default;
+
+ uint8_t id() const { return bytes_[kIndexId]; }
+ const uint8_t* bytes() const { return bytes_; }
+
+ void Set(uint8_t id, uint16_t sequence, Flag flag);
+
+ // Checks whether |response| is a match for this header.
+ bool Matches(const uint8_t* response);
+
+ private:
+ uint8_t bytes_[kHeaderSize];
+};
+
+Header::Header() {
+ Set(kIdError, 0, kFlagNone);
+}
+
+void Header::Set(uint8_t id, uint16_t sequence, Flag flag) {
+ bytes_[kIndexId] = id;
+ bytes_[kIndexFlags] = flag;
+ bytes_[kIndexSeqH] = sequence >> 8;
+ bytes_[kIndexSeqL] = sequence;
+}
+
+bool Header::Matches(const uint8_t* response) {
+ // Sequence numbers must be the same to match, but the response ID can either be the same
+ // or an error response which is always accepted.
+ return bytes_[kIndexSeqH] == response[kIndexSeqH] &&
+ bytes_[kIndexSeqL] == response[kIndexSeqL] &&
+ (bytes_[kIndexId] == response[kIndexId] || response[kIndexId] == kIdError);
+}
+
+// Implements the Transport interface to work with the fastboot engine.
+class UdpTransport : public Transport {
+ public:
+ // Factory function so we can return nullptr if initialization fails.
+ static std::unique_ptr<UdpTransport> NewTransport(std::unique_ptr<Socket> socket,
+ std::string* error);
+ ~UdpTransport() override = default;
+
+ ssize_t Read(void* data, size_t length) override;
+ ssize_t Write(const void* data, size_t length) override;
+ int Close() override;
+
+ private:
+ explicit UdpTransport(std::unique_ptr<Socket> socket) : socket_(std::move(socket)) {}
+
+ // Performs the UDP initialization procedure. Returns true on success.
+ bool InitializeProtocol(std::string* error);
+
+ // Sends |length| bytes from |data| and waits for the response packet up to |attempts| times.
+ // Continuation packets are handled automatically and any return data is written to |rx_data|.
+ // Excess bytes that cannot fit in |rx_data| are dropped.
+ // On success, returns the number of response data bytes received, which may be greater than
+ // |rx_length|. On failure, returns -1 and fills |error| on failure.
+ ssize_t SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+ size_t rx_length, int attempts, std::string* error);
+
+ // Helper for SendData(); sends a single packet and handles the response. |header| specifies
+ // the initial outgoing packet information but may be modified by this function.
+ ssize_t SendSinglePacketHelper(Header* header, const uint8_t* tx_data, size_t tx_length,
+ uint8_t* rx_data, size_t rx_length, int attempts,
+ std::string* error);
+
+ std::unique_ptr<Socket> socket_;
+ int sequence_ = -1;
+ size_t max_data_length_ = kMinPacketSize - kHeaderSize;
+ std::vector<uint8_t> rx_packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(UdpTransport);
+};
+
+std::unique_ptr<UdpTransport> UdpTransport::NewTransport(std::unique_ptr<Socket> socket,
+ std::string* error) {
+ std::unique_ptr<UdpTransport> transport(new UdpTransport(std::move(socket)));
+
+ if (!transport->InitializeProtocol(error)) {
+ return nullptr;
+ }
+
+ return transport;
+}
+
+bool UdpTransport::InitializeProtocol(std::string* error) {
+ uint8_t rx_data[4];
+
+ sequence_ = 0;
+ rx_packet_.resize(kMinPacketSize);
+
+ // First send the query packet to sync with the target. Only attempt this a small number of
+ // times so we can fail out quickly if the target isn't available.
+ ssize_t rx_bytes = SendData(kIdDeviceQuery, nullptr, 0, rx_data, sizeof(rx_data),
+ kMaxConnectAttempts, error);
+ if (rx_bytes == -1) {
+ return false;
+ } else if (rx_bytes < 2) {
+ *error = "invalid query response from target";
+ return false;
+ }
+ // The first two bytes contain the next expected sequence number.
+ sequence_ = ExtractUint16(rx_data);
+
+ // Now send the initialization packet with our version and maximum packet size.
+ uint8_t init_data[] = {kProtocolVersion >> 8, kProtocolVersion & 0xFF,
+ kHostMaxPacketSize >> 8, kHostMaxPacketSize & 0xFF};
+ rx_bytes = SendData(kIdInitialization, init_data, sizeof(init_data), rx_data, sizeof(rx_data),
+ kMaxTransmissionAttempts, error);
+ if (rx_bytes == -1) {
+ return false;
+ } else if (rx_bytes < 4) {
+ *error = "invalid initialization response from target";
+ return false;
+ }
+
+ // The first two data bytes contain the version, the second two bytes contain the target max
+ // supported packet size, which must be at least 512 bytes.
+ uint16_t version = ExtractUint16(rx_data);
+ if (version < kProtocolVersion) {
+ *error = android::base::StringPrintf("target reported invalid protocol version %d",
+ version);
+ return false;
+ }
+ uint16_t packet_size = ExtractUint16(rx_data + 2);
+ if (packet_size < kMinPacketSize) {
+ *error = android::base::StringPrintf("target reported invalid packet size %d", packet_size);
+ return false;
+ }
+
+ packet_size = std::min(kHostMaxPacketSize, packet_size);
+ max_data_length_ = packet_size - kHeaderSize;
+ rx_packet_.resize(packet_size);
+
+ return true;
+}
+
+// SendData() is just responsible for chunking |data| into packets until it's all been sent.
+// Per-packet timeout/retransmission logic is done in SendSinglePacketHelper().
+ssize_t UdpTransport::SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+ size_t rx_length, int attempts, std::string* error) {
+ if (socket_ == nullptr) {
+ *error = "socket is closed";
+ return -1;
+ }
+
+ Header header;
+ size_t packet_data_length;
+ ssize_t ret = 0;
+ // We often send header-only packets with no data as part of the protocol, so always send at
+ // least once even if |length| == 0, then repeat until we've sent all of |data|.
+ do {
+ // Set the continuation flag and truncate packet data if needed.
+ if (tx_length > max_data_length_) {
+ packet_data_length = max_data_length_;
+ header.Set(id, sequence_, kFlagContinuation);
+ } else {
+ packet_data_length = tx_length;
+ header.Set(id, sequence_, kFlagNone);
+ }
+
+ ssize_t bytes = SendSinglePacketHelper(&header, tx_data, packet_data_length, rx_data,
+ rx_length, attempts, error);
+
+ // Advance our read and write buffers for the next packet. Keep going even if we run out
+ // of receive buffer space so we can detect overflows.
+ if (bytes == -1) {
+ return -1;
+ } else if (static_cast<size_t>(bytes) < rx_length) {
+ rx_data += bytes;
+ rx_length -= bytes;
+ } else {
+ rx_data = nullptr;
+ rx_length = 0;
+ }
+
+ tx_length -= packet_data_length;
+ tx_data += packet_data_length;
+
+ ret += bytes;
+ } while (tx_length > 0);
+
+ return ret;
+}
+
+ssize_t UdpTransport::SendSinglePacketHelper(
+ Header* header, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+ size_t rx_length, const int attempts, std::string* error) {
+ ssize_t total_data_bytes = 0;
+ error->clear();
+
+ int attempts_left = attempts;
+ while (attempts_left > 0) {
+ if (!socket_->Send({{header->bytes(), kHeaderSize}, {tx_data, tx_length}})) {
+ *error = Socket::GetErrorMessage();
+ return -1;
+ }
+
+ // Keep receiving until we get a matching response or we timeout.
+ ssize_t bytes = 0;
+ do {
+ bytes = socket_->Receive(rx_packet_.data(), rx_packet_.size(), kResponseTimeoutMs);
+ if (bytes == -1) {
+ if (socket_->ReceiveTimedOut()) {
+ break;
+ }
+ *error = Socket::GetErrorMessage();
+ return -1;
+ } else if (bytes < static_cast<ssize_t>(kHeaderSize)) {
+ *error = "protocol error: incomplete header";
+ return -1;
+ }
+ } while (!header->Matches(rx_packet_.data()));
+
+ if (socket_->ReceiveTimedOut()) {
+ --attempts_left;
+ continue;
+ }
+ ++sequence_;
+
+ // Save to |error| or |rx_data| as appropriate.
+ if (rx_packet_[kIndexId] == kIdError) {
+ error->append(rx_packet_.data() + kHeaderSize, rx_packet_.data() + bytes);
+ } else {
+ total_data_bytes += bytes - kHeaderSize;
+ size_t rx_data_bytes = std::min<size_t>(bytes - kHeaderSize, rx_length);
+ if (rx_data_bytes > 0) {
+ memcpy(rx_data, rx_packet_.data() + kHeaderSize, rx_data_bytes);
+ rx_data += rx_data_bytes;
+ rx_length -= rx_data_bytes;
+ }
+ }
+
+ // If the response has a continuation flag we need to prompt for more data by sending
+ // an empty packet.
+ if (rx_packet_[kIndexFlags] & kFlagContinuation) {
+ // We got a valid response so reset our attempt counter.
+ attempts_left = attempts;
+ header->Set(rx_packet_[kIndexId], sequence_, kFlagNone);
+ tx_data = nullptr;
+ tx_length = 0;
+ continue;
+ }
+
+ break;
+ }
+
+ if (attempts_left <= 0) {
+ *error = "no response from target";
+ return -1;
+ }
+
+ if (rx_packet_[kIndexId] == kIdError) {
+ *error = "target reported error: " + *error;
+ return -1;
+ }
+
+ return total_data_bytes;
+}
+
+ssize_t UdpTransport::Read(void* data, size_t length) {
+ // Read from the target by sending an empty packet.
+ std::string error;
+ ssize_t bytes = SendData(kIdFastboot, nullptr, 0, reinterpret_cast<uint8_t*>(data), length,
+ kMaxTransmissionAttempts, &error);
+
+ if (bytes == -1) {
+ fprintf(stderr, "UDP error: %s\n", error.c_str());
+ return -1;
+ } else if (static_cast<size_t>(bytes) > length) {
+ // Fastboot protocol error: the target sent more data than our fastboot engine was prepared
+ // to receive.
+ fprintf(stderr, "UDP error: receive overflow, target sent too much fastboot data\n");
+ return -1;
+ }
+
+ return bytes;
+}
+
+ssize_t UdpTransport::Write(const void* data, size_t length) {
+ std::string error;
+ ssize_t bytes = SendData(kIdFastboot, reinterpret_cast<const uint8_t*>(data), length, nullptr,
+ 0, kMaxTransmissionAttempts, &error);
+
+ if (bytes == -1) {
+ fprintf(stderr, "UDP error: %s\n", error.c_str());
+ return -1;
+ } else if (bytes > 0) {
+ // UDP protocol error: only empty ACK packets are allowed when writing to a device.
+ fprintf(stderr, "UDP error: target sent fastboot data out-of-turn\n");
+ return -1;
+ }
+
+ return length;
+}
+
+int UdpTransport::Close() {
+ if (socket_ == nullptr) {
+ return 0;
+ }
+
+ int result = socket_->Close();
+ socket_.reset();
+ return result;
+}
+
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
+ return internal::Connect(Socket::NewClient(Socket::Protocol::kUdp, hostname, port, error),
+ error);
+}
+
+namespace internal {
+
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) {
+ if (sock == nullptr) {
+ // If Socket creation failed |error| is already set.
+ return nullptr;
+ }
+
+ return UdpTransport::NewTransport(std::move(sock), error);
+}
+
+} // namespace internal
+
+} // namespace udp
diff --git a/fastboot/udp.h b/fastboot/udp.h
new file mode 100644
index 0000000..14f5b35
--- /dev/null
+++ b/fastboot/udp.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef UDP_H_
+#define UDP_H_
+
+#include <memory>
+#include <string>
+
+#include "socket.h"
+#include "transport.h"
+
+namespace udp {
+
+constexpr int kDefaultPort = 5554;
+
+// Returns a newly allocated Transport object connected to |hostname|:|port|. On failure, |error| is
+// filled and nullptr is returned.
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error);
+
+// Internal namespace for test use only.
+namespace internal {
+
+constexpr uint16_t kProtocolVersion = 1;
+
+// This will be negotiated with the device so may end up being smaller.
+constexpr uint16_t kHostMaxPacketSize = 8192;
+
+// Retransmission constants. Retransmission timeout must be at least 500ms, and the host must
+// attempt to send packets for at least 1 minute once the device has connected. See
+// fastboot_protocol.txt for more information.
+constexpr int kResponseTimeoutMs = 500;
+constexpr int kMaxConnectAttempts = 4;
+constexpr int kMaxTransmissionAttempts = 60 * 1000 / kResponseTimeoutMs;
+
+enum Id : uint8_t {
+ kIdError = 0x00,
+ kIdDeviceQuery = 0x01,
+ kIdInitialization = 0x02,
+ kIdFastboot = 0x03
+};
+
+enum Flag : uint8_t {
+ kFlagNone = 0x00,
+ kFlagContinuation = 0x01
+};
+
+// Creates a UDP Transport object using a given Socket. Used for unit tests to create a Transport
+// object that uses a SocketMock.
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error);
+
+} // namespace internal
+
+} // namespace udp
+
+#endif // UDP_H_
diff --git a/fastboot/udp_test.cpp b/fastboot/udp_test.cpp
new file mode 100644
index 0000000..ff8cf0f
--- /dev/null
+++ b/fastboot/udp_test.cpp
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "udp.h"
+
+#include <gtest/gtest.h>
+
+#include "socket.h"
+#include "socket_mock.h"
+
+using namespace udp;
+using namespace udp::internal;
+
+// Some possible corner case sequence numbers we want to check.
+static const uint16_t kTestSequenceNumbers[] = {0x0000, 0x0001, 0x00FF, 0x0100,
+ 0x7FFF, 0x8000, 0xFFFF};
+
+// Converts |value| to a binary big-endian string.
+static std::string PacketValue(uint16_t value) {
+ return std::string{static_cast<char>(value >> 8), static_cast<char>(value)};
+}
+
+// Returns an Error packet.
+static std::string ErrorPacket(uint16_t sequence, const std::string& message = "",
+ char flags = kFlagNone) {
+ return std::string{kIdError, flags} + PacketValue(sequence) + message;
+}
+
+// Returns a Query packet with no data.
+static std::string QueryPacket(uint16_t sequence) {
+ return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence);
+}
+
+// Returns a Query packet with a 2-byte |new_sequence|.
+static std::string QueryPacket(uint16_t sequence, uint16_t new_sequence) {
+ return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence) +
+ PacketValue(new_sequence);
+}
+
+// Returns an Init packet with a 2-byte |version| and |max_packet_size|.
+static std::string InitPacket(uint16_t sequence, uint16_t version, uint16_t max_packet_size) {
+ return std::string{kIdInitialization, kFlagNone} + PacketValue(sequence) +
+ PacketValue(version) + PacketValue(max_packet_size);
+}
+
+// Returns a Fastboot packet with |data|.
+static std::string FastbootPacket(uint16_t sequence, const std::string& data = "",
+ char flags = kFlagNone) {
+ return std::string{kIdFastboot, flags} + PacketValue(sequence) + data;
+}
+
+// Fixture class to test protocol initialization. Usage is to set up the expected calls to the
+// SocketMock object then call UdpConnect() and check the result.
+class UdpConnectTest : public ::testing::Test {
+ public:
+ UdpConnectTest() : mock_socket_(new SocketMock) {}
+
+ // Run the initialization, return whether it was successful or not. This passes ownership of
+ // the current |mock_socket_| but allocates a new one for re-use.
+ bool UdpConnect(std::string* error = nullptr) {
+ std::string local_error;
+ if (error == nullptr) {
+ error = &local_error;
+ }
+ std::unique_ptr<Transport> transport(Connect(std::move(mock_socket_), error));
+ mock_socket_.reset(new SocketMock);
+ return transport != nullptr && error->empty();
+ }
+
+ protected:
+ std::unique_ptr<SocketMock> mock_socket_;
+};
+
+// Tests a successful protocol initialization with various starting sequence numbers.
+TEST_F(UdpConnectTest, InitializationSuccess) {
+ for (uint16_t seq : kTestSequenceNumbers) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, seq));
+ mock_socket_->ExpectSend(InitPacket(seq, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(seq, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+ }
+}
+
+// Tests continuation packets during initialization.
+TEST_F(UdpConnectTest, InitializationContinuationSuccess) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagContinuation, 0, 0, 0x44});
+ mock_socket_->ExpectSend(std::string{kIdDeviceQuery, kFlagNone, 0, 1});
+ mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagNone, 0, 1, 0x55});
+
+ mock_socket_->ExpectSend(InitPacket(0x4455, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x55, 0});
+ mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x56});
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x56, 1});
+ mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x57});
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x57, 2});
+ mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x58});
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagNone, 0x44, 0x58, 0});
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+
+// Tests a mismatched version number; as long as the minimum of the two versions is supported
+// we should allow the connection.
+TEST_F(UdpConnectTest, InitializationVersionMismatch) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, 2, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, 0, 1024));
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, QueryResponseTimeoutFailure) {
+ for (int i = 0; i < kMaxConnectAttempts; ++i) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceiveTimeout();
+ }
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, QueryResponseReceiveFailure) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceiveFailure();
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, InitResponseTimeoutFailure) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ for (int i = 0; i < kMaxTransmissionAttempts; ++i) {
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceiveTimeout();
+ }
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, InitResponseReceiveFailure) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceiveFailure();
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+// Tests that we can recover up to the maximum number of allowed retries.
+TEST_F(UdpConnectTest, ResponseRecovery) {
+ // The device query packet can recover from up to (kMaxConnectAttempts - 1) timeouts.
+ for (int i = 0; i < kMaxConnectAttempts - 1; ++i) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceiveTimeout();
+ }
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+
+ // Subsequent packets try up to (kMaxTransmissionAttempts - 1) times.
+ for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceiveTimeout();
+ }
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests that the host can handle receiving additional bytes for forward compatibility.
+TEST_F(UdpConnectTest, ExtraResponseDataSuccess) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0) + "foo");
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024) + "bar");
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests mismatched response sequence numbers. A wrong sequence number is interpreted as a previous
+// retransmission and just ignored so we should be able to recover.
+TEST_F(UdpConnectTest, WrongSequenceRecovery) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(1, 0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(1, kProtocolVersion, 1024));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests mismatched response IDs. This should also be interpreted as a retransmission and ignored.
+TEST_F(UdpConnectTest, WrongIdRecovery) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(FastbootPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(FastbootPacket(0));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests an invalid query response. Query responses must have at least 2 bytes of data.
+TEST_F(UdpConnectTest, InvalidQueryResponseFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("invalid query response from target", error);
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0) + std::string{0x00});
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("invalid query response from target", error);
+}
+
+// Tests an invalid initialization response. Max packet size must be at least 512 bytes.
+TEST_F(UdpConnectTest, InvalidInitResponseFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 511));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("target reported invalid packet size 511", error);
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, 0, 1024));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("target reported invalid protocol version 0", error);
+}
+
+TEST_F(UdpConnectTest, ErrorResponseFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(ErrorPacket(0, "error1"));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_NE(std::string::npos, error.find("error1"));
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(ErrorPacket(0, "error2"));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_NE(std::string::npos, error.find("error2"));
+}
+
+// Tests an error response with continuation flag.
+TEST_F(UdpConnectTest, ErrorContinuationFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(ErrorPacket(0, "error1", kFlagContinuation));
+ mock_socket_->ExpectSend(ErrorPacket(1));
+ mock_socket_->AddReceive(ErrorPacket(1, " ", kFlagContinuation));
+ mock_socket_->ExpectSend(ErrorPacket(2));
+ mock_socket_->AddReceive(ErrorPacket(2, "error2"));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_NE(std::string::npos, error.find("error1 error2"));
+}
+
+// Fixture class to test UDP Transport read/write functionality.
+class UdpTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ // Create |transport_| starting at sequence 0 with 512 byte max packet size. Tests can call
+ // InitializeTransport() again to change settings.
+ ASSERT_TRUE(InitializeTransport(0, 512));
+ }
+
+ // Sets up |mock_socket_| to correctly initialize the protocol and creates |transport_|. This
+ // can be called multiple times in a test if needed.
+ bool InitializeTransport(uint16_t starting_sequence, int device_max_packet_size = 512) {
+ mock_socket_ = new SocketMock;
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, starting_sequence));
+ mock_socket_->ExpectSend(
+ InitPacket(starting_sequence, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(
+ InitPacket(starting_sequence, kProtocolVersion, device_max_packet_size));
+
+ std::string error;
+ transport_ = Connect(std::unique_ptr<Socket>(mock_socket_), &error);
+ return transport_ != nullptr && error.empty();
+ }
+
+ // Writes |message| to |transport_|, returns true on success.
+ bool Write(const std::string& message) {
+ return transport_->Write(message.data(), message.length()) ==
+ static_cast<ssize_t>(message.length());
+ }
+
+ // Reads from |transport_|, returns true if it matches |message|.
+ bool Read(const std::string& message) {
+ std::string buffer(message.length(), '\0');
+ return transport_->Read(&buffer[0], buffer.length()) ==
+ static_cast<ssize_t>(message.length()) && buffer == message;
+ }
+
+ protected:
+ // |mock_socket_| is a raw pointer here because we transfer ownership to |transport_| but we
+ // need to retain a pointer to set send and receive expectations.
+ SocketMock* mock_socket_ = nullptr;
+ std::unique_ptr<Transport> transport_;
+};
+
+// Tests sequence behavior with various starting sequence numbers.
+TEST_F(UdpTest, SequenceIncrementCheck) {
+ for (uint16_t seq : kTestSequenceNumbers) {
+ ASSERT_TRUE(InitializeTransport(seq));
+
+ for (int i = 0; i < 10; ++i) {
+ mock_socket_->ExpectSend(FastbootPacket(++seq, "foo"));
+ mock_socket_->AddReceive(FastbootPacket(seq, ""));
+ mock_socket_->ExpectSend(FastbootPacket(++seq, ""));
+ mock_socket_->AddReceive(FastbootPacket(seq, "bar"));
+
+ EXPECT_TRUE(Write("foo"));
+ EXPECT_TRUE(Read("bar"));
+ }
+ }
+}
+
+// Tests sending and receiving a few small packets.
+TEST_F(UdpTest, ReadAndWriteSmallPackets) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceive(FastbootPacket(1, ""));
+ mock_socket_->ExpectSend(FastbootPacket(2, ""));
+ mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+ EXPECT_TRUE(Write("foo"));
+ EXPECT_TRUE(Read("bar"));
+
+ mock_socket_->ExpectSend(FastbootPacket(3, "12345 67890"));
+ mock_socket_->AddReceive(FastbootPacket(3));
+ mock_socket_->ExpectSend(FastbootPacket(4, "\x01\x02\x03\x04\x05"));
+ mock_socket_->AddReceive(FastbootPacket(4));
+
+ EXPECT_TRUE(Write("12345 67890"));
+ EXPECT_TRUE(Write("\x01\x02\x03\x04\x05"));
+
+ // Reads are done by sending empty packets.
+ mock_socket_->ExpectSend(FastbootPacket(5));
+ mock_socket_->AddReceive(FastbootPacket(5, "foo bar baz"));
+ mock_socket_->ExpectSend(FastbootPacket(6));
+ mock_socket_->AddReceive(FastbootPacket(6, "\x01\x02\x03\x04\x05"));
+
+ EXPECT_TRUE(Read("foo bar baz"));
+ EXPECT_TRUE(Read("\x01\x02\x03\x04\x05"));
+}
+
+TEST_F(UdpTest, ResponseTimeoutFailure) {
+ for (int i = 0; i < kMaxTransmissionAttempts; ++i) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceiveTimeout();
+ }
+
+ EXPECT_FALSE(Write("foo"));
+}
+
+TEST_F(UdpTest, ResponseReceiveFailure) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceiveFailure();
+
+ EXPECT_FALSE(Write("foo"));
+}
+
+TEST_F(UdpTest, ResponseTimeoutRecovery) {
+ for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceiveTimeout();
+ }
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceive(FastbootPacket(1, ""));
+
+ EXPECT_TRUE(Write("foo"));
+}
+
+// Tests continuation packets for various max packet sizes.
+// The important part of this test is that regardless of what kind of packet fragmentation happens
+// at the socket layer, a single call to Transport::Read() and Transport::Write() is all the
+// fastboot code needs to do.
+TEST_F(UdpTest, ContinuationPackets) {
+ for (uint16_t max_packet_size : {512, 1024, 1200}) {
+ ASSERT_TRUE(InitializeTransport(0, max_packet_size));
+
+ // Initialize the data we want to send. Use (size - 4) to leave room for the header.
+ size_t max_data_size = max_packet_size - 4;
+ std::string data(max_data_size * 3, '\0');
+ for (size_t i = 0; i < data.length(); ++i) {
+ data[i] = i;
+ }
+ std::string chunks[] = {data.substr(0, max_data_size),
+ data.substr(max_data_size, max_data_size),
+ data.substr(max_data_size * 2, max_data_size)};
+
+ // Write data: split into 3 UDP packets, each of which will be ACKed.
+ mock_socket_->ExpectSend(FastbootPacket(1, chunks[0], kFlagContinuation));
+ mock_socket_->AddReceive(FastbootPacket(1));
+ mock_socket_->ExpectSend(FastbootPacket(2, chunks[1], kFlagContinuation));
+ mock_socket_->AddReceive(FastbootPacket(2));
+ mock_socket_->ExpectSend(FastbootPacket(3, chunks[2]));
+ mock_socket_->AddReceive(FastbootPacket(3));
+ EXPECT_TRUE(Write(data));
+
+ // Same thing for reading the data.
+ mock_socket_->ExpectSend(FastbootPacket(4));
+ mock_socket_->AddReceive(FastbootPacket(4, chunks[0], kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(5));
+ mock_socket_->AddReceive(FastbootPacket(5, chunks[1], kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(6));
+ mock_socket_->AddReceive(FastbootPacket(6, chunks[2]));
+ EXPECT_TRUE(Read(data));
+ }
+}
+
+// Tests that the continuation bit is respected even if the packet isn't max size.
+TEST_F(UdpTest, SmallContinuationPackets) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+ EXPECT_TRUE(Read("foobar"));
+}
+
+// Tests receiving an error packet mid-continuation.
+TEST_F(UdpTest, ContinuationPacketError) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceive(ErrorPacket(2, "test error"));
+
+ EXPECT_FALSE(Read("foo"));
+}
+
+// Tests timeout during a continuation sequence.
+TEST_F(UdpTest, ContinuationTimeoutRecovery) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceiveTimeout();
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+ EXPECT_TRUE(Read("foobar"));
+}
+
+// Tests read overflow returns -1 to indicate the failure.
+TEST_F(UdpTest, MultipleReadPacket) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foobarbaz"));
+
+ char buffer[3];
+ EXPECT_EQ(-1, transport_->Read(buffer, 3));
+}
+
+// Tests that packets arriving out-of-order are ignored.
+TEST_F(UdpTest, IgnoreOutOfOrderPackets) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(0, "sequence too low"));
+ mock_socket_->AddReceive(FastbootPacket(2, "sequence too high"));
+ mock_socket_->AddReceive(QueryPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "correct"));
+
+ EXPECT_TRUE(Read("correct"));
+}
+
+// Tests that an error response with the correct sequence number causes immediate failure.
+TEST_F(UdpTest, ErrorResponse) {
+ // Error packets with the wrong sequence number should be ignored like any other packet.
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceive(ErrorPacket(0, "ignored error"));
+ mock_socket_->AddReceive(FastbootPacket(1));
+
+ EXPECT_TRUE(Write("foo"));
+
+ // Error packets with the correct sequence should abort immediately without retransmission.
+ mock_socket_->ExpectSend(FastbootPacket(2, "foo"));
+ mock_socket_->AddReceive(ErrorPacket(2, "test error"));
+
+ EXPECT_FALSE(Write("foo"));
+}
+
+// Tests that attempting to use a closed transport returns -1 without making any socket calls.
+TEST_F(UdpTest, CloseTransport) {
+ char buffer[32];
+ EXPECT_EQ(0, transport_->Close());
+ EXPECT_EQ(-1, transport_->Write("foo", 3));
+ EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));
+}
diff --git a/fastboot/usb.h b/fastboot/usb.h
index c7b748e..4acf12d 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -29,16 +29,9 @@
#ifndef _USB_H_
#define _USB_H_
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "transport.h"
-typedef struct usb_handle usb_handle;
-
-typedef struct usb_ifc_info usb_ifc_info;
-
-struct usb_ifc_info
-{
+struct usb_ifc_info {
/* from device descriptor */
unsigned short dev_vendor;
unsigned short dev_product;
@@ -62,14 +55,6 @@
typedef int (*ifc_match_func)(usb_ifc_info *ifc);
-usb_handle *usb_open(ifc_match_func callback);
-int usb_close(usb_handle *h);
-int usb_read(usb_handle *h, void *_data, int len);
-int usb_write(usb_handle *h, const void *_data, int len);
-int usb_wait_for_disconnect(usb_handle *h);
-
-#if defined(__cplusplus)
-}
-#endif
+Transport* usb_open(ifc_match_func callback);
#endif
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 7b87907..cdab4f1 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -43,9 +43,15 @@
#include <linux/version.h>
#include <linux/usb/ch9.h>
+#include <chrono>
+#include <memory>
+#include <thread>
+
#include "fastboot.h"
#include "usb.h"
+using namespace std::chrono_literals;
+
#define MAX_RETRIES 5
/* Timeout in seconds for usb_wait_for_disconnect.
@@ -85,6 +91,22 @@
unsigned char ep_out;
};
+class LinuxUsbTransport : public Transport {
+ public:
+ explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+ ~LinuxUsbTransport() override = default;
+
+ ssize_t Read(void* data, size_t len) override;
+ ssize_t Write(const void* data, size_t len) override;
+ int Close() override;
+ int WaitForDisconnect() override;
+
+ private:
+ std::unique_ptr<usb_handle> handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(LinuxUsbTransport);
+};
+
/* True if name isn't a valid name for a USB device in /sys/bus/usb/devices.
* Device names are made up of numbers, dots, and dashes, e.g., '7-1.5'.
* We reject interfaces (e.g., '7-1.5:1.0') and host controllers (e.g. 'usb1').
@@ -127,7 +149,7 @@
int in, out;
unsigned i;
unsigned e;
-
+
if (check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
return -1;
dev = (struct usb_device_descriptor *)ptr;
@@ -308,52 +330,49 @@
return 0;
}
-static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
+static std::unique_ptr<usb_handle> find_usb_device(const char* base, ifc_match_func callback)
{
- usb_handle *usb = 0;
+ std::unique_ptr<usb_handle> usb;
char devname[64];
char desc[1024];
int n, in, out, ifc;
- DIR *busdir;
struct dirent *de;
int fd;
int writable;
- busdir = opendir(base);
- if(busdir == 0) return 0;
+ std::unique_ptr<DIR, decltype(&closedir)> busdir(opendir(base), closedir);
+ if (busdir == 0) return 0;
- while((de = readdir(busdir)) && (usb == 0)) {
- if(badname(de->d_name)) continue;
+ while ((de = readdir(busdir.get())) && (usb == nullptr)) {
+ if (badname(de->d_name)) continue;
- if(!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
+ if (!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
// DBG("[ scanning %s ]\n", devname);
writable = 1;
- if((fd = open(devname, O_RDWR)) < 0) {
+ if ((fd = open(devname, O_RDWR)) < 0) {
// Check if we have read-only access, so we can give a helpful
// diagnostic like "adb devices" does.
writable = 0;
- if((fd = open(devname, O_RDONLY)) < 0) {
+ if ((fd = open(devname, O_RDONLY)) < 0) {
continue;
}
}
n = read(fd, desc, sizeof(desc));
- if(filter_usb_device(de->d_name, desc, n, writable, callback,
- &in, &out, &ifc) == 0) {
- usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ if (filter_usb_device(de->d_name, desc, n, writable, callback, &in, &out, &ifc) == 0) {
+ usb.reset(new usb_handle());
strcpy(usb->fname, devname);
usb->ep_in = in;
usb->ep_out = out;
usb->desc = fd;
n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc);
- if(n != 0) {
+ if (n != 0) {
close(fd);
- free(usb);
- usb = 0;
+ usb.reset();
continue;
}
} else {
@@ -361,19 +380,18 @@
}
}
}
- closedir(busdir);
return usb;
}
-int usb_write(usb_handle *h, const void *_data, int len)
+ssize_t LinuxUsbTransport::Write(const void* _data, size_t len)
{
unsigned char *data = (unsigned char*) _data;
unsigned count = 0;
struct usbdevfs_bulktransfer bulk;
int n;
- if(h->ep_out == 0 || h->desc == -1) {
+ if (handle_->ep_out == 0 || handle_->desc == -1) {
return -1;
}
@@ -381,12 +399,12 @@
int xfer;
xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
- bulk.ep = h->ep_out;
+ bulk.ep = handle_->ep_out;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 0;
- n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
+ n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);
if(n != xfer) {
DBG("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
@@ -401,38 +419,37 @@
return count;
}
-int usb_read(usb_handle *h, void *_data, int len)
+ssize_t LinuxUsbTransport::Read(void* _data, size_t len)
{
unsigned char *data = (unsigned char*) _data;
unsigned count = 0;
struct usbdevfs_bulktransfer bulk;
int n, retry;
- if(h->ep_in == 0 || h->desc == -1) {
+ if (handle_->ep_in == 0 || handle_->desc == -1) {
return -1;
}
- while(len > 0) {
+ while (len > 0) {
int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
- bulk.ep = h->ep_in;
+ bulk.ep = handle_->ep_in;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 0;
retry = 0;
- do{
- DBG("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
- n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
- DBG("[ usb read %d ] = %d, fname=%s, Retry %d \n", xfer, n, h->fname, retry);
+ do {
+ DBG("[ usb read %d fd = %d], fname=%s\n", xfer, handle_->desc, handle_->fname);
+ n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);
+ DBG("[ usb read %d ] = %d, fname=%s, Retry %d \n", xfer, n, handle_->fname, retry);
- if( n < 0 ) {
- DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno));
- if ( ++retry > MAX_RETRIES ) return -1;
- sleep( 1 );
- }
- }
- while( n < 0 );
+ if (n < 0) {
+ DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno));
+ if (++retry > MAX_RETRIES) return -1;
+ std::this_thread::sleep_for(1s);
+ }
+ } while (n < 0);
count += n;
len -= n;
@@ -446,24 +463,12 @@
return count;
}
-void usb_kick(usb_handle *h)
+int LinuxUsbTransport::Close()
{
int fd;
- fd = h->desc;
- h->desc = -1;
- if(fd >= 0) {
- close(fd);
- DBG("[ usb closed %d ]\n", fd);
- }
-}
-
-int usb_close(usb_handle *h)
-{
- int fd;
-
- fd = h->desc;
- h->desc = -1;
+ fd = handle_->desc;
+ handle_->desc = -1;
if(fd >= 0) {
close(fd);
DBG("[ usb closed %d ]\n", fd);
@@ -472,22 +477,22 @@
return 0;
}
-usb_handle *usb_open(ifc_match_func callback)
+Transport* usb_open(ifc_match_func callback)
{
- return find_usb_device("/sys/bus/usb/devices", callback);
+ std::unique_ptr<usb_handle> handle = find_usb_device("/sys/bus/usb/devices", callback);
+ return handle ? new LinuxUsbTransport(std::move(handle)) : nullptr;
}
/* Wait for the system to notice the device is gone, so that a subsequent
* fastboot command won't try to access the device before it's rebooted.
* Returns 0 for success, -1 for timeout.
*/
-int usb_wait_for_disconnect(usb_handle *usb)
+int LinuxUsbTransport::WaitForDisconnect()
{
double deadline = now() + WAIT_FOR_DISCONNECT_TIMEOUT;
while (now() < deadline) {
- if (access(usb->fname, F_OK))
- return 0;
- usleep(50000);
+ if (access(handle_->fname, F_OK)) return 0;
+ std::this_thread::sleep_for(50ms);
}
return -1;
}
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 7ae2aa5..9069baa 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -35,6 +35,8 @@
#include <IOKit/IOMessage.h>
#include <mach/mach_port.h>
+#include <memory>
+
#include "usb.h"
@@ -63,6 +65,21 @@
unsigned int zero_mask;
};
+class OsxUsbTransport : public Transport {
+ public:
+ OsxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+ ~OsxUsbTransport() override = default;
+
+ ssize_t Read(void* data, size_t len) override;
+ ssize_t Write(const void* data, size_t len) override;
+ int Close() override;
+
+ private:
+ std::unique_ptr<usb_handle> handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(OsxUsbTransport);
+};
+
/** Try out all the interfaces and see if there's a match. Returns 0 on
* success, -1 on failure. */
static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
@@ -75,7 +92,6 @@
HRESULT result;
SInt32 score;
UInt8 interfaceNumEndpoints;
- UInt8 configuration;
// Placing the constant KIOUSBFindInterfaceDontCare into the following
// fields of the IOUSBFindInterfaceRequest structure will allow us to
@@ -85,13 +101,6 @@
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
- // SetConfiguration will kill an existing UMS connection, so let's
- // not do this if not necessary.
- configuration = 0;
- (*dev)->GetConfiguration(dev, &configuration);
- if (configuration != 1)
- (*dev)->SetConfiguration(dev, 1);
-
// Get an iterator for the interfaces on the device
kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
@@ -379,7 +388,7 @@
/** Initializes the USB system. Returns 0 on success, -1 on error. */
-static int init_usb(ifc_match_func callback, usb_handle **handle) {
+static int init_usb(ifc_match_func callback, std::unique_ptr<usb_handle>* handle) {
int ret = -1;
CFMutableDictionaryRef matchingDict;
kern_return_t result;
@@ -432,8 +441,8 @@
}
if (h.success) {
- *handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
- memcpy(*handle, &h, sizeof(usb_handle));
+ handle->reset(new usb_handle);
+ memcpy(handle->get(), &h, sizeof(usb_handle));
ret = 0;
break;
}
@@ -452,28 +461,23 @@
* Definitions of this file's public functions.
*/
-usb_handle *usb_open(ifc_match_func callback) {
- usb_handle *handle = NULL;
+Transport* usb_open(ifc_match_func callback) {
+ std::unique_ptr<usb_handle> handle;
if (init_usb(callback, &handle) < 0) {
/* Something went wrong initializing USB. */
- return NULL;
+ return nullptr;
}
- return handle;
+ return new OsxUsbTransport(std::move(handle));
}
-int usb_close(usb_handle *h) {
+int OsxUsbTransport::Close() {
/* TODO: Something better here? */
return 0;
}
-int usb_wait_for_disconnect(usb_handle *usb) {
- /* TODO: Punt for now */
- return 0;
-}
-
-int usb_read(usb_handle *h, void *data, int len) {
+ssize_t OsxUsbTransport::Read(void* data, size_t len) {
IOReturn result;
UInt32 numBytes = len;
@@ -481,22 +485,21 @@
return 0;
}
- if (h == NULL) {
+ if (handle_ == nullptr) {
return -1;
}
- if (h->interface == NULL) {
+ if (handle_->interface == nullptr) {
ERR("usb_read interface was null\n");
return -1;
}
- if (h->bulkIn == 0) {
+ if (handle_->bulkIn == 0) {
ERR("bulkIn endpoint not assigned\n");
return -1;
}
- result = (*h->interface)->ReadPipe(
- h->interface, h->bulkIn, data, &numBytes);
+ result = (*handle_->interface)->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
if (result == 0) {
return (int) numBytes;
@@ -507,30 +510,30 @@
return -1;
}
-int usb_write(usb_handle *h, const void *data, int len) {
+ssize_t OsxUsbTransport::Write(const void* data, size_t len) {
IOReturn result;
if (len == 0) {
return 0;
}
- if (h == NULL) {
+ if (handle_ == NULL) {
return -1;
}
- if (h->interface == NULL) {
+ if (handle_->interface == NULL) {
ERR("usb_write interface was null\n");
return -1;
}
- if (h->bulkOut == 0) {
+ if (handle_->bulkOut == 0) {
ERR("bulkOut endpoint not assigned\n");
return -1;
}
#if 0
- result = (*h->interface)->WritePipe(
- h->interface, h->bulkOut, (void *)data, len);
+ result = (*handle_->interface)->WritePipe(
+ handle_->interface, handle_->bulkOut, (void *)data, len);
#else
/* Attempt to work around crashes in the USB driver that may be caused
* by trying to write too much data at once. The kernel IOCopyMapper
@@ -543,8 +546,8 @@
int lenToSend = lenRemaining > maxLenToSend
? maxLenToSend : lenRemaining;
- result = (*h->interface)->WritePipe(
- h->interface, h->bulkOut, (void *)data, lenToSend);
+ result = (*handle_->interface)->WritePipe(
+ handle_->interface, handle_->bulkOut, (void *)data, lenToSend);
if (result != 0) break;
lenRemaining -= lenToSend;
@@ -553,11 +556,11 @@
#endif
#if 0
- if ((result == 0) && (h->zero_mask)) {
+ if ((result == 0) && (handle_->zero_mask)) {
/* we need 0-markers and our transfer */
- if(!(len & h->zero_mask)) {
- result = (*h->interface)->WritePipe(
- h->interface, h->bulkOut, (void *)data, 0);
+ if(!(len & handle_->zero_mask)) {
+ result = (*handle_->interface)->WritePipe(
+ handle_->interface, handle_->bulkOut, (void *)data, 0);
}
}
#endif
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index a09610f..3dab5ac 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -34,6 +34,9 @@
#include <stdio.h>
#include <stdlib.h>
+#include <memory>
+#include <string>
+
#include "usb.h"
//#define TRACE_USB 1
@@ -60,24 +63,32 @@
ADBAPIHANDLE adb_write_pipe;
/// Interface name
- char* interface_name;
+ std::string interface_name;
+};
+
+class WindowsUsbTransport : public Transport {
+ public:
+ WindowsUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+ ~WindowsUsbTransport() override = default;
+
+ ssize_t Read(void* data, size_t len) override;
+ ssize_t Write(const void* data, size_t len) override;
+ int Close() override;
+
+ private:
+ std::unique_ptr<usb_handle> handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowsUsbTransport);
};
/// Class ID assigned to the device by androidusb.sys
static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
-
/// Checks if interface (device) matches certain criteria
int recognized_device(usb_handle* handle, ifc_match_func callback);
/// Opens usb interface (device) by interface (device) name.
-usb_handle* do_usb_open(const wchar_t* interface_name);
-
-/// Writes data to the opened usb handle
-int usb_write(usb_handle* handle, const void* data, int len);
-
-/// Reads data using the opened usb handle
-int usb_read(usb_handle *handle, void* data, int len);
+std::unique_ptr<usb_handle> do_usb_open(const wchar_t* interface_name);
/// Cleans up opened usb handle
void usb_cleanup_handle(usb_handle* handle);
@@ -85,23 +96,17 @@
/// Cleans up (but don't close) opened usb handle
void usb_kick(usb_handle* handle);
-/// Closes opened usb handle
-int usb_close(usb_handle* handle);
-
-usb_handle* do_usb_open(const wchar_t* interface_name) {
+std::unique_ptr<usb_handle> do_usb_open(const wchar_t* interface_name) {
// Allocate our handle
- usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
- if (NULL == ret)
- return NULL;
+ std::unique_ptr<usb_handle> ret(new usb_handle);
// Create interface.
ret->adb_interface = AdbCreateInterfaceByName(interface_name);
- if (NULL == ret->adb_interface) {
- free(ret);
+ if (nullptr == ret->adb_interface) {
errno = GetLastError();
- return NULL;
+ return nullptr;
}
// Open read pipe (endpoint)
@@ -109,35 +114,30 @@
AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
AdbOpenAccessTypeReadWrite,
AdbOpenSharingModeReadWrite);
- if (NULL != ret->adb_read_pipe) {
+ if (nullptr != ret->adb_read_pipe) {
// Open write pipe (endpoint)
ret->adb_write_pipe =
AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
AdbOpenAccessTypeReadWrite,
AdbOpenSharingModeReadWrite);
- if (NULL != ret->adb_write_pipe) {
+ if (nullptr != ret->adb_write_pipe) {
// Save interface name
unsigned long name_len = 0;
// First get expected name length
AdbGetInterfaceName(ret->adb_interface,
- NULL,
+ nullptr,
&name_len,
true);
if (0 != name_len) {
- ret->interface_name = (char*)malloc(name_len);
-
- if (NULL != ret->interface_name) {
- // Now save the name
- if (AdbGetInterfaceName(ret->adb_interface,
- ret->interface_name,
- &name_len,
- true)) {
- // We're done at this point
- return ret;
- }
- } else {
- SetLastError(ERROR_OUTOFMEMORY);
+ // Now save the name
+ ret->interface_name.resize(name_len);
+ if (AdbGetInterfaceName(ret->adb_interface,
+ &ret->interface_name[0],
+ &name_len,
+ true)) {
+ // We're done at this point
+ return ret;
}
}
}
@@ -145,35 +145,31 @@
// Something went wrong.
errno = GetLastError();
- usb_cleanup_handle(ret);
- free(ret);
+ usb_cleanup_handle(ret.get());
SetLastError(errno);
- return NULL;
+ return nullptr;
}
-int usb_write(usb_handle* handle, const void* data, int len) {
+ssize_t WindowsUsbTransport::Write(const void* data, size_t len) {
unsigned long time_out = 5000;
unsigned long written = 0;
unsigned count = 0;
int ret;
DBG("usb_write %d\n", len);
- if (NULL != handle) {
+ if (nullptr != handle_) {
// Perform write
while(len > 0) {
int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
- ret = AdbWriteEndpointSync(handle->adb_write_pipe,
- (void*)data,
- (unsigned long)xfer,
- &written,
- time_out);
+ ret = AdbWriteEndpointSync(handle_->adb_write_pipe, const_cast<void*>(data), xfer,
+ &written, time_out);
errno = GetLastError();
DBG("AdbWriteEndpointSync returned %d, errno: %d\n", ret, errno);
if (ret == 0) {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
- usb_kick(handle);
+ usb_kick(handle_.get());
return -1;
}
@@ -194,21 +190,17 @@
return -1;
}
-int usb_read(usb_handle *handle, void* data, int len) {
+ssize_t WindowsUsbTransport::Read(void* data, size_t len) {
unsigned long time_out = 0;
unsigned long read = 0;
int ret;
DBG("usb_read %d\n", len);
- if (NULL != handle) {
+ if (nullptr != handle_) {
while (1) {
int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
- ret = AdbReadEndpointSync(handle->adb_read_pipe,
- (void*)data,
- (unsigned long)xfer,
- &read,
- time_out);
+ ret = AdbReadEndpointSync(handle_->adb_read_pipe, data, xfer, &read, time_out);
errno = GetLastError();
DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
if (ret) {
@@ -216,7 +208,7 @@
} else {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
- usb_kick(handle);
+ usb_kick(handle_.get());
break;
}
// else we timed out - try again
@@ -233,8 +225,6 @@
void usb_cleanup_handle(usb_handle* handle) {
if (NULL != handle) {
- if (NULL != handle->interface_name)
- free(handle->interface_name);
if (NULL != handle->adb_write_pipe)
AdbCloseHandle(handle->adb_write_pipe);
if (NULL != handle->adb_read_pipe)
@@ -242,7 +232,7 @@
if (NULL != handle->adb_interface)
AdbCloseHandle(handle->adb_interface);
- handle->interface_name = NULL;
+ handle->interface_name.clear();
handle->adb_write_pipe = NULL;
handle->adb_read_pipe = NULL;
handle->adb_interface = NULL;
@@ -258,23 +248,18 @@
}
}
-int usb_close(usb_handle* handle) {
+int WindowsUsbTransport::Close() {
DBG("usb_close\n");
- if (NULL != handle) {
+ if (nullptr != handle_) {
// Cleanup handle
- usb_cleanup_handle(handle);
- free(handle);
+ usb_cleanup_handle(handle_.get());
+ handle_.reset();
}
return 0;
}
-int usb_wait_for_disconnect(usb_handle *usb) {
- /* TODO: Punt for now */
- return 0;
-}
-
int recognized_device(usb_handle* handle, ifc_match_func callback) {
struct usb_ifc_info info;
USB_DEVICE_DESCRIPTOR device_desc;
@@ -326,8 +311,8 @@
return 0;
}
-static usb_handle *find_usb_device(ifc_match_func callback) {
- usb_handle* handle = NULL;
+static std::unique_ptr<usb_handle> find_usb_device(ifc_match_func callback) {
+ std::unique_ptr<usb_handle> handle;
char entry_buffer[2048];
char interf_name[2048];
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
@@ -356,13 +341,12 @@
handle = do_usb_open(next_interface->device_name);
if (NULL != handle) {
// Lets see if this interface (device) belongs to us
- if (recognized_device(handle, callback)) {
+ if (recognized_device(handle.get(), callback)) {
// found it!
break;
} else {
- usb_cleanup_handle(handle);
- free(handle);
- handle = NULL;
+ usb_cleanup_handle(handle.get());
+ handle.reset();
}
}
@@ -373,13 +357,8 @@
return handle;
}
-usb_handle *usb_open(ifc_match_func callback)
+Transport* usb_open(ifc_match_func callback)
{
- return find_usb_device(callback);
-}
-
-// called from fastboot.c
-void sleep(int seconds)
-{
- Sleep(seconds * 1000);
+ std::unique_ptr<usb_handle> handle = find_usb_device(callback);
+ return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
}
diff --git a/fastboot/usbtest.cpp b/fastboot/usbtest.cpp
index e6e2b37..9423c6d 100644
--- a/fastboot/usbtest.cpp
+++ b/fastboot/usbtest.cpp
@@ -86,7 +86,7 @@
return 0;
}
-int test_null(usb_handle *usb)
+int test_null(Transport* usb)
{
unsigned i;
unsigned char buf[4096];
@@ -94,8 +94,8 @@
long long t0, t1;
t0 = NOW();
- for(i = 0; i < arg_count; i++) {
- if(usb_write(usb, buf, arg_size) != (int)arg_size) {
+ for (i = 0; i < arg_count; i++) {
+ if (usb->Write(buf, arg_size) != static_cast<int>(arg_size)) {
fprintf(stderr,"write failed (%s)\n", strerror(errno));
return -1;
}
@@ -105,15 +105,15 @@
return 0;
}
-int test_zero(usb_handle *usb)
+int test_zero(Transport* usb)
{
unsigned i;
unsigned char buf[4096];
long long t0, t1;
t0 = NOW();
- for(i = 0; i < arg_count; i++) {
- if(usb_read(usb, buf, arg_size) != (int)arg_size) {
+ for (i = 0; i < arg_count; i++) {
+ if (usb->Read(buf, arg_size) != static_cast<int>(arg_size)) {
fprintf(stderr,"read failed (%s)\n", strerror(errno));
return -1;
}
@@ -127,7 +127,7 @@
{
const char *cmd;
ifc_match_func match;
- int (*test)(usb_handle *usb);
+ int (*test)(Transport* usb);
const char *help;
} tests[] = {
{ "list", printifc, NULL, "list interfaces" },
@@ -177,7 +177,7 @@
int main(int argc, char **argv)
{
- usb_handle *usb;
+ Transport* usb;
int i;
if(argc < 2)
diff --git a/fastboot/util_linux.cpp b/fastboot/util_linux.cpp
deleted file mode 100644
index b788199..0000000
--- a/fastboot/util_linux.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2008 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 "fastboot.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-void get_my_path(char *path)
-{
- char proc[64];
- char *x;
-
- sprintf(proc, "/proc/%d/exe", getpid());
- int err = readlink(proc, path, PATH_MAX - 1);
-
- if(err <= 0) {
- path[0] = 0;
- } else {
- path[err] = 0;
- x = strrchr(path,'/');
- if(x) x[1] = 0;
- }
-}
diff --git a/fastboot/util_osx.cpp b/fastboot/util_osx.cpp
deleted file mode 100644
index ae0b024..0000000
--- a/fastboot/util_osx.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2008 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 "fastboot.h"
-
-#import <Carbon/Carbon.h>
-#include <unistd.h>
-
-void get_my_path(char s[PATH_MAX])
-{
- CFBundleRef mainBundle = CFBundleGetMainBundle();
- CFURLRef executableURL = CFBundleCopyExecutableURL(mainBundle);
- CFStringRef executablePathString = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
- CFRelease(executableURL);
-
- CFStringGetFileSystemRepresentation(executablePathString, s, PATH_MAX-1);
- CFRelease(executablePathString);
-
- char *x;
- x = strrchr(s, '/');
- if(x) x[1] = 0;
-}
-
-
diff --git a/fastboot/util_windows.cpp b/fastboot/util_windows.cpp
deleted file mode 100644
index ec52f39..0000000
--- a/fastboot/util_windows.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2008 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 "fastboot.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include <windows.h>
-
-void get_my_path(char exe[PATH_MAX])
-{
- char* r;
-
- GetModuleFileName( NULL, exe, PATH_MAX-1 );
- exe[PATH_MAX-1] = 0;
- r = strrchr( exe, '\\' );
- if (r)
- *r = 0;
-}
-
diff --git a/fingerprintd/Android.mk b/fingerprintd/Android.mk
new file mode 100644
index 0000000..48b9525
--- /dev/null
+++ b/fingerprintd/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
+LOCAL_SRC_FILES := \
+ FingerprintDaemonProxy.cpp \
+ IFingerprintDaemon.cpp \
+ IFingerprintDaemonCallback.cpp \
+ fingerprintd.cpp
+LOCAL_MODULE := fingerprintd
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ liblog \
+ libhardware \
+ libutils \
+ libkeystore_binder
+include $(BUILD_EXECUTABLE)
diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp
new file mode 100644
index 0000000..1c7da30
--- /dev/null
+++ b/fingerprintd/FingerprintDaemonProxy.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "fingerprintd"
+
+#include <binder/IServiceManager.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
+#include <hardware/hw_auth_token.h>
+#include <keystore/IKeystoreService.h>
+#include <keystore/keystore.h> // for error codes
+#include <utils/Log.h>
+
+#include "FingerprintDaemonProxy.h"
+
+namespace android {
+
+FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = NULL;
+
+// Supported fingerprint HAL version
+static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0);
+
+FingerprintDaemonProxy::FingerprintDaemonProxy() : mModule(NULL), mDevice(NULL), mCallback(NULL) {
+
+}
+
+FingerprintDaemonProxy::~FingerprintDaemonProxy() {
+ closeHal();
+}
+
+void FingerprintDaemonProxy::hal_notify_callback(const fingerprint_msg_t *msg) {
+ FingerprintDaemonProxy* instance = FingerprintDaemonProxy::getInstance();
+ const sp<IFingerprintDaemonCallback> callback = instance->mCallback;
+ if (callback == NULL) {
+ ALOGE("Invalid callback object");
+ return;
+ }
+ const int64_t device = (int64_t) instance->mDevice;
+ switch (msg->type) {
+ case FINGERPRINT_ERROR:
+ ALOGD("onError(%d)", msg->data.error);
+ callback->onError(device, msg->data.error);
+ break;
+ case FINGERPRINT_ACQUIRED:
+ ALOGD("onAcquired(%d)", msg->data.acquired.acquired_info);
+ callback->onAcquired(device, msg->data.acquired.acquired_info);
+ break;
+ case FINGERPRINT_AUTHENTICATED:
+ ALOGD("onAuthenticated(fid=%d, gid=%d)",
+ msg->data.authenticated.finger.fid,
+ msg->data.authenticated.finger.gid);
+ if (msg->data.authenticated.finger.fid != 0) {
+ const uint8_t* hat = reinterpret_cast<const uint8_t *>(&msg->data.authenticated.hat);
+ instance->notifyKeystore(hat, sizeof(msg->data.authenticated.hat));
+ }
+ callback->onAuthenticated(device,
+ msg->data.authenticated.finger.fid,
+ msg->data.authenticated.finger.gid);
+ break;
+ case FINGERPRINT_TEMPLATE_ENROLLING:
+ ALOGD("onEnrollResult(fid=%d, gid=%d, rem=%d)",
+ msg->data.enroll.finger.fid,
+ msg->data.enroll.finger.gid,
+ msg->data.enroll.samples_remaining);
+ callback->onEnrollResult(device,
+ msg->data.enroll.finger.fid,
+ msg->data.enroll.finger.gid,
+ msg->data.enroll.samples_remaining);
+ break;
+ case FINGERPRINT_TEMPLATE_REMOVED:
+ ALOGD("onRemove(fid=%d, gid=%d)",
+ msg->data.removed.finger.fid,
+ msg->data.removed.finger.gid);
+ callback->onRemoved(device,
+ msg->data.removed.finger.fid,
+ msg->data.removed.finger.gid);
+ break;
+ case FINGERPRINT_TEMPLATE_ENUMERATING:
+ ALOGD("onEnumerate(fid=%d, gid=%d, rem=%d)",
+ msg->data.enumerated.finger.fid,
+ msg->data.enumerated.finger.gid,
+ msg->data.enumerated.remaining_templates);
+ callback->onEnumerate(device,
+ msg->data.enumerated.finger.fid,
+ msg->data.enumerated.finger.gid,
+ msg->data.enumerated.remaining_templates);
+ break;
+ default:
+ ALOGE("invalid msg type: %d", msg->type);
+ return;
+ }
+}
+
+void FingerprintDaemonProxy::notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length) {
+ if (auth_token != NULL && auth_token_length > 0) {
+ // TODO: cache service?
+ sp < IServiceManager > sm = defaultServiceManager();
+ sp < IBinder > binder = sm->getService(String16("android.security.keystore"));
+ sp < IKeystoreService > service = interface_cast < IKeystoreService > (binder);
+ if (service != NULL) {
+ status_t ret = service->addAuthToken(auth_token, auth_token_length);
+ if (ret != ResponseCode::NO_ERROR) {
+ ALOGE("Falure sending auth token to KeyStore: %d", ret);
+ }
+ } else {
+ ALOGE("Unable to communicate with KeyStore");
+ }
+ }
+}
+
+void FingerprintDaemonProxy::init(const sp<IFingerprintDaemonCallback>& callback) {
+ if (mCallback != NULL && IInterface::asBinder(callback) != IInterface::asBinder(mCallback)) {
+ IInterface::asBinder(mCallback)->unlinkToDeath(this);
+ }
+ IInterface::asBinder(callback)->linkToDeath(this);
+ mCallback = callback;
+}
+
+int32_t FingerprintDaemonProxy::enroll(const uint8_t* token, ssize_t tokenSize, int32_t groupId,
+ int32_t timeout) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "enroll(gid=%d, timeout=%d)\n", groupId, timeout);
+ if (tokenSize != sizeof(hw_auth_token_t) ) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "enroll() : invalid token size %zu\n", tokenSize);
+ return -1;
+ }
+ const hw_auth_token_t* authToken = reinterpret_cast<const hw_auth_token_t*>(token);
+ return mDevice->enroll(mDevice, authToken, groupId, timeout);
+}
+
+uint64_t FingerprintDaemonProxy::preEnroll() {
+ return mDevice->pre_enroll(mDevice);
+}
+
+int32_t FingerprintDaemonProxy::postEnroll() {
+ return mDevice->post_enroll(mDevice);
+}
+
+int32_t FingerprintDaemonProxy::stopEnrollment() {
+ ALOG(LOG_VERBOSE, LOG_TAG, "stopEnrollment()\n");
+ return mDevice->cancel(mDevice);
+}
+
+int32_t FingerprintDaemonProxy::authenticate(uint64_t sessionId, uint32_t groupId) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "authenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId);
+ return mDevice->authenticate(mDevice, sessionId, groupId);
+}
+
+int32_t FingerprintDaemonProxy::stopAuthentication() {
+ ALOG(LOG_VERBOSE, LOG_TAG, "stopAuthentication()\n");
+ return mDevice->cancel(mDevice);
+}
+
+int32_t FingerprintDaemonProxy::remove(int32_t fingerId, int32_t groupId) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "remove(fid=%d, gid=%d)\n", fingerId, groupId);
+ return mDevice->remove(mDevice, groupId, fingerId);
+}
+
+int32_t FingerprintDaemonProxy::enumerate() {
+ ALOG(LOG_VERBOSE, LOG_TAG, "enumerate()\n");
+ return mDevice->enumerate(mDevice);
+}
+
+uint64_t FingerprintDaemonProxy::getAuthenticatorId() {
+ return mDevice->get_authenticator_id(mDevice);
+}
+
+int32_t FingerprintDaemonProxy::setActiveGroup(int32_t groupId, const uint8_t* path,
+ ssize_t pathlen) {
+ if (pathlen >= PATH_MAX || pathlen <= 0) {
+ ALOGE("Bad path length: %zd", pathlen);
+ return -1;
+ }
+ // Convert to null-terminated string
+ char path_name[PATH_MAX];
+ memcpy(path_name, path, pathlen);
+ path_name[pathlen] = '\0';
+ ALOG(LOG_VERBOSE, LOG_TAG, "setActiveGroup(%d, %s, %zu)", groupId, path_name, pathlen);
+ return mDevice->set_active_group(mDevice, groupId, path_name);
+}
+
+int64_t FingerprintDaemonProxy::openHal() {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
+ int err;
+ const hw_module_t *hw_module = NULL;
+ if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
+ ALOGE("Can't open fingerprint HW Module, error: %d", err);
+ return 0;
+ }
+ if (NULL == hw_module) {
+ ALOGE("No valid fingerprint module");
+ return 0;
+ }
+
+ mModule = reinterpret_cast<const fingerprint_module_t*>(hw_module);
+
+ if (mModule->common.methods->open == NULL) {
+ ALOGE("No valid open method");
+ return 0;
+ }
+
+ hw_device_t *device = NULL;
+
+ if (0 != (err = mModule->common.methods->open(hw_module, NULL, &device))) {
+ ALOGE("Can't open fingerprint methods, error: %d", err);
+ return 0;
+ }
+
+ if (kVersion != device->version) {
+ ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
+ // return 0; // FIXME
+ }
+
+ mDevice = reinterpret_cast<fingerprint_device_t*>(device);
+ err = mDevice->set_notify(mDevice, hal_notify_callback);
+ if (err < 0) {
+ ALOGE("Failed in call to set_notify(), err=%d", err);
+ return 0;
+ }
+
+ // Sanity check - remove
+ if (mDevice->notify != hal_notify_callback) {
+ ALOGE("NOTIFY not set properly: %p != %p", mDevice->notify, hal_notify_callback);
+ }
+
+ ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
+ return reinterpret_cast<int64_t>(mDevice); // This is just a handle
+}
+
+int32_t FingerprintDaemonProxy::closeHal() {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeCloseHal()\n");
+ if (mDevice == NULL) {
+ ALOGE("No valid device");
+ return -ENOSYS;
+ }
+ int err;
+ if (0 != (err = mDevice->common.close(reinterpret_cast<hw_device_t*>(mDevice)))) {
+ ALOGE("Can't close fingerprint module, error: %d", err);
+ return err;
+ }
+ mDevice = NULL;
+ return 0;
+}
+
+void FingerprintDaemonProxy::binderDied(const wp<IBinder>& who) {
+ ALOGD("binder died");
+ int err;
+ if (0 != (err = closeHal())) {
+ ALOGE("Can't close fingerprint device, error: %d", err);
+ }
+ if (IInterface::asBinder(mCallback) == who) {
+ mCallback = NULL;
+ }
+}
+
+}
diff --git a/fingerprintd/FingerprintDaemonProxy.h b/fingerprintd/FingerprintDaemonProxy.h
new file mode 100644
index 0000000..145b4c9
--- /dev/null
+++ b/fingerprintd/FingerprintDaemonProxy.h
@@ -0,0 +1,64 @@
+/*
+ * 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 FINGERPRINT_DAEMON_PROXY_H_
+#define FINGERPRINT_DAEMON_PROXY_H_
+
+#include "IFingerprintDaemon.h"
+#include "IFingerprintDaemonCallback.h"
+
+namespace android {
+
+class FingerprintDaemonProxy : public BnFingerprintDaemon {
+ public:
+ static FingerprintDaemonProxy* getInstance() {
+ if (sInstance == NULL) {
+ sInstance = new FingerprintDaemonProxy();
+ }
+ return sInstance;
+ }
+
+ // These reflect binder methods.
+ virtual void init(const sp<IFingerprintDaemonCallback>& callback);
+ virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId, int32_t timeout);
+ virtual uint64_t preEnroll();
+ virtual int32_t postEnroll();
+ virtual int32_t stopEnrollment();
+ virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId);
+ virtual int32_t stopAuthentication();
+ virtual int32_t remove(int32_t fingerId, int32_t groupId);
+ virtual int32_t enumerate();
+ virtual uint64_t getAuthenticatorId();
+ virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen);
+ virtual int64_t openHal();
+ virtual int32_t closeHal();
+
+ private:
+ FingerprintDaemonProxy();
+ virtual ~FingerprintDaemonProxy();
+ void binderDied(const wp<IBinder>& who);
+ void notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length);
+ static void hal_notify_callback(const fingerprint_msg_t *msg);
+
+ static FingerprintDaemonProxy* sInstance;
+ fingerprint_module_t const* mModule;
+ fingerprint_device_t* mDevice;
+ sp<IFingerprintDaemonCallback> mCallback;
+};
+
+} // namespace android
+
+#endif // FINGERPRINT_DAEMON_PROXY_H_
diff --git a/fingerprintd/IFingerprintDaemon.cpp b/fingerprintd/IFingerprintDaemon.cpp
new file mode 100644
index 0000000..bc4af56
--- /dev/null
+++ b/fingerprintd/IFingerprintDaemon.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+#include <inttypes.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
+#include <utils/String16.h>
+#include <utils/Looper.h>
+#include <keystore/IKeystoreService.h>
+#include <keystore/keystore.h> // for error code
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
+#include <hardware/hw_auth_token.h>
+#include "IFingerprintDaemon.h"
+#include "IFingerprintDaemonCallback.h"
+
+namespace android {
+
+static const String16 USE_FINGERPRINT_PERMISSION("android.permission.USE_FINGERPRINT");
+static const String16 MANAGE_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT");
+static const String16 HAL_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT"); // TODO
+static const String16 DUMP_PERMISSION("android.permission.DUMP");
+
+const android::String16
+IFingerprintDaemon::descriptor("android.hardware.fingerprint.IFingerprintDaemon");
+
+const android::String16&
+IFingerprintDaemon::getInterfaceDescriptor() const {
+ return IFingerprintDaemon::descriptor;
+}
+
+status_t BnFingerprintDaemon::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ switch(code) {
+ case AUTHENTICATE: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const uint64_t sessionId = data.readInt64();
+ const uint32_t groupId = data.readInt32();
+ const int32_t ret = authenticate(sessionId, groupId);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ };
+ case CANCEL_AUTHENTICATION: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int32_t ret = stopAuthentication();
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case ENROLL: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const ssize_t tokenSize = data.readInt32();
+ const uint8_t* token = static_cast<const uint8_t *>(data.readInplace(tokenSize));
+ const int32_t groupId = data.readInt32();
+ const int32_t timeout = data.readInt32();
+ const int32_t ret = enroll(token, tokenSize, groupId, timeout);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case CANCEL_ENROLLMENT: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int32_t ret = stopEnrollment();
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case PRE_ENROLL: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const uint64_t ret = preEnroll();
+ reply->writeNoException();
+ reply->writeInt64(ret);
+ return NO_ERROR;
+ }
+ case POST_ENROLL: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int32_t ret = postEnroll();
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case REMOVE: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int32_t fingerId = data.readInt32();
+ const int32_t groupId = data.readInt32();
+ const int32_t ret = remove(fingerId, groupId);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case ENUMERATE: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int32_t ret = enumerate();
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case GET_AUTHENTICATOR_ID: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const uint64_t ret = getAuthenticatorId();
+ reply->writeNoException();
+ reply->writeInt64(ret);
+ return NO_ERROR;
+ }
+ case SET_ACTIVE_GROUP: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int32_t group = data.readInt32();
+ const ssize_t pathSize = data.readInt32();
+ const uint8_t* path = static_cast<const uint8_t *>(data.readInplace(pathSize));
+ const int32_t ret = setActiveGroup(group, path, pathSize);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case OPEN_HAL: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int64_t ret = openHal();
+ reply->writeNoException();
+ reply->writeInt64(ret);
+ return NO_ERROR;
+ }
+ case CLOSE_HAL: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int32_t ret = closeHal();
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case INIT: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ sp<IFingerprintDaemonCallback> callback =
+ interface_cast<IFingerprintDaemonCallback>(data.readStrongBinder());
+ init(callback);
+ reply->writeNoException();
+ return NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+};
+
+bool BnFingerprintDaemon::checkPermission(const String16& permission) {
+ const IPCThreadState* ipc = IPCThreadState::self();
+ const int calling_pid = ipc->getCallingPid();
+ const int calling_uid = ipc->getCallingUid();
+ return PermissionCache::checkPermission(permission, calling_pid, calling_uid);
+}
+
+
+}; // namespace android
diff --git a/fingerprintd/IFingerprintDaemon.h b/fingerprintd/IFingerprintDaemon.h
new file mode 100644
index 0000000..23c36ff
--- /dev/null
+++ b/fingerprintd/IFingerprintDaemon.h
@@ -0,0 +1,88 @@
+/*
+ * 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 IFINGERPRINT_DAEMON_H_
+#define IFINGERPRINT_DAEMON_H_
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IFingerprintDaemonCallback;
+
+/*
+* Abstract base class for native implementation of FingerprintService.
+*
+* Note: This must be kept manually in sync with IFingerprintDaemon.aidl
+*/
+class IFingerprintDaemon : public IInterface, public IBinder::DeathRecipient {
+ public:
+ enum {
+ AUTHENTICATE = IBinder::FIRST_CALL_TRANSACTION + 0,
+ CANCEL_AUTHENTICATION = IBinder::FIRST_CALL_TRANSACTION + 1,
+ ENROLL = IBinder::FIRST_CALL_TRANSACTION + 2,
+ CANCEL_ENROLLMENT = IBinder::FIRST_CALL_TRANSACTION + 3,
+ PRE_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 4,
+ REMOVE = IBinder::FIRST_CALL_TRANSACTION + 5,
+ GET_AUTHENTICATOR_ID = IBinder::FIRST_CALL_TRANSACTION + 6,
+ SET_ACTIVE_GROUP = IBinder::FIRST_CALL_TRANSACTION + 7,
+ OPEN_HAL = IBinder::FIRST_CALL_TRANSACTION + 8,
+ CLOSE_HAL = IBinder::FIRST_CALL_TRANSACTION + 9,
+ INIT = IBinder::FIRST_CALL_TRANSACTION + 10,
+ POST_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 11,
+ ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 12,
+ };
+
+ IFingerprintDaemon() { }
+ virtual ~IFingerprintDaemon() { }
+ virtual const android::String16& getInterfaceDescriptor() const;
+
+ // Binder interface methods
+ virtual void init(const sp<IFingerprintDaemonCallback>& callback) = 0;
+ virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId,
+ int32_t timeout) = 0;
+ virtual uint64_t preEnroll() = 0;
+ virtual int32_t postEnroll() = 0;
+ virtual int32_t stopEnrollment() = 0;
+ virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId) = 0;
+ virtual int32_t stopAuthentication() = 0;
+ virtual int32_t remove(int32_t fingerId, int32_t groupId) = 0;
+ virtual int32_t enumerate() = 0;
+ virtual uint64_t getAuthenticatorId() = 0;
+ virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen) = 0;
+ virtual int64_t openHal() = 0;
+ virtual int32_t closeHal() = 0;
+
+ // DECLARE_META_INTERFACE - C++ client interface not needed
+ static const android::String16 descriptor;
+ static void hal_notify_callback(const fingerprint_msg_t *msg);
+};
+
+// ----------------------------------------------------------------------------
+
+class BnFingerprintDaemon: public BnInterface<IFingerprintDaemon> {
+ public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0);
+ private:
+ bool checkPermission(const String16& permission);
+};
+
+} // namespace android
+
+#endif // IFINGERPRINT_DAEMON_H_
+
diff --git a/fingerprintd/IFingerprintDaemonCallback.cpp b/fingerprintd/IFingerprintDaemonCallback.cpp
new file mode 100644
index 0000000..1d75aa7
--- /dev/null
+++ b/fingerprintd/IFingerprintDaemonCallback.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IFingerprintDaemonCallback"
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+
+#include "IFingerprintDaemonCallback.h"
+
+namespace android {
+
+class BpFingerprintDaemonCallback : public BpInterface<IFingerprintDaemonCallback>
+{
+public:
+ explicit BpFingerprintDaemonCallback(const sp<IBinder>& impl) :
+ BpInterface<IFingerprintDaemonCallback>(impl) {
+ }
+ virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
+ data.writeInt64(devId);
+ data.writeInt32(fpId);
+ data.writeInt32(gpId);
+ data.writeInt32(rem);
+ return remote()->transact(ON_ENROLL_RESULT, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
+ data.writeInt64(devId);
+ data.writeInt32(acquiredInfo);
+ return remote()->transact(ON_ACQUIRED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t onAuthenticated(int64_t devId, int32_t fpId, int32_t gpId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
+ data.writeInt64(devId);
+ data.writeInt32(fpId);
+ data.writeInt32(gpId);
+ return remote()->transact(ON_AUTHENTICATED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t onError(int64_t devId, int32_t error) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
+ data.writeInt64(devId);
+ data.writeInt32(error);
+ return remote()->transact(ON_ERROR, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t onRemoved(int64_t devId, int32_t fpId, int32_t gpId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
+ data.writeInt64(devId);
+ data.writeInt32(fpId);
+ data.writeInt32(gpId);
+ return remote()->transact(ON_REMOVED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t onEnumerate(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
+ data.writeInt64(devId);
+ data.writeInt32(fpId);
+ data.writeInt32(gpId);
+ data.writeInt32(rem);
+ return remote()->transact(ON_ENUMERATE, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(FingerprintDaemonCallback,
+ "android.hardware.fingerprint.IFingerprintDaemonCallback");
+
+}; // namespace android
diff --git a/fingerprintd/IFingerprintDaemonCallback.h b/fingerprintd/IFingerprintDaemonCallback.h
new file mode 100644
index 0000000..e343cb4
--- /dev/null
+++ b/fingerprintd/IFingerprintDaemonCallback.h
@@ -0,0 +1,54 @@
+/*
+ * 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 IFINGERPRINT_DAEMON_CALLBACK_H_
+#define IFINGERPRINT_DAEMON_CALLBACK_H_
+
+#include <inttypes.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+/*
+* Communication channel back to FingerprintService.java
+*/
+class IFingerprintDaemonCallback : public IInterface {
+ public:
+ // must be kept in sync with IFingerprintService.aidl
+ enum {
+ ON_ENROLL_RESULT = IBinder::FIRST_CALL_TRANSACTION + 0,
+ ON_ACQUIRED = IBinder::FIRST_CALL_TRANSACTION + 1,
+ ON_AUTHENTICATED = IBinder::FIRST_CALL_TRANSACTION + 2,
+ ON_ERROR = IBinder::FIRST_CALL_TRANSACTION + 3,
+ ON_REMOVED = IBinder::FIRST_CALL_TRANSACTION + 4,
+ ON_ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 5,
+ };
+
+ virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) = 0;
+ virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) = 0;
+ virtual status_t onAuthenticated(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
+ virtual status_t onError(int64_t devId, int32_t error) = 0;
+ virtual status_t onRemoved(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
+ virtual status_t onEnumerate(int64_t devId, int32_t fingerId, int32_t groupId, int32_t rem) = 0;
+
+ DECLARE_META_INTERFACE(FingerprintDaemonCallback);
+};
+
+}; // namespace android
+
+#endif // IFINGERPRINT_DAEMON_CALLBACK_H_
diff --git a/fingerprintd/fingerprintd.cpp b/fingerprintd/fingerprintd.cpp
new file mode 100644
index 0000000..2fc2d0a
--- /dev/null
+++ b/fingerprintd/fingerprintd.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "fingerprintd"
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
+#include <hardware/hw_auth_token.h>
+#include <keystore/IKeystoreService.h>
+#include <keystore/keystore.h> // for error codes
+#include <log/log.h>
+#include <utils/Log.h>
+#include <utils/String16.h>
+
+#include "FingerprintDaemonProxy.h"
+
+int main() {
+ ALOGI("Starting " LOG_TAG);
+ android::sp<android::IServiceManager> serviceManager = android::defaultServiceManager();
+ android::sp<android::FingerprintDaemonProxy> proxy =
+ android::FingerprintDaemonProxy::getInstance();
+ android::status_t ret = serviceManager->addService(
+ android::FingerprintDaemonProxy::descriptor, proxy);
+ if (ret != android::OK) {
+ ALOGE("Couldn't register " LOG_TAG " binder service!");
+ return -1;
+ }
+
+ /*
+ * We're the only thread in existence, so we're just going to process
+ * Binder transaction as a single-threaded program.
+ */
+ android::IPCThreadState::self()->joinThreadPool();
+ ALOGI("Done");
+ return 0;
+}
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 08d0671..956c702 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -1,43 +1,68 @@
# Copyright 2011 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
+
+common_static_libraries := \
+ liblogwrap \
+ libfec \
+ libfec_rs \
+ libbase \
+ libcrypto_utils \
+ libcrypto \
+ libext4_utils \
+ libsquashfs_utils \
+ libselinux \
+ libavb
+
include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+LOCAL_SRC_FILES:= \
+ fs_mgr.cpp \
+ fs_mgr_dm_ioctl.cpp \
+ fs_mgr_format.cpp \
+ fs_mgr_fstab.cpp \
+ fs_mgr_slotselect.cpp \
+ fs_mgr_verity.cpp \
+ fs_mgr_avb.cpp \
+ fs_mgr_avb_ops.cpp
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ system/vold \
+ system/extras/ext4_utils \
+ bootable/recovery
LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils
-LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Werror
-
+ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
+ ifeq ($(TARGET_USES_MKE2FS), true)
+ LOCAL_CFLAGS += -DTARGET_USES_MKE2FS
+ endif
+endif
ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
endif
-
include $(BUILD_STATIC_LIBRARY)
-
-
include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= fs_mgr_main.c
-
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+LOCAL_SRC_FILES:= fs_mgr_main.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
LOCAL_MODULE:= fs_mgr
-
LOCAL_MODULE_TAGS := optional
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-
-LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils
+LOCAL_STATIC_LIBRARIES := libfs_mgr \
+ $(common_static_libraries) \
+ libcutils \
+ liblog \
+ libc \
+ libsparse \
+ libz \
+ libselinux
LOCAL_CXX_STL := libc++_static
-
LOCAL_CFLAGS := -Werror
-
include $(BUILD_EXECUTABLE)
-
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
deleted file mode 100644
index 044c895..0000000
--- a/fs_mgr/fs_mgr.c
+++ /dev/null
@@ -1,866 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <libgen.h>
-#include <time.h>
-#include <sys/swap.h>
-#include <dirent.h>
-#include <ext4.h>
-#include <ext4_sb.h>
-#include <ext4_crypt_init_extensions.h>
-
-#include <linux/loop.h>
-#include <private/android_filesystem_config.h>
-#include <cutils/android_reboot.h>
-#include <cutils/partition_utils.h>
-#include <cutils/properties.h>
-#include <logwrap/logwrap.h>
-
-#include "mincrypt/rsa.h"
-#include "mincrypt/sha.h"
-#include "mincrypt/sha256.h"
-
-#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_verity.h"
-
-#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
-#define KEY_IN_FOOTER "footer"
-
-#define E2FSCK_BIN "/system/bin/e2fsck"
-#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs"
-#define MKSWAP_BIN "/system/bin/mkswap"
-
-#define FSCK_LOG_FILE "/dev/fscklogs/log"
-
-#define ZRAM_CONF_DEV "/sys/block/zram0/disksize"
-
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
-
-/*
- * gettime() - returns the time in seconds of the system's monotonic clock or
- * zero on error.
- */
-static time_t gettime(void)
-{
- struct timespec ts;
- int ret;
-
- ret = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (ret < 0) {
- ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
- return 0;
- }
-
- return ts.tv_sec;
-}
-
-static int wait_for_file(const char *filename, int timeout)
-{
- struct stat info;
- time_t timeout_time = gettime() + timeout;
- int ret = -1;
-
- while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
- usleep(10000);
-
- return ret;
-}
-
-static void check_fs(char *blk_device, char *fs_type, char *target)
-{
- int status;
- int ret;
- long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
- char tmpmnt_opts[64] = "errors=remount-ro";
- char *e2fsck_argv[] = {
- E2FSCK_BIN,
- "-y",
- blk_device
- };
-
- /* Check for the types of filesystems we know how to check */
- if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
- /*
- * First try to mount and unmount the filesystem. We do this because
- * the kernel is more efficient than e2fsck in running the journal and
- * processing orphaned inodes, and on at least one device with a
- * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes
- * to do what the kernel does in about a second.
- *
- * After mounting and unmounting the filesystem, run e2fsck, and if an
- * error is recorded in the filesystem superblock, e2fsck will do a full
- * check. Otherwise, it does nothing. If the kernel cannot mount the
- * filesytsem due to an error, e2fsck is still run to do a full check
- * fix the filesystem.
- */
- errno = 0;
- if (!strcmp(fs_type, "ext4")) {
- // This option is only valid with ext4
- strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
- }
- ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
- INFO("%s(): mount(%s,%s,%s)=%d: %s\n",
- __func__, blk_device, target, fs_type, ret, strerror(errno));
- if (!ret) {
- int i;
- for (i = 0; i < 5; i++) {
- // Try to umount 5 times before continuing on.
- // Should we try rebooting if all attempts fail?
- int result = umount(target);
- if (result == 0) {
- INFO("%s(): unmount(%s) succeeded\n", __func__, target);
- break;
- }
- ERROR("%s(): umount(%s)=%d: %s\n", __func__, target, result, strerror(errno));
- sleep(1);
- }
- }
-
- /*
- * Some system images do not have e2fsck for licensing reasons
- * (e.g. recent SDK system images). Detect these and skip the check.
- */
- if (access(E2FSCK_BIN, X_OK)) {
- INFO("Not running %s on %s (executable not in system image)\n",
- E2FSCK_BIN, blk_device);
- } else {
- INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
-
- ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
- &status, true, LOG_KLOG | LOG_FILE,
- true, FSCK_LOG_FILE, NULL, 0);
-
- if (ret < 0) {
- /* No need to check for error in fork, we can't really handle it now */
- ERROR("Failed trying to run %s\n", E2FSCK_BIN);
- }
- }
- } else if (!strcmp(fs_type, "f2fs")) {
- char *f2fs_fsck_argv[] = {
- F2FS_FSCK_BIN,
- "-a",
- blk_device
- };
- INFO("Running %s -a %s\n", F2FS_FSCK_BIN, blk_device);
-
- ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv,
- &status, true, LOG_KLOG | LOG_FILE,
- true, FSCK_LOG_FILE, NULL, 0);
- if (ret < 0) {
- /* No need to check for error in fork, we can't really handle it now */
- ERROR("Failed trying to run %s\n", F2FS_FSCK_BIN);
- }
- }
-
- return;
-}
-
-static void remove_trailing_slashes(char *n)
-{
- int len;
-
- len = strlen(n) - 1;
- while ((*(n + len) == '/') && len) {
- *(n + len) = '\0';
- len--;
- }
-}
-
-/*
- * Mark the given block device as read-only, using the BLKROSET ioctl.
- * Return 0 on success, and -1 on error.
- */
-int fs_mgr_set_blk_ro(const char *blockdev)
-{
- int fd;
- int rc = -1;
- int ON = 1;
-
- fd = TEMP_FAILURE_RETRY(open(blockdev, O_RDONLY | O_CLOEXEC));
- if (fd < 0) {
- // should never happen
- return rc;
- }
-
- rc = ioctl(fd, BLKROSET, &ON);
- close(fd);
-
- return rc;
-}
-
-/*
- * __mount(): wrapper around the mount() system call which also
- * sets the underlying block device to read-only if the mount is read-only.
- * See "man 2 mount" for return values.
- */
-static int __mount(const char *source, const char *target, const struct fstab_rec *rec)
-{
- unsigned long mountflags = rec->flags;
- int ret;
- int save_errno;
-
- /* We need this because sometimes we have legacy symlinks
- * that are lingering around and need cleaning up.
- */
- struct stat info;
- if (!lstat(target, &info))
- if ((info.st_mode & S_IFMT) == S_IFLNK)
- unlink(target);
- mkdir(target, 0755);
- ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
- save_errno = errno;
- INFO("%s(source=%s,target=%s,type=%s)=%d\n", __func__, source, target, rec->fs_type, ret);
- if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
- fs_mgr_set_blk_ro(source);
- }
- errno = save_errno;
- return ret;
-}
-
-static int fs_match(char *in1, char *in2)
-{
- char *n1;
- char *n2;
- int ret;
-
- n1 = strdup(in1);
- n2 = strdup(in2);
-
- remove_trailing_slashes(n1);
- remove_trailing_slashes(n2);
-
- ret = !strcmp(n1, n2);
-
- free(n1);
- free(n2);
-
- return ret;
-}
-
-static int device_is_debuggable() {
- int ret = -1;
- char value[PROP_VALUE_MAX];
- ret = __system_property_get("ro.debuggable", value);
- if (ret < 0)
- return ret;
- return strcmp(value, "1") ? 0 : 1;
-}
-
-static int device_is_secure() {
- int ret = -1;
- char value[PROP_VALUE_MAX];
- ret = __system_property_get("ro.secure", value);
- /* If error, we want to fail secure */
- if (ret < 0)
- return 1;
- return strcmp(value, "0") ? 1 : 0;
-}
-
-static int device_is_force_encrypted() {
- int ret = -1;
- char value[PROP_VALUE_MAX];
- ret = __system_property_get("ro.vold.forceencryption", value);
- if (ret < 0)
- return 0;
- return strcmp(value, "1") ? 0 : 1;
-}
-
-/*
- * Tries to mount any of the consecutive fstab entries that match
- * the mountpoint of the one given by fstab->recs[start_idx].
- *
- * end_idx: On return, will be the last rec that was looked at.
- * attempted_idx: On return, will indicate which fstab rec
- * succeeded. In case of failure, it will be the start_idx.
- * Returns
- * -1 on failure with errno set to match the 1st mount failure.
- * 0 on success.
- */
-static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
-{
- int i;
- int mount_errno = 0;
- int mounted = 0;
-
- if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
- errno = EINVAL;
- if (end_idx) *end_idx = start_idx;
- if (attempted_idx) *end_idx = start_idx;
- return -1;
- }
-
- /* Hunt down an fstab entry for the same mount point that might succeed */
- for (i = start_idx;
- /* We required that fstab entries for the same mountpoint be consecutive */
- i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point);
- i++) {
- /*
- * Don't try to mount/encrypt the same mount point again.
- * Deal with alternate entries for the same point which are required to be all following
- * each other.
- */
- if (mounted) {
- ERROR("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted as %s.\n", __func__,
- fstab->recs[i].mount_point, i, fstab->recs[i].fs_type, fstab->recs[*attempted_idx].fs_type);
- continue;
- }
-
- if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
- check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
- fstab->recs[i].mount_point);
- }
- if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
- *attempted_idx = i;
- mounted = 1;
- if (i != start_idx) {
- ERROR("%s(): Mounted %s on %s with fs_type=%s instead of %s\n", __func__,
- fstab->recs[i].blk_device, fstab->recs[i].mount_point, fstab->recs[i].fs_type,
- fstab->recs[start_idx].fs_type);
- }
- } else {
- /* back up errno for crypto decisions */
- mount_errno = errno;
- }
- }
-
- /* Adjust i for the case where it was still withing the recs[] */
- if (i < fstab->num_entries) --i;
-
- *end_idx = i;
- if (!mounted) {
- *attempted_idx = start_idx;
- errno = mount_errno;
- return -1;
- }
- return 0;
-}
-
-static int translate_ext_labels(struct fstab_rec *rec)
-{
- DIR *blockdir = NULL;
- struct dirent *ent;
- char *label;
- size_t label_len;
- int ret = -1;
-
- if (strncmp(rec->blk_device, "LABEL=", 6))
- return 0;
-
- label = rec->blk_device + 6;
- label_len = strlen(label);
-
- if (label_len > 16) {
- ERROR("FS label is longer than allowed by filesystem\n");
- goto out;
- }
-
-
- blockdir = opendir("/dev/block");
- if (!blockdir) {
- ERROR("couldn't open /dev/block\n");
- goto out;
- }
-
- while ((ent = readdir(blockdir))) {
- int fd;
- char super_buf[1024];
- struct ext4_super_block *sb;
-
- if (ent->d_type != DT_BLK)
- continue;
-
- fd = openat(dirfd(blockdir), ent->d_name, O_RDONLY);
- if (fd < 0) {
- ERROR("Cannot open block device /dev/block/%s\n", ent->d_name);
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(lseek(fd, 1024, SEEK_SET)) < 0 ||
- TEMP_FAILURE_RETRY(read(fd, super_buf, 1024)) != 1024) {
- /* Probably a loopback device or something else without a readable
- * superblock.
- */
- close(fd);
- continue;
- }
-
- sb = (struct ext4_super_block *)super_buf;
- if (sb->s_magic != EXT4_SUPER_MAGIC) {
- INFO("/dev/block/%s not ext{234}\n", ent->d_name);
- continue;
- }
-
- if (!strncmp(label, sb->s_volume_name, label_len)) {
- char *new_blk_device;
-
- if (asprintf(&new_blk_device, "/dev/block/%s", ent->d_name) < 0) {
- ERROR("Could not allocate block device string\n");
- goto out;
- }
-
- INFO("resolved label %s to %s\n", rec->blk_device, new_blk_device);
-
- free(rec->blk_device);
- rec->blk_device = new_blk_device;
- ret = 0;
- break;
- }
- }
-
-out:
- closedir(blockdir);
- return ret;
-}
-
-// Check to see if a mountable volume has encryption requirements
-static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec)
-{
- /* If this is block encryptable, need to trigger encryption */
- if ( (rec->fs_mgr_flags & MF_FORCECRYPT)
- || (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {
- if (umount(rec->mount_point) == 0) {
- return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
- } else {
- WARNING("Could not umount %s (%s) - allow continue unencrypted\n",
- rec->mount_point, strerror(errno));
- return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
- }
- }
-
- // Deal with file level encryption
- if (rec->fs_mgr_flags & MF_FILEENCRYPTION) {
- // Default or not yet initialized encryption requires no more work here
- if (!e4crypt_non_default_key(rec->mount_point)) {
- INFO("%s is default file encrypted\n", rec->mount_point);
- return FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED;
- }
-
- INFO("%s is non-default file encrypted\n", rec->mount_point);
-
- // Uses non-default key, so must unmount and set up temp file system
- if (umount(rec->mount_point)) {
- ERROR("Failed to umount %s - rebooting\n", rec->mount_point);
- return FS_MGR_MNTALL_FAIL;
- }
-
- if (fs_mgr_do_tmpfs_mount(rec->mount_point) != 0) {
- ERROR("Failed to mount a tmpfs at %s\n", rec->mount_point);
- return FS_MGR_MNTALL_FAIL;
- }
-
- // Mount data temporarily so we can access unencrypted dir
- char tmp_mnt[PATH_MAX];
- strlcpy(tmp_mnt, rec->mount_point, sizeof(tmp_mnt));
- strlcat(tmp_mnt, "/tmp_mnt", sizeof(tmp_mnt));
- if (mkdir(tmp_mnt, 0700)) {
- ERROR("Failed to create temp mount point\n");
- return FS_MGR_MNTALL_FAIL;
- }
-
- if (fs_mgr_do_mount(fstab, rec->mount_point,
- rec->blk_device, tmp_mnt)) {
- ERROR("Error temp mounting encrypted file system\n");
- return FS_MGR_MNTALL_FAIL;
- }
-
- return FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED;
- }
-
- return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
-}
-
-/* When multiple fstab records share the same mount_point, it will
- * try to mount each one in turn, and ignore any duplicates after a
- * first successful mount.
- * Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
- */
-int fs_mgr_mount_all(struct fstab *fstab)
-{
- int i = 0;
- int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
- int error_count = 0;
- int mret = -1;
- int mount_errno = 0;
- int attempted_idx = -1;
-
- if (!fstab) {
- return -1;
- }
-
- for (i = 0; i < fstab->num_entries; i++) {
- /* Don't mount entries that are managed by vold */
- if (fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) {
- continue;
- }
-
- /* Skip swap and raw partition entries such as boot, recovery, etc */
- if (!strcmp(fstab->recs[i].fs_type, "swap") ||
- !strcmp(fstab->recs[i].fs_type, "emmc") ||
- !strcmp(fstab->recs[i].fs_type, "mtd")) {
- continue;
- }
-
- /* Skip mounting the root partition, as it will already have been mounted */
- if (!strcmp(fstab->recs[i].mount_point, "/")) {
- if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) {
- fs_mgr_set_blk_ro(fstab->recs[i].blk_device);
- }
- continue;
- }
-
- /* Translate LABEL= file system labels into block devices */
- if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
- !strcmp(fstab->recs[i].fs_type, "ext3") ||
- !strcmp(fstab->recs[i].fs_type, "ext4")) {
- int tret = translate_ext_labels(&fstab->recs[i]);
- if (tret < 0) {
- ERROR("Could not translate label to block device\n");
- continue;
- }
- }
-
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
- }
-
- if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
- int rc = fs_mgr_setup_verity(&fstab->recs[i]);
- if (device_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
- INFO("Verity disabled");
- } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
- ERROR("Could not set up verified partition, skipping!\n");
- continue;
- }
- }
- int last_idx_inspected;
- mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
- i = last_idx_inspected;
- mount_errno = errno;
-
- /* Deal with encryptability. */
- if (!mret) {
- int status = handle_encryptable(fstab, &fstab->recs[attempted_idx]);
-
- if (status == FS_MGR_MNTALL_FAIL) {
- /* Fatal error - no point continuing */
- return status;
- }
-
- if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
- if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
- // Log and continue
- ERROR("Only one encryptable/encrypted partition supported\n");
- }
- encryptable = status;
- }
-
- /* Success! Go get the next one */
- continue;
- }
-
- /* mount(2) returned an error, check if it's encryptable and deal with it */
- if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
- fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
- if(partition_wiped(fstab->recs[attempted_idx].blk_device)) {
- ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__,
- fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
- fstab->recs[attempted_idx].fs_type);
- encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
- continue;
- } else {
- /* Need to mount a tmpfs at this mountpoint for now, and set
- * properties that vold will query later for decrypting
- */
- ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__,
- fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
- fstab->recs[attempted_idx].fs_type);
- if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
- ++error_count;
- continue;
- }
- }
- encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
- } else {
- ERROR("Failed to mount an un-encryptable or wiped partition on"
- "%s at %s options: %s error: %s\n",
- fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
- fstab->recs[attempted_idx].fs_options, strerror(mount_errno));
- ++error_count;
- continue;
- }
- }
-
- if (error_count) {
- return -1;
- } else {
- return encryptable;
- }
-}
-
-/* If tmp_mount_point is non-null, mount the filesystem there. This is for the
- * tmp mount we do to check the user password
- * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
- * in turn, and stop on 1st success, or no more match.
- */
-int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
- char *tmp_mount_point)
-{
- int i = 0;
- int ret = FS_MGR_DOMNT_FAILED;
- int mount_errors = 0;
- int first_mount_errno = 0;
- char *m;
-
- if (!fstab) {
- return ret;
- }
-
- for (i = 0; i < fstab->num_entries; i++) {
- if (!fs_match(fstab->recs[i].mount_point, n_name)) {
- continue;
- }
-
- /* We found our match */
- /* If this swap or a raw partition, report an error */
- if (!strcmp(fstab->recs[i].fs_type, "swap") ||
- !strcmp(fstab->recs[i].fs_type, "emmc") ||
- !strcmp(fstab->recs[i].fs_type, "mtd")) {
- ERROR("Cannot mount filesystem of type %s on %s\n",
- fstab->recs[i].fs_type, n_blk_device);
- goto out;
- }
-
- /* First check the filesystem if requested */
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(n_blk_device, WAIT_TIMEOUT);
- }
-
- if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
- check_fs(n_blk_device, fstab->recs[i].fs_type,
- fstab->recs[i].mount_point);
- }
-
- if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
- int rc = fs_mgr_setup_verity(&fstab->recs[i]);
- if (device_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
- INFO("Verity disabled");
- } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
- ERROR("Could not set up verified partition, skipping!\n");
- continue;
- }
- }
-
- /* Now mount it where requested */
- if (tmp_mount_point) {
- m = tmp_mount_point;
- } else {
- m = fstab->recs[i].mount_point;
- }
- if (__mount(n_blk_device, m, &fstab->recs[i])) {
- if (!first_mount_errno) first_mount_errno = errno;
- mount_errors++;
- continue;
- } else {
- ret = 0;
- goto out;
- }
- }
- if (mount_errors) {
- ERROR("Cannot mount filesystem on %s at %s. error: %s\n",
- n_blk_device, m, strerror(first_mount_errno));
- if (first_mount_errno == EBUSY) {
- ret = FS_MGR_DOMNT_BUSY;
- } else {
- ret = FS_MGR_DOMNT_FAILED;
- }
- } else {
- /* We didn't find a match, say so and return an error */
- ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point);
- }
-
-out:
- return ret;
-}
-
-/*
- * mount a tmpfs filesystem at the given point.
- * return 0 on success, non-zero on failure.
- */
-int fs_mgr_do_tmpfs_mount(char *n_name)
-{
- int ret;
-
- ret = mount("tmpfs", n_name, "tmpfs",
- MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
- if (ret < 0) {
- ERROR("Cannot mount tmpfs filesystem at %s\n", n_name);
- return -1;
- }
-
- /* Success */
- return 0;
-}
-
-int fs_mgr_unmount_all(struct fstab *fstab)
-{
- int i = 0;
- int ret = 0;
-
- if (!fstab) {
- return -1;
- }
-
- while (fstab->recs[i].blk_device) {
- if (umount(fstab->recs[i].mount_point)) {
- ERROR("Cannot unmount filesystem at %s\n", fstab->recs[i].mount_point);
- ret = -1;
- }
- i++;
- }
-
- return ret;
-}
-
-/* This must be called after mount_all, because the mkswap command needs to be
- * available.
- */
-int fs_mgr_swapon_all(struct fstab *fstab)
-{
- int i = 0;
- int flags = 0;
- int err = 0;
- int ret = 0;
- int status;
- char *mkswap_argv[2] = {
- MKSWAP_BIN,
- NULL
- };
-
- if (!fstab) {
- return -1;
- }
-
- for (i = 0; i < fstab->num_entries; i++) {
- /* Skip non-swap entries */
- if (strcmp(fstab->recs[i].fs_type, "swap")) {
- continue;
- }
-
- if (fstab->recs[i].zram_size > 0) {
- /* A zram_size was specified, so we need to configure the
- * device. There is no point in having multiple zram devices
- * on a system (all the memory comes from the same pool) so
- * we can assume the device number is 0.
- */
- FILE *zram_fp;
-
- zram_fp = fopen(ZRAM_CONF_DEV, "r+");
- if (zram_fp == NULL) {
- ERROR("Unable to open zram conf device %s\n", ZRAM_CONF_DEV);
- ret = -1;
- continue;
- }
- fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size);
- fclose(zram_fp);
- }
-
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
- }
-
- /* Initialize the swap area */
- mkswap_argv[1] = fstab->recs[i].blk_device;
- err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv,
- &status, true, LOG_KLOG, false, NULL,
- NULL, 0);
- if (err) {
- ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device);
- ret = -1;
- continue;
- }
-
- /* If -1, then no priority was specified in fstab, so don't set
- * SWAP_FLAG_PREFER or encode the priority */
- if (fstab->recs[i].swap_prio >= 0) {
- flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) &
- SWAP_FLAG_PRIO_MASK;
- flags |= SWAP_FLAG_PREFER;
- } else {
- flags = 0;
- }
- err = swapon(fstab->recs[i].blk_device, flags);
- if (err) {
- ERROR("swapon failed for %s\n", fstab->recs[i].blk_device);
- ret = -1;
- }
- }
-
- return ret;
-}
-
-/*
- * key_loc must be at least PROPERTY_VALUE_MAX bytes long
- *
- * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
- */
-int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size)
-{
- int i = 0;
-
- if (!fstab) {
- return -1;
- }
- /* Initialize return values to null strings */
- if (key_loc) {
- *key_loc = '\0';
- }
- if (real_blk_device) {
- *real_blk_device = '\0';
- }
-
- /* Look for the encryptable partition to find the data */
- for (i = 0; i < fstab->num_entries; i++) {
- /* Don't deal with vold managed enryptable partitions here */
- if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
- continue;
- }
- if (!(fstab->recs[i].fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT))) {
- continue;
- }
-
- /* We found a match */
- if (key_loc) {
- strlcpy(key_loc, fstab->recs[i].key_loc, size);
- }
- if (real_blk_device) {
- strlcpy(real_blk_device, fstab->recs[i].blk_device, size);
- }
- break;
- }
-
- return 0;
-}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
new file mode 100644
index 0000000..be84e8a
--- /dev/null
+++ b/fs_mgr/fs_mgr.cpp
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/swap.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
+#include <cutils/partition_utils.h>
+#include <cutils/properties.h>
+#include <ext4_utils/ext4.h>
+#include <ext4_utils/ext4_crypt_init_extensions.h>
+#include <ext4_utils/ext4_sb.h>
+#include <ext4_utils/ext4_utils.h>
+#include <ext4_utils/wipe.h>
+#include <linux/fs.h>
+#include <linux/loop.h>
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_avb.h"
+#include "fs_mgr_priv_verity.h"
+
+#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
+#define KEY_IN_FOOTER "footer"
+
+#define E2FSCK_BIN "/system/bin/e2fsck"
+#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs"
+#define MKSWAP_BIN "/system/bin/mkswap"
+#define TUNE2FS_BIN "/system/bin/tune2fs"
+
+#define FSCK_LOG_FILE "/dev/fscklogs/log"
+
+#define ZRAM_CONF_DEV "/sys/block/zram0/disksize"
+#define ZRAM_CONF_MCS "/sys/block/zram0/max_comp_streams"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+/*
+ * gettime() - returns the time in seconds of the system's monotonic clock or
+ * zero on error.
+ */
+static time_t gettime(void)
+{
+ struct timespec ts;
+ int ret;
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (ret < 0) {
+ PERROR << "clock_gettime(CLOCK_MONOTONIC) failed";
+ return 0;
+ }
+
+ return ts.tv_sec;
+}
+
+static int wait_for_file(const char *filename, int timeout)
+{
+ struct stat info;
+ time_t timeout_time = gettime() + timeout;
+ int ret = -1;
+
+ while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
+ usleep(10000);
+
+ return ret;
+}
+
+static void check_fs(const char *blk_device, char *fs_type, char *target)
+{
+ int status;
+ int ret;
+ long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
+ char tmpmnt_opts[64] = "errors=remount-ro";
+ const char *e2fsck_argv[] = {
+ E2FSCK_BIN,
+#ifndef TARGET_USES_MKE2FS // "-f" only for old ext4 generation tool
+ "-f",
+#endif
+ "-y",
+ blk_device
+ };
+
+ /* Check for the types of filesystems we know how to check */
+ if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+ /*
+ * First try to mount and unmount the filesystem. We do this because
+ * the kernel is more efficient than e2fsck in running the journal and
+ * processing orphaned inodes, and on at least one device with a
+ * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes
+ * to do what the kernel does in about a second.
+ *
+ * After mounting and unmounting the filesystem, run e2fsck, and if an
+ * error is recorded in the filesystem superblock, e2fsck will do a full
+ * check. Otherwise, it does nothing. If the kernel cannot mount the
+ * filesytsem due to an error, e2fsck is still run to do a full check
+ * fix the filesystem.
+ */
+ errno = 0;
+ if (!strcmp(fs_type, "ext4")) {
+ // This option is only valid with ext4
+ strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
+ }
+ ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
+ PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target
+ << "," << fs_type << ")=" << ret;
+ if (!ret) {
+ int i;
+ for (i = 0; i < 5; i++) {
+ // Try to umount 5 times before continuing on.
+ // Should we try rebooting if all attempts fail?
+ int result = umount(target);
+ if (result == 0) {
+ LINFO << __FUNCTION__ << "(): unmount(" << target
+ << ") succeeded";
+ break;
+ }
+ PERROR << __FUNCTION__ << "(): umount(" << target << ")="
+ << result;
+ sleep(1);
+ }
+ }
+
+ /*
+ * Some system images do not have e2fsck for licensing reasons
+ * (e.g. recent SDK system images). Detect these and skip the check.
+ */
+ if (access(E2FSCK_BIN, X_OK)) {
+ LINFO << "Not running " << E2FSCK_BIN << " on " << blk_device
+ << " (executable not in system image)";
+ } else {
+ LINFO << "Running " << E2FSCK_BIN << " on " << blk_device;
+
+ ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv),
+ const_cast<char **>(e2fsck_argv),
+ &status, true, LOG_KLOG | LOG_FILE,
+ true,
+ const_cast<char *>(FSCK_LOG_FILE),
+ NULL, 0);
+
+ if (ret < 0) {
+ /* No need to check for error in fork, we can't really handle it now */
+ LERROR << "Failed trying to run " << E2FSCK_BIN;
+ }
+ }
+ } else if (!strcmp(fs_type, "f2fs")) {
+ const char *f2fs_fsck_argv[] = {
+ F2FS_FSCK_BIN,
+ "-a",
+ blk_device
+ };
+ LINFO << "Running " << F2FS_FSCK_BIN << " -a " << blk_device;
+
+ ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv),
+ const_cast<char **>(f2fs_fsck_argv),
+ &status, true, LOG_KLOG | LOG_FILE,
+ true, const_cast<char *>(FSCK_LOG_FILE),
+ NULL, 0);
+ if (ret < 0) {
+ /* No need to check for error in fork, we can't really handle it now */
+ LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
+ }
+ }
+
+ return;
+}
+
+/* Function to read the primary superblock */
+static int read_super_block(int fd, struct ext4_super_block *sb)
+{
+ off64_t ret;
+
+ ret = lseek64(fd, 1024, SEEK_SET);
+ if (ret < 0)
+ return ret;
+
+ ret = read(fd, sb, sizeof(*sb));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(*sb))
+ return ret;
+
+ return 0;
+}
+
+static ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es)
+{
+ return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) |
+ le32_to_cpu(es->s_blocks_count_lo);
+}
+
+static ext4_fsblk_t ext4_r_blocks_count(struct ext4_super_block *es)
+{
+ return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) |
+ le32_to_cpu(es->s_r_blocks_count_lo);
+}
+
+static int do_quota(char *blk_device, char *fs_type, struct fstab_rec *rec)
+{
+ int force_check = 0;
+ if (!strcmp(fs_type, "ext4")) {
+ /*
+ * Some system images do not have tune2fs for licensing reasons
+ * Detect these and skip reserve blocks.
+ */
+ if (access(TUNE2FS_BIN, X_OK)) {
+ LERROR << "Not running " << TUNE2FS_BIN << " on "
+ << blk_device << " (executable not in system image)";
+ } else {
+ const char* arg1 = nullptr;
+ const char* arg2 = nullptr;
+ int status = 0;
+ int ret = 0;
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
+ if (fd >= 0) {
+ struct ext4_super_block sb;
+ ret = read_super_block(fd, &sb);
+ if (ret < 0) {
+ PERROR << "Can't read '" << blk_device << "' super block";
+ return force_check;
+ }
+
+ int has_quota = (sb.s_feature_ro_compat
+ & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+ int want_quota = fs_mgr_is_quota(rec) != 0;
+
+ if (has_quota == want_quota) {
+ LINFO << "Requested quota status is match on " << blk_device;
+ return force_check;
+ } else if (want_quota) {
+ LINFO << "Enabling quota on " << blk_device;
+ arg1 = "-Oquota";
+ arg2 = "-Qusrquota,grpquota";
+ force_check = 1;
+ } else {
+ LINFO << "Disabling quota on " << blk_device;
+ arg1 = "-Q^usrquota,^grpquota";
+ arg2 = "-O^quota";
+ }
+ } else {
+ PERROR << "Failed to open '" << blk_device << "'";
+ return force_check;
+ }
+
+ const char *tune2fs_argv[] = {
+ TUNE2FS_BIN,
+ arg1,
+ arg2,
+ blk_device,
+ };
+ ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
+ const_cast<char **>(tune2fs_argv),
+ &status, true, LOG_KLOG | LOG_FILE,
+ true, NULL, NULL, 0);
+ if (ret < 0) {
+ /* No need to check for error in fork, we can't really handle it now */
+ LERROR << "Failed trying to run " << TUNE2FS_BIN;
+ }
+ }
+ }
+ return force_check;
+}
+
+static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec)
+{
+ /* Check for the types of filesystems we know how to check */
+ if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+ /*
+ * Some system images do not have tune2fs for licensing reasons
+ * Detect these and skip reserve blocks.
+ */
+ if (access(TUNE2FS_BIN, X_OK)) {
+ LERROR << "Not running " << TUNE2FS_BIN << " on "
+ << blk_device << " (executable not in system image)";
+ } else {
+ LINFO << "Running " << TUNE2FS_BIN << " on " << blk_device;
+
+ int status = 0;
+ int ret = 0;
+ unsigned long reserved_blocks = 0;
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
+ if (fd >= 0) {
+ struct ext4_super_block sb;
+ ret = read_super_block(fd, &sb);
+ if (ret < 0) {
+ PERROR << "Can't read '" << blk_device << "' super block";
+ return;
+ }
+ reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(&sb);
+ unsigned long reserved_threshold = ext4_blocks_count(&sb) * 0.02;
+ if (reserved_threshold < reserved_blocks) {
+ LWARNING << "Reserved blocks " << reserved_blocks
+ << " is too large";
+ reserved_blocks = reserved_threshold;
+ }
+
+ if (ext4_r_blocks_count(&sb) == reserved_blocks) {
+ LINFO << "Have reserved same blocks";
+ return;
+ }
+ } else {
+ PERROR << "Failed to open '" << blk_device << "'";
+ return;
+ }
+
+ char buf[16] = {0};
+ snprintf(buf, sizeof (buf), "-r %lu", reserved_blocks);
+ const char *tune2fs_argv[] = {
+ TUNE2FS_BIN,
+ buf,
+ blk_device,
+ };
+
+ ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
+ const_cast<char **>(tune2fs_argv),
+ &status, true, LOG_KLOG | LOG_FILE,
+ true, NULL, NULL, 0);
+
+ if (ret < 0) {
+ /* No need to check for error in fork, we can't really handle it now */
+ LERROR << "Failed trying to run " << TUNE2FS_BIN;
+ }
+ }
+ }
+}
+
+static void remove_trailing_slashes(char *n)
+{
+ int len;
+
+ len = strlen(n) - 1;
+ while ((*(n + len) == '/') && len) {
+ *(n + len) = '\0';
+ len--;
+ }
+}
+
+/*
+ * Mark the given block device as read-only, using the BLKROSET ioctl.
+ * Return 0 on success, and -1 on error.
+ */
+int fs_mgr_set_blk_ro(const char *blockdev)
+{
+ int fd;
+ int rc = -1;
+ int ON = 1;
+
+ fd = TEMP_FAILURE_RETRY(open(blockdev, O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ // should never happen
+ return rc;
+ }
+
+ rc = ioctl(fd, BLKROSET, &ON);
+ close(fd);
+
+ return rc;
+}
+
+/*
+ * __mount(): wrapper around the mount() system call which also
+ * sets the underlying block device to read-only if the mount is read-only.
+ * See "man 2 mount" for return values.
+ */
+static int __mount(const char *source, const char *target, const struct fstab_rec *rec)
+{
+ unsigned long mountflags = rec->flags;
+ int ret;
+ int save_errno;
+
+ /* We need this because sometimes we have legacy symlinks
+ * that are lingering around and need cleaning up.
+ */
+ struct stat info;
+ if (!lstat(target, &info))
+ if ((info.st_mode & S_IFMT) == S_IFLNK)
+ unlink(target);
+ mkdir(target, 0755);
+ ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
+ save_errno = errno;
+ LINFO << __FUNCTION__ << "(source=" << source << ",target="
+ << target << ",type=" << rec->fs_type << ")=" << ret;
+ if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
+ fs_mgr_set_blk_ro(source);
+ }
+ errno = save_errno;
+ return ret;
+}
+
+static int fs_match(const char *in1, const char *in2)
+{
+ char *n1;
+ char *n2;
+ int ret;
+
+ n1 = strdup(in1);
+ n2 = strdup(in2);
+
+ remove_trailing_slashes(n1);
+ remove_trailing_slashes(n2);
+
+ ret = !strcmp(n1, n2);
+
+ free(n1);
+ free(n2);
+
+ return ret;
+}
+
+static int device_is_secure() {
+ int ret = -1;
+ char value[PROP_VALUE_MAX];
+ ret = __system_property_get("ro.secure", value);
+ /* If error, we want to fail secure */
+ if (ret < 0)
+ return 1;
+ return strcmp(value, "0") ? 1 : 0;
+}
+
+static int device_is_force_encrypted() {
+ int ret = -1;
+ char value[PROP_VALUE_MAX];
+ ret = __system_property_get("ro.vold.forceencryption", value);
+ if (ret < 0)
+ return 0;
+ return strcmp(value, "1") ? 0 : 1;
+}
+
+/*
+ * Tries to mount any of the consecutive fstab entries that match
+ * the mountpoint of the one given by fstab->recs[start_idx].
+ *
+ * end_idx: On return, will be the last rec that was looked at.
+ * attempted_idx: On return, will indicate which fstab rec
+ * succeeded. In case of failure, it will be the start_idx.
+ * Returns
+ * -1 on failure with errno set to match the 1st mount failure.
+ * 0 on success.
+ */
+static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
+{
+ int i;
+ int mount_errno = 0;
+ int mounted = 0;
+
+ if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
+ errno = EINVAL;
+ if (end_idx) *end_idx = start_idx;
+ if (attempted_idx) *attempted_idx = start_idx;
+ return -1;
+ }
+
+ /* Hunt down an fstab entry for the same mount point that might succeed */
+ for (i = start_idx;
+ /* We required that fstab entries for the same mountpoint be consecutive */
+ i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point);
+ i++) {
+ /*
+ * Don't try to mount/encrypt the same mount point again.
+ * Deal with alternate entries for the same point which are required to be all following
+ * each other.
+ */
+ if (mounted) {
+ LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint="
+ << fstab->recs[i].mount_point << " rec[" << i
+ << "].fs_type=" << fstab->recs[i].fs_type
+ << " already mounted as "
+ << fstab->recs[*attempted_idx].fs_type;
+ continue;
+ }
+
+ int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ &fstab->recs[i]);
+
+ if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
+ check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ fstab->recs[i].mount_point);
+ }
+
+ if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
+ do_reserved_size(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ &fstab->recs[i]);
+ }
+
+ if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
+ *attempted_idx = i;
+ mounted = 1;
+ if (i != start_idx) {
+ LERROR << __FUNCTION__ << "(): Mounted "
+ << fstab->recs[i].blk_device << " on "
+ << fstab->recs[i].mount_point << " with fs_type="
+ << fstab->recs[i].fs_type << " instead of "
+ << fstab->recs[start_idx].fs_type;
+ }
+ } else {
+ /* back up errno for crypto decisions */
+ mount_errno = errno;
+ }
+ }
+
+ /* Adjust i for the case where it was still withing the recs[] */
+ if (i < fstab->num_entries) --i;
+
+ *end_idx = i;
+ if (!mounted) {
+ *attempted_idx = start_idx;
+ errno = mount_errno;
+ return -1;
+ }
+ return 0;
+}
+
+static int translate_ext_labels(struct fstab_rec *rec)
+{
+ DIR *blockdir = NULL;
+ struct dirent *ent;
+ char *label;
+ size_t label_len;
+ int ret = -1;
+
+ if (strncmp(rec->blk_device, "LABEL=", 6))
+ return 0;
+
+ label = rec->blk_device + 6;
+ label_len = strlen(label);
+
+ if (label_len > 16) {
+ LERROR << "FS label is longer than allowed by filesystem";
+ goto out;
+ }
+
+
+ blockdir = opendir("/dev/block");
+ if (!blockdir) {
+ LERROR << "couldn't open /dev/block";
+ goto out;
+ }
+
+ while ((ent = readdir(blockdir))) {
+ int fd;
+ char super_buf[1024];
+ struct ext4_super_block *sb;
+
+ if (ent->d_type != DT_BLK)
+ continue;
+
+ fd = openat(dirfd(blockdir), ent->d_name, O_RDONLY);
+ if (fd < 0) {
+ LERROR << "Cannot open block device /dev/block/" << ent->d_name;
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(lseek(fd, 1024, SEEK_SET)) < 0 ||
+ TEMP_FAILURE_RETRY(read(fd, super_buf, 1024)) != 1024) {
+ /* Probably a loopback device or something else without a readable
+ * superblock.
+ */
+ close(fd);
+ continue;
+ }
+
+ sb = (struct ext4_super_block *)super_buf;
+ if (sb->s_magic != EXT4_SUPER_MAGIC) {
+ LINFO << "/dev/block/" << ent->d_name << " not ext{234}";
+ continue;
+ }
+
+ if (!strncmp(label, sb->s_volume_name, label_len)) {
+ char *new_blk_device;
+
+ if (asprintf(&new_blk_device, "/dev/block/%s", ent->d_name) < 0) {
+ LERROR << "Could not allocate block device string";
+ goto out;
+ }
+
+ LINFO << "resolved label " << rec->blk_device << " to "
+ << new_blk_device;
+
+ free(rec->blk_device);
+ rec->blk_device = new_blk_device;
+ ret = 0;
+ break;
+ }
+ }
+
+out:
+ closedir(blockdir);
+ return ret;
+}
+
+static bool needs_block_encryption(const struct fstab_rec* rec)
+{
+ if (device_is_force_encrypted() && fs_mgr_is_encryptable(rec)) return true;
+ if (rec->fs_mgr_flags & MF_FORCECRYPT) return true;
+ if (rec->fs_mgr_flags & MF_CRYPT) {
+ /* Check for existence of convert_fde breadcrumb file */
+ char convert_fde_name[PATH_MAX];
+ snprintf(convert_fde_name, sizeof(convert_fde_name),
+ "%s/misc/vold/convert_fde", rec->mount_point);
+ if (access(convert_fde_name, F_OK) == 0) return true;
+ }
+ if (rec->fs_mgr_flags & MF_FORCEFDEORFBE) {
+ /* Check for absence of convert_fbe breadcrumb file */
+ char convert_fbe_name[PATH_MAX];
+ snprintf(convert_fbe_name, sizeof(convert_fbe_name),
+ "%s/convert_fbe", rec->mount_point);
+ if (access(convert_fbe_name, F_OK) != 0) return true;
+ }
+ return false;
+}
+
+// Check to see if a mountable volume has encryption requirements
+static int handle_encryptable(const struct fstab_rec* rec)
+{
+ /* If this is block encryptable, need to trigger encryption */
+ if (needs_block_encryption(rec)) {
+ if (umount(rec->mount_point) == 0) {
+ return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
+ } else {
+ PWARNING << "Could not umount " << rec->mount_point
+ << " - allow continue unencrypted";
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
+ }
+ } else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
+ // Deal with file level encryption
+ LINFO << rec->mount_point << " is file encrypted";
+ return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
+ } else if (fs_mgr_is_encryptable(rec)) {
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
+ } else {
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
+ }
+}
+
+int fs_mgr_test_access(const char *device) {
+ int tries = 25;
+ while (tries--) {
+ if (!access(device, F_OK) || errno != ENOENT) {
+ return 0;
+ }
+ usleep(40 * 1000);
+ }
+ return -1;
+}
+
+/* When multiple fstab records share the same mount_point, it will
+ * try to mount each one in turn, and ignore any duplicates after a
+ * first successful mount.
+ * Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
+ */
+int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
+{
+ int i = 0;
+ int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
+ int error_count = 0;
+ int mret = -1;
+ int mount_errno = 0;
+ int attempted_idx = -1;
+ int avb_ret = FS_MGR_SETUP_AVB_FAIL;
+
+ if (!fstab) {
+ return -1;
+ }
+
+ if (fs_mgr_is_avb_used() &&
+ (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
+ return -1;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ /* Don't mount entries that are managed by vold or not for the mount mode*/
+ if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
+ ((mount_mode == MOUNT_MODE_LATE) && !fs_mgr_is_latemount(&fstab->recs[i])) ||
+ ((mount_mode == MOUNT_MODE_EARLY) && fs_mgr_is_latemount(&fstab->recs[i]))) {
+ continue;
+ }
+
+ /* Skip swap and raw partition entries such as boot, recovery, etc */
+ if (!strcmp(fstab->recs[i].fs_type, "swap") ||
+ !strcmp(fstab->recs[i].fs_type, "emmc") ||
+ !strcmp(fstab->recs[i].fs_type, "mtd")) {
+ continue;
+ }
+
+ /* Skip mounting the root partition, as it will already have been mounted */
+ if (!strcmp(fstab->recs[i].mount_point, "/")) {
+ if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) {
+ fs_mgr_set_blk_ro(fstab->recs[i].blk_device);
+ }
+ continue;
+ }
+
+ /* Translate LABEL= file system labels into block devices */
+ if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
+ !strcmp(fstab->recs[i].fs_type, "ext3") ||
+ !strcmp(fstab->recs[i].fs_type, "ext4")) {
+ int tret = translate_ext_labels(&fstab->recs[i]);
+ if (tret < 0) {
+ LERROR << "Could not translate label to block device";
+ continue;
+ }
+ }
+
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
+ wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ }
+
+ if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
+ /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
+ * should set up the device without using dm-verity.
+ * The actual mounting still take place in the following
+ * mount_with_alternatives().
+ */
+ if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
+ LINFO << "AVB HASHTREE disabled";
+ } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
+ FS_MGR_SETUP_AVB_SUCCESS) {
+ LERROR << "Failed to set up AVB on partition: "
+ << fstab->recs[i].mount_point << ", skipping!";
+ /* Skips mounting the device. */
+ continue;
+ }
+ } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
+ int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
+ if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+ LINFO << "Verity disabled";
+ } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
+ LERROR << "Could not set up verified partition, skipping!";
+ continue;
+ }
+ }
+
+ int last_idx_inspected;
+ int top_idx = i;
+
+ mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
+ i = last_idx_inspected;
+ mount_errno = errno;
+
+ /* Deal with encryptability. */
+ if (!mret) {
+ int status = handle_encryptable(&fstab->recs[attempted_idx]);
+
+ if (status == FS_MGR_MNTALL_FAIL) {
+ /* Fatal error - no point continuing */
+ return status;
+ }
+
+ if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
+ if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
+ // Log and continue
+ LERROR << "Only one encryptable/encrypted partition supported";
+ }
+ encryptable = status;
+ }
+
+ /* Success! Go get the next one */
+ continue;
+ }
+
+ /* mount(2) returned an error, handle the encryptable/formattable case */
+ bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
+ bool crypt_footer = false;
+ if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
+ fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {
+ /* top_idx and attempted_idx point at the same partition, but sometimes
+ * at two different lines in the fstab. Use the top one for formatting
+ * as that is the preferred one.
+ */
+ LERROR << __FUNCTION__ << "(): " << fstab->recs[top_idx].blk_device
+ << " is wiped and " << fstab->recs[top_idx].mount_point
+ << " " << fstab->recs[top_idx].fs_type
+ << " is formattable. Format it.";
+ if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
+ strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+ int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
+ if (fd >= 0) {
+ LINFO << __FUNCTION__ << "(): also wipe "
+ << fstab->recs[top_idx].key_loc;
+ wipe_block_device(fd, get_file_size(fd));
+ close(fd);
+ } else {
+ PERROR << __FUNCTION__ << "(): "
+ << fstab->recs[top_idx].key_loc << " wouldn't open";
+ }
+ } else if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
+ !strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+ crypt_footer = true;
+ }
+ if (fs_mgr_do_format(&fstab->recs[top_idx], crypt_footer) == 0) {
+ /* Let's replay the mount actions. */
+ i = top_idx - 1;
+ continue;
+ } else {
+ LERROR << __FUNCTION__ << "(): Format failed. "
+ << "Suggest recovery...";
+ encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
+ continue;
+ }
+ }
+ if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
+ fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
+ if (wiped) {
+ LERROR << __FUNCTION__ << "(): "
+ << fstab->recs[attempted_idx].blk_device
+ << " is wiped and "
+ << fstab->recs[attempted_idx].mount_point << " "
+ << fstab->recs[attempted_idx].fs_type
+ << " is encryptable. Suggest recovery...";
+ encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
+ continue;
+ } else {
+ /* Need to mount a tmpfs at this mountpoint for now, and set
+ * properties that vold will query later for decrypting
+ */
+ LERROR << __FUNCTION__ << "(): possibly an encryptable blkdev "
+ << fstab->recs[attempted_idx].blk_device
+ << " for mount " << fstab->recs[attempted_idx].mount_point
+ << " type " << fstab->recs[attempted_idx].fs_type;
+ if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
+ ++error_count;
+ continue;
+ }
+ }
+ encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
+ } else {
+ if (fs_mgr_is_nofail(&fstab->recs[attempted_idx])) {
+ PERROR << "Ignoring failure to mount an un-encryptable or wiped partition on"
+ << fstab->recs[attempted_idx].blk_device << " at "
+ << fstab->recs[attempted_idx].mount_point << " options: "
+ << fstab->recs[attempted_idx].fs_options;
+ } else {
+ PERROR << "Failed to mount an un-encryptable or wiped partition on"
+ << fstab->recs[attempted_idx].blk_device << " at "
+ << fstab->recs[attempted_idx].mount_point << " options: "
+ << fstab->recs[attempted_idx].fs_options;
+ ++error_count;
+ }
+ continue;
+ }
+ }
+
+ if (fs_mgr_is_avb_used()) {
+ fs_mgr_unload_vbmeta_images();
+ }
+
+ if (error_count) {
+ return -1;
+ } else {
+ return encryptable;
+ }
+}
+
+/* If tmp_mount_point is non-null, mount the filesystem there. This is for the
+ * tmp mount we do to check the user password
+ * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
+ * in turn, and stop on 1st success, or no more match.
+ */
+int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
+ char *tmp_mount_point)
+{
+ int i = 0;
+ int ret = FS_MGR_DOMNT_FAILED;
+ int mount_errors = 0;
+ int first_mount_errno = 0;
+ char *m;
+ int avb_ret = FS_MGR_SETUP_AVB_FAIL;
+
+ if (!fstab) {
+ return ret;
+ }
+
+ if (fs_mgr_is_avb_used() &&
+ (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
+ return ret;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ if (!fs_match(fstab->recs[i].mount_point, n_name)) {
+ continue;
+ }
+
+ /* We found our match */
+ /* If this swap or a raw partition, report an error */
+ if (!strcmp(fstab->recs[i].fs_type, "swap") ||
+ !strcmp(fstab->recs[i].fs_type, "emmc") ||
+ !strcmp(fstab->recs[i].fs_type, "mtd")) {
+ LERROR << "Cannot mount filesystem of type "
+ << fstab->recs[i].fs_type << " on " << n_blk_device;
+ goto out;
+ }
+
+ /* First check the filesystem if requested */
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
+ wait_for_file(n_blk_device, WAIT_TIMEOUT);
+ }
+
+ int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ &fstab->recs[i]);
+
+ if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
+ check_fs(n_blk_device, fstab->recs[i].fs_type,
+ fstab->recs[i].mount_point);
+ }
+
+ if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
+ do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i]);
+ }
+
+ if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
+ /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
+ * should set up the device without using dm-verity.
+ * The actual mounting still take place in the following
+ * mount_with_alternatives().
+ */
+ if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
+ LINFO << "AVB HASHTREE disabled";
+ } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
+ FS_MGR_SETUP_AVB_SUCCESS) {
+ LERROR << "Failed to set up AVB on partition: "
+ << fstab->recs[i].mount_point << ", skipping!";
+ /* Skips mounting the device. */
+ continue;
+ }
+ } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
+ int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
+ if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+ LINFO << "Verity disabled";
+ } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
+ LERROR << "Could not set up verified partition, skipping!";
+ continue;
+ }
+ }
+
+ /* Now mount it where requested */
+ if (tmp_mount_point) {
+ m = tmp_mount_point;
+ } else {
+ m = fstab->recs[i].mount_point;
+ }
+ if (__mount(n_blk_device, m, &fstab->recs[i])) {
+ if (!first_mount_errno) first_mount_errno = errno;
+ mount_errors++;
+ continue;
+ } else {
+ ret = 0;
+ goto out;
+ }
+ }
+ if (mount_errors) {
+ PERROR << "Cannot mount filesystem on " << n_blk_device
+ << " at " << m;
+ if (first_mount_errno == EBUSY) {
+ ret = FS_MGR_DOMNT_BUSY;
+ } else {
+ ret = FS_MGR_DOMNT_FAILED;
+ }
+ } else {
+ /* We didn't find a match, say so and return an error */
+ LERROR << "Cannot find mount point " << fstab->recs[i].mount_point
+ << " in fstab";
+ }
+
+out:
+ if (fs_mgr_is_avb_used()) {
+ fs_mgr_unload_vbmeta_images();
+ }
+ return ret;
+}
+
+/*
+ * mount a tmpfs filesystem at the given point.
+ * return 0 on success, non-zero on failure.
+ */
+int fs_mgr_do_tmpfs_mount(char *n_name)
+{
+ int ret;
+
+ ret = mount("tmpfs", n_name, "tmpfs",
+ MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
+ if (ret < 0) {
+ LERROR << "Cannot mount tmpfs filesystem at " << n_name;
+ return -1;
+ }
+
+ /* Success */
+ return 0;
+}
+
+int fs_mgr_unmount_all(struct fstab *fstab)
+{
+ int i = 0;
+ int ret = 0;
+
+ if (!fstab) {
+ return -1;
+ }
+
+ while (fstab->recs[i].blk_device) {
+ if (umount(fstab->recs[i].mount_point)) {
+ LERROR << "Cannot unmount filesystem at "
+ << fstab->recs[i].mount_point;
+ ret = -1;
+ }
+ i++;
+ }
+
+ return ret;
+}
+
+/* This must be called after mount_all, because the mkswap command needs to be
+ * available.
+ */
+int fs_mgr_swapon_all(struct fstab *fstab)
+{
+ int i = 0;
+ int flags = 0;
+ int err = 0;
+ int ret = 0;
+ int status;
+ const char *mkswap_argv[2] = {
+ MKSWAP_BIN,
+ nullptr
+ };
+
+ if (!fstab) {
+ return -1;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ /* Skip non-swap entries */
+ if (strcmp(fstab->recs[i].fs_type, "swap")) {
+ continue;
+ }
+
+ if (fstab->recs[i].zram_size > 0) {
+ /* A zram_size was specified, so we need to configure the
+ * device. There is no point in having multiple zram devices
+ * on a system (all the memory comes from the same pool) so
+ * we can assume the device number is 0.
+ */
+ FILE *zram_fp;
+ FILE *zram_mcs_fp;
+
+ if (fstab->recs[i].max_comp_streams >= 0) {
+ zram_mcs_fp = fopen(ZRAM_CONF_MCS, "r+");
+ if (zram_mcs_fp == NULL) {
+ LERROR << "Unable to open zram conf comp device "
+ << ZRAM_CONF_MCS;
+ ret = -1;
+ continue;
+ }
+ fprintf(zram_mcs_fp, "%d\n", fstab->recs[i].max_comp_streams);
+ fclose(zram_mcs_fp);
+ }
+
+ zram_fp = fopen(ZRAM_CONF_DEV, "r+");
+ if (zram_fp == NULL) {
+ LERROR << "Unable to open zram conf device " << ZRAM_CONF_DEV;
+ ret = -1;
+ continue;
+ }
+ fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size);
+ fclose(zram_fp);
+ }
+
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
+ wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ }
+
+ /* Initialize the swap area */
+ mkswap_argv[1] = fstab->recs[i].blk_device;
+ err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv),
+ const_cast<char **>(mkswap_argv),
+ &status, true, LOG_KLOG, false, NULL,
+ NULL, 0);
+ if (err) {
+ LERROR << "mkswap failed for " << fstab->recs[i].blk_device;
+ ret = -1;
+ continue;
+ }
+
+ /* If -1, then no priority was specified in fstab, so don't set
+ * SWAP_FLAG_PREFER or encode the priority */
+ if (fstab->recs[i].swap_prio >= 0) {
+ flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) &
+ SWAP_FLAG_PRIO_MASK;
+ flags |= SWAP_FLAG_PREFER;
+ } else {
+ flags = 0;
+ }
+ err = swapon(fstab->recs[i].blk_device, flags);
+ if (err) {
+ LERROR << "swapon failed for " << fstab->recs[i].blk_device;
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * key_loc must be at least PROPERTY_VALUE_MAX bytes long
+ *
+ * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
+ */
+int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size)
+{
+ int i = 0;
+
+ if (!fstab) {
+ return -1;
+ }
+ /* Initialize return values to null strings */
+ if (key_loc) {
+ *key_loc = '\0';
+ }
+ if (real_blk_device) {
+ *real_blk_device = '\0';
+ }
+
+ /* Look for the encryptable partition to find the data */
+ for (i = 0; i < fstab->num_entries; i++) {
+ /* Don't deal with vold managed enryptable partitions here */
+ if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
+ continue;
+ }
+ if (!(fstab->recs[i].fs_mgr_flags
+ & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE))) {
+ continue;
+ }
+
+ /* We found a match */
+ if (key_loc) {
+ strlcpy(key_loc, fstab->recs[i].key_loc, size);
+ }
+ if (real_blk_device) {
+ strlcpy(real_blk_device, fstab->recs[i].blk_device, size);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int fs_mgr_early_setup_verity(struct fstab_rec *fstab_rec)
+{
+ if ((fstab_rec->fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
+ int rc = fs_mgr_setup_verity(fstab_rec, false);
+ if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+ LINFO << "Verity disabled";
+ return FS_MGR_EARLY_SETUP_VERITY_NO_VERITY;
+ } else if (rc == FS_MGR_SETUP_VERITY_SUCCESS) {
+ return FS_MGR_EARLY_SETUP_VERITY_SUCCESS;
+ } else {
+ return FS_MGR_EARLY_SETUP_VERITY_FAIL;
+ }
+ } else if (device_is_secure()) {
+ LERROR << "Verity must be enabled for early mounted partitions on secured devices";
+ return FS_MGR_EARLY_SETUP_VERITY_FAIL;
+ }
+ return FS_MGR_EARLY_SETUP_VERITY_NO_VERITY;
+}
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
new file mode 100644
index 0000000..70140d8
--- /dev/null
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/properties.h>
+#include <libavb/libavb.h>
+#include <openssl/sha.h>
+#include <sys/ioctl.h>
+#include <utils/Compat.h>
+
+#include "fs_mgr.h"
+#include "fs_mgr_avb_ops.h"
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_avb.h"
+#include "fs_mgr_priv_dm_ioctl.h"
+#include "fs_mgr_priv_sha.h"
+
+/* The format of dm-verity construction parameters:
+ * <version> <dev> <hash_dev> <data_block_size> <hash_block_size>
+ * <num_data_blocks> <hash_start_block> <algorithm> <digest> <salt>
+ */
+#define VERITY_TABLE_FORMAT \
+ "%u %s %s %u %u " \
+ "%" PRIu64 " %" PRIu64 " %s %s %s "
+
+#define VERITY_TABLE_PARAMS(hashtree_desc, blk_device, digest, salt) \
+ hashtree_desc.dm_verity_version, blk_device, blk_device, \
+ hashtree_desc.data_block_size, hashtree_desc.hash_block_size, \
+ hashtree_desc.image_size / \
+ hashtree_desc.data_block_size, /* num_data_blocks. */ \
+ hashtree_desc.tree_offset / \
+ hashtree_desc.hash_block_size, /* hash_start_block. */ \
+ (char *)hashtree_desc.hash_algorithm, digest, salt
+
+#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
+#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
+
+/* The default format of dm-verity optional parameters:
+ * <#opt_params> ignore_zero_blocks restart_on_corruption
+ */
+#define VERITY_TABLE_OPT_DEFAULT_FORMAT "2 %s %s"
+#define VERITY_TABLE_OPT_DEFAULT_PARAMS \
+ VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
+
+/* The FEC (forward error correction) format of dm-verity optional parameters:
+ * <#opt_params> use_fec_from_device <fec_dev>
+ * fec_roots <num> fec_blocks <num> fec_start <offset>
+ * ignore_zero_blocks restart_on_corruption
+ */
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+ "10 use_fec_from_device %s fec_roots %u fec_blocks %" PRIu64 \
+ " fec_start %" PRIu64 " %s %s"
+
+/* Note that fec_blocks is the size that FEC covers, *not* the
+ * size of the FEC data. Since we use FEC for everything up until
+ * the FEC data, it's the same as the offset (fec_start).
+ */
+#define VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device) \
+ blk_device, hashtree_desc.fec_num_roots, \
+ hashtree_desc.fec_offset / \
+ hashtree_desc.data_block_size, /* fec_blocks */ \
+ hashtree_desc.fec_offset / \
+ hashtree_desc.data_block_size, /* fec_start */ \
+ VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
+
+AvbSlotVerifyData *fs_mgr_avb_verify_data = nullptr;
+AvbOps *fs_mgr_avb_ops = nullptr;
+
+enum HashAlgorithm {
+ kInvalid = 0,
+ kSHA256 = 1,
+ kSHA512 = 2,
+};
+
+struct androidboot_vbmeta {
+ HashAlgorithm hash_alg;
+ uint8_t digest[SHA512_DIGEST_LENGTH];
+ size_t vbmeta_size;
+ bool allow_verification_error;
+};
+
+androidboot_vbmeta fs_mgr_vbmeta_prop;
+
+static inline bool nibble_value(const char &c, uint8_t *value)
+{
+ FS_MGR_CHECK(value != nullptr);
+
+ switch (c) {
+ case '0' ... '9':
+ *value = c - '0';
+ break;
+ case 'a' ... 'f':
+ *value = c - 'a' + 10;
+ break;
+ case 'A' ... 'F':
+ *value = c - 'A' + 10;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool hex_to_bytes(uint8_t *bytes,
+ size_t bytes_len,
+ const std::string &hex)
+{
+ FS_MGR_CHECK(bytes != nullptr);
+
+ if (hex.size() % 2 != 0) {
+ return false;
+ }
+ if (hex.size() / 2 > bytes_len) {
+ return false;
+ }
+ for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {
+ uint8_t high;
+ if (!nibble_value(hex[i], &high)) {
+ return false;
+ }
+ uint8_t low;
+ if (!nibble_value(hex[i + 1], &low)) {
+ return false;
+ }
+ bytes[j] = (high << 4) | low;
+ }
+ return true;
+}
+
+static std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len)
+{
+ FS_MGR_CHECK(bytes != nullptr);
+
+ static const char *hex_digits = "0123456789abcdef";
+ std::string hex;
+
+ for (size_t i = 0; i < bytes_len; i++) {
+ hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);
+ hex.push_back(hex_digits[bytes[i] & 0x0F]);
+ }
+ return hex;
+}
+
+static bool load_vbmeta_prop(androidboot_vbmeta *vbmeta_prop)
+{
+ FS_MGR_CHECK(vbmeta_prop != nullptr);
+
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+
+ std::string hash_alg;
+ std::string digest;
+
+ for (const auto &entry :
+ android::base::Split(android::base::Trim(cmdline), " ")) {
+ std::vector<std::string> pieces = android::base::Split(entry, "=");
+ const std::string &key = pieces[0];
+ const std::string &value = pieces[1];
+
+ if (key == "androidboot.vbmeta.device_state") {
+ vbmeta_prop->allow_verification_error = (value == "unlocked");
+ } else if (key == "androidboot.vbmeta.hash_alg") {
+ hash_alg = value;
+ } else if (key == "androidboot.vbmeta.size") {
+ if (!android::base::ParseUint(value.c_str(),
+ &vbmeta_prop->vbmeta_size)) {
+ return false;
+ }
+ } else if (key == "androidboot.vbmeta.digest") {
+ digest = value;
+ }
+ }
+
+ // Reads hash algorithm.
+ size_t expected_digest_size = 0;
+ if (hash_alg == "sha256") {
+ expected_digest_size = SHA256_DIGEST_LENGTH * 2;
+ vbmeta_prop->hash_alg = kSHA256;
+ } else if (hash_alg == "sha512") {
+ expected_digest_size = SHA512_DIGEST_LENGTH * 2;
+ vbmeta_prop->hash_alg = kSHA512;
+ } else {
+ LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
+ return false;
+ }
+
+ // Reads digest.
+ if (digest.size() != expected_digest_size) {
+ LERROR << "Unexpected digest size: " << digest.size() << " (expected: "
+ << expected_digest_size << ")";
+ return false;
+ }
+
+ if (!hex_to_bytes(vbmeta_prop->digest, sizeof(vbmeta_prop->digest),
+ digest)) {
+ LERROR << "Hash digest contains non-hexidecimal character: "
+ << digest.c_str();
+ return false;
+ }
+
+ return true;
+}
+
+template <typename Hasher>
+static std::pair<size_t, bool> verify_vbmeta_digest(
+ const AvbSlotVerifyData &verify_data, const androidboot_vbmeta &vbmeta_prop)
+{
+ size_t total_size = 0;
+ Hasher hasher;
+ for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
+ hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
+ verify_data.vbmeta_images[n].vbmeta_size);
+ total_size += verify_data.vbmeta_images[n].vbmeta_size;
+ }
+
+ bool matched = (memcmp(hasher.finalize(), vbmeta_prop.digest,
+ Hasher::DIGEST_SIZE) == 0);
+
+ return std::make_pair(total_size, matched);
+}
+
+static bool verify_vbmeta_images(const AvbSlotVerifyData &verify_data,
+ const androidboot_vbmeta &vbmeta_prop)
+{
+ if (verify_data.num_vbmeta_images == 0) {
+ LERROR << "No vbmeta images";
+ return false;
+ }
+
+ size_t total_size = 0;
+ bool digest_matched = false;
+
+ if (vbmeta_prop.hash_alg == kSHA256) {
+ std::tie(total_size, digest_matched) =
+ verify_vbmeta_digest<SHA256Hasher>(verify_data, vbmeta_prop);
+ } else if (vbmeta_prop.hash_alg == kSHA512) {
+ std::tie(total_size, digest_matched) =
+ verify_vbmeta_digest<SHA512Hasher>(verify_data, vbmeta_prop);
+ }
+
+ if (total_size != vbmeta_prop.vbmeta_size) {
+ LERROR << "total vbmeta size mismatch: " << total_size
+ << " (expected: " << vbmeta_prop.vbmeta_size << ")";
+ return false;
+ }
+
+ if (!digest_matched) {
+ LERROR << "vbmeta digest mismatch";
+ return false;
+ }
+
+ return true;
+}
+
+static bool hashtree_load_verity_table(
+ struct dm_ioctl *io,
+ const std::string &dm_device_name,
+ int fd,
+ const std::string &blk_device,
+ const AvbHashtreeDescriptor &hashtree_desc,
+ const std::string &salt,
+ const std::string &root_digest)
+{
+ fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG);
+
+ // The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
+ char *buffer = (char *)io;
+
+ // Builds the dm_target_spec arguments.
+ struct dm_target_spec *dm_target =
+ (struct dm_target_spec *)&buffer[sizeof(struct dm_ioctl)];
+ io->target_count = 1;
+ dm_target->status = 0;
+ dm_target->sector_start = 0;
+ dm_target->length = hashtree_desc.image_size / 512;
+ strcpy(dm_target->target_type, "verity");
+
+ // Builds the verity params.
+ char *verity_params =
+ buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+ size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
+
+ int res = 0;
+ if (hashtree_desc.fec_size > 0) {
+ res = snprintf(
+ verity_params, bufsize,
+ VERITY_TABLE_FORMAT VERITY_TABLE_OPT_FEC_FORMAT,
+ VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(),
+ root_digest.c_str(), salt.c_str()),
+ VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device.c_str()));
+ } else {
+ res = snprintf(verity_params, bufsize,
+ VERITY_TABLE_FORMAT VERITY_TABLE_OPT_DEFAULT_FORMAT,
+ VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(),
+ root_digest.c_str(), salt.c_str()),
+ VERITY_TABLE_OPT_DEFAULT_PARAMS);
+ }
+
+ if (res < 0 || (size_t)res >= bufsize) {
+ LERROR << "Error building verity table; insufficient buffer size?";
+ return false;
+ }
+
+ LINFO << "Loading verity table: '" << verity_params << "'";
+
+ // Sets ext target boundary.
+ verity_params += strlen(verity_params) + 1;
+ verity_params = (char *)(((unsigned long)verity_params + 7) & ~7);
+ dm_target->next = verity_params - buffer;
+
+ // Sends the ioctl to load the verity table.
+ if (ioctl(fd, DM_TABLE_LOAD, io)) {
+ PERROR << "Error loading verity table";
+ return false;
+ }
+
+ return true;
+}
+
+static bool hashtree_dm_verity_setup(struct fstab_rec *fstab_entry,
+ const AvbHashtreeDescriptor &hashtree_desc,
+ const std::string &salt,
+ const std::string &root_digest)
+{
+ // Gets the device mapper fd.
+ android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
+ if (fd < 0) {
+ PERROR << "Error opening device mapper";
+ return false;
+ }
+
+ // Creates the device.
+ 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)) {
+ 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)) {
+ LERROR << "Couldn't get verity device number!";
+ return false;
+ }
+
+ // Loads the verity mapping table.
+ if (!hashtree_load_verity_table(io, mount_point, fd,
+ std::string(fstab_entry->blk_device),
+ hashtree_desc, salt, root_digest)) {
+ LERROR << "Couldn't load verity table!";
+ return false;
+ }
+
+ // Activates the device.
+ if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+ return false;
+ }
+
+ // Marks the underlying block device as read-only.
+ fs_mgr_set_blk_ro(fstab_entry->blk_device);
+
+ // TODO(bowgotsai): support verified all partition at boot.
+ // Updates fstab_rec->blk_device to verity device name.
+ free(fstab_entry->blk_device);
+ fstab_entry->blk_device = strdup(verity_blk_name.c_str());
+
+ // Makes sure we've set everything up properly.
+ if (fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool get_hashtree_descriptor(const std::string &partition_name,
+ const AvbSlotVerifyData &verify_data,
+ AvbHashtreeDescriptor *out_hashtree_desc,
+ std::string *out_salt,
+ std::string *out_digest)
+{
+ bool found = false;
+ const uint8_t *desc_partition_name;
+
+ for (size_t i = 0; i < verify_data.num_vbmeta_images && !found; i++) {
+ // Get descriptors from vbmeta_images[i].
+ size_t num_descriptors;
+ std::unique_ptr<const AvbDescriptor *[], decltype(&avb_free)>
+ descriptors(
+ avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
+ verify_data.vbmeta_images[i].vbmeta_size,
+ &num_descriptors),
+ avb_free);
+
+ if (!descriptors || num_descriptors < 1) {
+ continue;
+ }
+
+ // Ensures that hashtree descriptor is either in /vbmeta or in
+ // the same partition for verity setup.
+ std::string vbmeta_partition_name(
+ verify_data.vbmeta_images[i].partition_name);
+ if (vbmeta_partition_name != "vbmeta" &&
+ vbmeta_partition_name != partition_name) {
+ LWARNING << "Skip vbmeta image at "
+ << verify_data.vbmeta_images[i].partition_name
+ << " for partition: " << partition_name.c_str();
+ continue;
+ }
+
+ for (size_t j = 0; j < num_descriptors && !found; j++) {
+ AvbDescriptor desc;
+ if (!avb_descriptor_validate_and_byteswap(descriptors[j], &desc)) {
+ LWARNING << "Descriptor[" << j << "] is invalid";
+ continue;
+ }
+ if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
+ desc_partition_name = (const uint8_t *)descriptors[j] +
+ sizeof(AvbHashtreeDescriptor);
+ if (!avb_hashtree_descriptor_validate_and_byteswap(
+ (AvbHashtreeDescriptor *)descriptors[j],
+ out_hashtree_desc)) {
+ continue;
+ }
+ if (out_hashtree_desc->partition_name_len !=
+ partition_name.length()) {
+ continue;
+ }
+ // Notes that desc_partition_name is not NUL-terminated.
+ std::string hashtree_partition_name(
+ (const char *)desc_partition_name,
+ out_hashtree_desc->partition_name_len);
+ if (hashtree_partition_name == partition_name) {
+ found = true;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ LERROR << "Partition descriptor not found: " << partition_name.c_str();
+ return false;
+ }
+
+ const uint8_t *desc_salt =
+ desc_partition_name + out_hashtree_desc->partition_name_len;
+ *out_salt = bytes_to_hex(desc_salt, out_hashtree_desc->salt_len);
+
+ const uint8_t *desc_digest = desc_salt + out_hashtree_desc->salt_len;
+ *out_digest = bytes_to_hex(desc_digest, out_hashtree_desc->root_digest_len);
+
+ return true;
+}
+
+static inline bool polling_vbmeta_blk_device(struct fstab *fstab)
+{
+ // It needs the block device symlink: fstab_rec->blk_device to read
+ // /vbmeta partition. However, the symlink created by ueventd might
+ // not be ready at this point. Use test_access() to poll it before
+ // trying to read the partition.
+ struct fstab_rec *fstab_entry =
+ fs_mgr_get_entry_for_mount_point(fstab, "/vbmeta");
+
+ // Makes sure /vbmeta block device is ready to access.
+ if (fs_mgr_test_access(fstab_entry->blk_device) < 0) {
+ return false;
+ }
+ return true;
+}
+
+static bool init_is_avb_used()
+{
+ // When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg,
+ // size, digest} in kernel cmdline. They will then be imported by init
+ // process to system properties: ro.boot.vbmeta.{hash_alg, size, digest}.
+ //
+ // Checks hash_alg as an indicator for whether AVB is used.
+ // We don't have to parse and check all of them here. The check will
+ // be done in fs_mgr_load_vbmeta_images() and FS_MGR_SETUP_AVB_FAIL will
+ // be returned when there is an error.
+
+ std::string hash_alg =
+ android::base::GetProperty("ro.boot.vbmeta.hash_alg", "");
+
+ if (hash_alg == "sha256" || hash_alg == "sha512") {
+ return true;
+ }
+
+ return false;
+}
+
+bool fs_mgr_is_avb_used()
+{
+ static bool result = init_is_avb_used();
+ return result;
+}
+
+int fs_mgr_load_vbmeta_images(struct fstab *fstab)
+{
+ FS_MGR_CHECK(fstab != nullptr);
+
+ if (!polling_vbmeta_blk_device(fstab)) {
+ LERROR << "Failed to find block device of /vbmeta";
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ // Gets the expected hash value of vbmeta images from
+ // kernel cmdline.
+ if (!load_vbmeta_prop(&fs_mgr_vbmeta_prop)) {
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ fs_mgr_avb_ops = fs_mgr_dummy_avb_ops_new(fstab);
+ if (fs_mgr_avb_ops == nullptr) {
+ LERROR << "Failed to allocate dummy avb_ops";
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ // Invokes avb_slot_verify() to load and verify all vbmeta images.
+ // Sets requested_partitions to nullptr as it's to copy the contents
+ // of HASH partitions into fs_mgr_avb_verify_data, which is not required as
+ // fs_mgr only deals with HASHTREE partitions.
+ const char *requested_partitions[] = {nullptr};
+ const char *ab_suffix =
+ android::base::GetProperty("ro.boot.slot_suffix", "").c_str();
+ AvbSlotVerifyResult verify_result = avb_slot_verify(
+ fs_mgr_avb_ops, requested_partitions, ab_suffix,
+ fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
+
+ // Only allow two verify results:
+ // - AVB_SLOT_VERIFY_RESULT_OK.
+ // - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state).
+ if (verify_result == AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) {
+ if (!fs_mgr_vbmeta_prop.allow_verification_error) {
+ LERROR << "ERROR_VERIFICATION isn't allowed";
+ goto fail;
+ }
+ } else if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
+ LERROR << "avb_slot_verify failed, result: " << verify_result;
+ goto fail;
+ }
+
+ // Verifies vbmeta images against the digest passed from bootloader.
+ if (!verify_vbmeta_images(*fs_mgr_avb_verify_data, fs_mgr_vbmeta_prop)) {
+ LERROR << "verify_vbmeta_images failed";
+ goto fail;
+ } else {
+ // Checks whether FLAGS_HASHTREE_DISABLED is set.
+ AvbVBMetaImageHeader vbmeta_header;
+ avb_vbmeta_image_header_to_host_byte_order(
+ (AvbVBMetaImageHeader *)fs_mgr_avb_verify_data->vbmeta_images[0]
+ .vbmeta_data,
+ &vbmeta_header);
+
+ bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
+ AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ if (hashtree_disabled) {
+ return FS_MGR_SETUP_AVB_HASHTREE_DISABLED;
+ }
+ }
+
+ if (verify_result == AVB_SLOT_VERIFY_RESULT_OK) {
+ return FS_MGR_SETUP_AVB_SUCCESS;
+ }
+
+fail:
+ fs_mgr_unload_vbmeta_images();
+ return FS_MGR_SETUP_AVB_FAIL;
+}
+
+void fs_mgr_unload_vbmeta_images()
+{
+ if (fs_mgr_avb_verify_data != nullptr) {
+ avb_slot_verify_data_free(fs_mgr_avb_verify_data);
+ }
+
+ if (fs_mgr_avb_ops != nullptr) {
+ fs_mgr_dummy_avb_ops_free(fs_mgr_avb_ops);
+ }
+}
+
+int fs_mgr_setup_avb(struct fstab_rec *fstab_entry)
+{
+ if (!fstab_entry || !fs_mgr_avb_verify_data ||
+ fs_mgr_avb_verify_data->num_vbmeta_images < 1) {
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ std::string partition_name(basename(fstab_entry->mount_point));
+ if (!avb_validate_utf8((const uint8_t *)partition_name.c_str(),
+ partition_name.length())) {
+ LERROR << "Partition name: " << partition_name.c_str()
+ << " is not valid UTF-8.";
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ AvbHashtreeDescriptor hashtree_descriptor;
+ std::string salt;
+ std::string root_digest;
+ if (!get_hashtree_descriptor(partition_name, *fs_mgr_avb_verify_data,
+ &hashtree_descriptor, &salt, &root_digest)) {
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ // Converts HASHTREE descriptor to verity_table_params.
+ if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt,
+ root_digest)) {
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ return FS_MGR_SETUP_AVB_SUCCESS;
+}
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
new file mode 100644
index 0000000..7683166
--- /dev/null
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2016 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 <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <string>
+
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <libavb/libavb.h>
+#include <utils/Compat.h>
+
+#include "fs_mgr.h"
+#include "fs_mgr_avb_ops.h"
+#include "fs_mgr_priv.h"
+
+static struct fstab *fs_mgr_fstab = nullptr;
+
+static AvbIOResult read_from_partition(AvbOps *ops ATTRIBUTE_UNUSED,
+ const char *partition,
+ int64_t offset,
+ size_t num_bytes,
+ void *buffer,
+ size_t *out_num_read)
+{
+ // The input |partition| name is with ab_suffix, e.g. system_a.
+ // Slot suffix (e.g. _a) will be appended to the device file path
+ // for partitions having 'slotselect' optin in fstab file, but it
+ // won't be appended to the mount point.
+ //
+ // In AVB, we can assume that there's an entry for the /misc mount
+ // point and use that to get the device file for the misc partition.
+ // From there we'll assume that a by-name scheme is used
+ // so we can just replace the trailing "misc" by the given
+ // |partition|, e.g.
+ //
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
+
+ struct fstab_rec *fstab_entry =
+ fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
+
+ if (fstab_entry == nullptr) {
+ LERROR << "Partition (" << partition << ") not found in fstab";
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ std::string partition_name(partition);
+ std::string path(fstab_entry->blk_device);
+ // Replaces the last field of device file if it's not misc.
+ if (!android::base::StartsWith(partition_name, "misc")) {
+ size_t end_slash = path.find_last_of("/");
+ std::string by_name_prefix(path.substr(0, end_slash + 1));
+ path = by_name_prefix + partition_name;
+ }
+
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+
+ if (fd < 0) {
+ PERROR << "Failed to open " << path.c_str();
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ // If offset is negative, interprets its absolute value as the
+ // number of bytes from the end of the partition.
+ if (offset < 0) {
+ off64_t total_size = lseek64(fd, 0, SEEK_END);
+ if (total_size == -1) {
+ LERROR << "Failed to lseek64 to end of the partition";
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ offset = total_size + offset;
+ // Repositions the offset to the beginning.
+ if (lseek64(fd, 0, SEEK_SET) == -1) {
+ LERROR << "Failed to lseek64 to the beginning of the partition";
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ }
+
+ // On Linux, we never get partial reads from block devices (except
+ // for EOF).
+ ssize_t num_read =
+ TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
+
+ if (num_read < 0 || (size_t)num_read != num_bytes) {
+ PERROR << "Failed to read " << num_bytes << " bytes from "
+ << path.c_str() << " offset " << offset;
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ if (out_num_read != nullptr) {
+ *out_num_read = num_read;
+ }
+
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_read_rollback_index(AvbOps *ops ATTRIBUTE_UNUSED,
+ size_t rollback_index_location
+ ATTRIBUTE_UNUSED,
+ uint64_t *out_rollback_index)
+{
+ // rollback_index has been checked in bootloader phase.
+ // In user-space, returns the smallest value 0 to pass the check.
+ *out_rollback_index = 0;
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_validate_vbmeta_public_key(
+ AvbOps *ops ATTRIBUTE_UNUSED,
+ const uint8_t *public_key_data ATTRIBUTE_UNUSED,
+ size_t public_key_length ATTRIBUTE_UNUSED,
+ const uint8_t *public_key_metadata ATTRIBUTE_UNUSED,
+ size_t public_key_metadata_length ATTRIBUTE_UNUSED,
+ bool *out_is_trusted)
+{
+ // vbmeta public key has been checked in bootloader phase.
+ // In user-space, returns true to pass the check.
+ //
+ // Addtionally, user-space should check
+ // androidboot.vbmeta.{hash_alg, size, digest} against the digest
+ // of all vbmeta images after invoking avb_slot_verify().
+
+ *out_is_trusted = true;
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_read_is_device_unlocked(AvbOps *ops ATTRIBUTE_UNUSED,
+ bool *out_is_unlocked)
+{
+ // The function is for bootloader to update the value into
+ // androidboot.vbmeta.device_state in kernel cmdline.
+ // In user-space, returns true as we don't need to update it anymore.
+ *out_is_unlocked = true;
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_get_unique_guid_for_partition(
+ AvbOps *ops ATTRIBUTE_UNUSED,
+ const char *partition ATTRIBUTE_UNUSED,
+ char *guid_buf,
+ size_t guid_buf_size)
+{
+ // The function is for bootloader to set the correct UUID
+ // for a given partition in kernel cmdline.
+ // In user-space, returns a faking one as we don't need to update
+ // it anymore.
+ snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition);
+ return AVB_IO_RESULT_OK;
+}
+
+AvbOps *fs_mgr_dummy_avb_ops_new(struct fstab *fstab)
+{
+ AvbOps *ops;
+
+ // Assigns the fstab to the static variable for later use.
+ fs_mgr_fstab = fstab;
+
+ ops = (AvbOps *)calloc(1, sizeof(AvbOps));
+ if (ops == nullptr) {
+ LERROR << "Error allocating memory for AvbOps";
+ return nullptr;
+ }
+
+ // We only need these operations since that's all what is being used
+ // by the avb_slot_verify(); Most of them are dummy operations because
+ // they're only required in bootloader but not required in user-space.
+ ops->read_from_partition = read_from_partition;
+ ops->read_rollback_index = dummy_read_rollback_index;
+ ops->validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
+ ops->read_is_device_unlocked = dummy_read_is_device_unlocked;
+ ops->get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
+
+ return ops;
+}
+
+void fs_mgr_dummy_avb_ops_free(AvbOps *ops)
+{
+ free(ops);
+}
diff --git a/fs_mgr/fs_mgr_avb_ops.h b/fs_mgr/fs_mgr_avb_ops.h
new file mode 100644
index 0000000..9f99be8
--- /dev/null
+++ b/fs_mgr/fs_mgr_avb_ops.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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_AVB_OPS_H
+#define __CORE_FS_MGR_AVB_OPS_H
+
+#include <libavb/libavb.h>
+
+#include "fs_mgr.h"
+
+__BEGIN_DECLS
+
+/* Allocates a "dummy" AvbOps instance solely for use in user-space.
+ * Returns nullptr on OOM.
+ *
+ * It mainly provides read_from_partitions() for user-space to get
+ * AvbSlotVerifyData.vbmeta_images[] and the caller MUST check their
+ * integrity against the androidboot.vbmeta.{hash_alg, size, digest}
+ * values from /proc/cmdline, e.g. verify_vbmeta_images()
+ * in fs_mgr_avb.cpp.
+ *
+ * Other I/O operations are only required in boot loader so we set
+ * them as dummy operations here.
+ * - Will allow any public key for signing.
+ * - returns 0 for any rollback index location.
+ * - returns device is unlocked regardless of the actual state.
+ * - returns a dummy guid for any partition.
+ *
+ * Frees with fs_mgr_dummy_avb_ops_free().
+ */
+AvbOps *fs_mgr_dummy_avb_ops_new(struct fstab *fstab);
+
+/* Frees an AvbOps instance previously allocated with fs_mgr_avb_ops_new(). */
+void fs_mgr_dummy_avb_ops_free(AvbOps *ops);
+
+__END_DECLS
+
+#endif /* __CORE_FS_MGR_AVB_OPS_H */
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
new file mode 100644
index 0000000..6012a39
--- /dev/null
+++ b/fs_mgr/fs_mgr_dm_ioctl.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <sys/ioctl.h>
+
+#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,
+ unsigned flags)
+{
+ memset(io, 0, DM_BUF_SIZE);
+ io->data_size = DM_BUF_SIZE;
+ io->data_start = sizeof(struct dm_ioctl);
+ io->version[0] = 4;
+ io->version[1] = 0;
+ io->version[2] = 0;
+ io->flags = flags | DM_READONLY_FLAG;
+ if (!name.empty()) {
+ strlcpy(io->name, name.c_str(), sizeof(io->name));
+ }
+}
+
+bool fs_mgr_create_verity_device(struct dm_ioctl *io,
+ const std::string &name,
+ int fd)
+{
+ fs_mgr_verity_ioctl_init(io, name, 1);
+ if (ioctl(fd, DM_DEV_CREATE, io)) {
+ PERROR << "Error creating device mapping";
+ return false;
+ }
+ 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, 0);
+ if (ioctl(fd, DM_DEV_REMOVE, io)) {
+ PERROR << "Error removing device mapping";
+ return false;
+ }
+ return true;
+}
+
+bool fs_mgr_get_verity_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, 0);
+ if (ioctl(fd, DM_DEV_STATUS, io)) {
+ PERROR << "Error fetching verity device number";
+ return false;
+ }
+
+ int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+ *out_dev_name = "/dev/block/dm-" + std::to_string(dev_num);
+
+ 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, 0);
+ if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+ PERROR << "Error activating verity device";
+ return false;
+ }
+ return true;
+}
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
new file mode 100644
index 0000000..5705f93
--- /dev/null
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <cutils/partition_utils.h>
+#include <sys/mount.h>
+
+#include <ext4_utils/ext4_utils.h>
+#include <ext4_utils/ext4.h>
+#include <ext4_utils/make_ext4fs.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/android.h>
+
+#include "fs_mgr_priv.h"
+#include "cryptfs.h"
+
+extern "C" {
+extern struct fs_info info; /* magic global from ext4_utils */
+extern void reset_ext4fs_info();
+}
+
+static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
+{
+ uint64_t dev_sz;
+ int fd, rc = 0;
+
+ if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
+ PERROR << "Cannot open block device";
+ return -1;
+ }
+
+ if ((ioctl(fd, BLKGETSIZE64, &dev_sz)) == -1) {
+ PERROR << "Cannot get block device size";
+ close(fd);
+ return -1;
+ }
+
+ struct selabel_handle *sehandle = selinux_android_file_context_handle();
+ if (!sehandle) {
+ /* libselinux logs specific error */
+ LERROR << "Cannot initialize android file_contexts";
+ close(fd);
+ return -1;
+ }
+
+ /* Format the partition using the calculated length */
+ reset_ext4fs_info();
+ info.len = (off64_t)dev_sz;
+ if (crypt_footer) {
+ info.len -= CRYPT_FOOTER_OFFSET;
+ }
+
+ /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
+ rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, sehandle, 0, 0, NULL, NULL, NULL);
+ if (rc) {
+ LERROR << "make_ext4fs returned " << rc;
+ }
+ close(fd);
+
+ if (sehandle) {
+ selabel_close(sehandle);
+ }
+
+ return rc;
+}
+
+static int format_f2fs(char *fs_blkdev)
+{
+ char * args[3];
+ int pid;
+ int rc = 0;
+
+ args[0] = (char *)"/sbin/mkfs.f2fs";
+ args[1] = fs_blkdev;
+ args[2] = (char *)0;
+
+ pid = fork();
+ if (pid < 0) {
+ return pid;
+ }
+ if (!pid) {
+ /* This doesn't return */
+ execv("/sbin/mkfs.f2fs", args);
+ exit(1);
+ }
+ for(;;) {
+ pid_t p = waitpid(pid, &rc, 0);
+ if (p != pid) {
+ LERROR << "Error waiting for child process - " << p;
+ rc = -1;
+ break;
+ }
+ if (WIFEXITED(rc)) {
+ rc = WEXITSTATUS(rc);
+ LINFO << args[0] << " done, status " << rc;
+ if (rc) {
+ rc = -1;
+ }
+ break;
+ }
+ LERROR << "Still waiting for " << args[0] << "...";
+ }
+
+ return rc;
+}
+
+int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
+{
+ int rc = -EINVAL;
+
+ LERROR << __FUNCTION__ << ": Format " << fstab->blk_device
+ << " as '" << fstab->fs_type << "'";
+
+ if (!strncmp(fstab->fs_type, "f2fs", 4)) {
+ rc = format_f2fs(fstab->blk_device);
+ } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
+ rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
+ } else {
+ LERROR << "File system type '" << fstab->fs_type << "' is not supported";
+ }
+
+ return rc;
+}
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
deleted file mode 100644
index 8b0f714..0000000
--- a/fs_mgr/fs_mgr_fstab.c
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-
-#include "fs_mgr_priv.h"
-
-struct fs_mgr_flag_values {
- char *key_loc;
- char *verity_loc;
- long long part_length;
- char *label;
- int partnum;
- int swap_prio;
- unsigned int zram_size;
-};
-
-struct flag_list {
- const char *name;
- unsigned flag;
-};
-
-static struct flag_list mount_flags[] = {
- { "noatime", MS_NOATIME },
- { "noexec", MS_NOEXEC },
- { "nosuid", MS_NOSUID },
- { "nodev", MS_NODEV },
- { "nodiratime", MS_NODIRATIME },
- { "ro", MS_RDONLY },
- { "rw", 0 },
- { "remount", MS_REMOUNT },
- { "bind", MS_BIND },
- { "rec", MS_REC },
- { "unbindable", MS_UNBINDABLE },
- { "private", MS_PRIVATE },
- { "slave", MS_SLAVE },
- { "shared", MS_SHARED },
- { "defaults", 0 },
- { 0, 0 },
-};
-
-static struct flag_list fs_mgr_flags[] = {
- { "wait", MF_WAIT },
- { "check", MF_CHECK },
- { "encryptable=",MF_CRYPT },
- { "forceencrypt=",MF_FORCECRYPT },
- { "fileencryption",MF_FILEENCRYPTION },
- { "nonremovable",MF_NONREMOVABLE },
- { "voldmanaged=",MF_VOLDMANAGED},
- { "length=", MF_LENGTH },
- { "recoveryonly",MF_RECOVERYONLY },
- { "swapprio=", MF_SWAPPRIO },
- { "zramsize=", MF_ZRAMSIZE },
- { "verify", MF_VERIFY },
- { "noemulatedsd", MF_NOEMULATEDSD },
- { "defaults", 0 },
- { 0, 0 },
-};
-
-static int parse_flags(char *flags, struct flag_list *fl,
- struct fs_mgr_flag_values *flag_vals,
- char *fs_options, int fs_options_len)
-{
- int f = 0;
- int i;
- char *p;
- char *savep;
-
- /* initialize flag values. If we find a relevant flag, we'll
- * update the value */
- if (flag_vals) {
- memset(flag_vals, 0, sizeof(*flag_vals));
- flag_vals->partnum = -1;
- flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
- }
-
- /* initialize fs_options to the null string */
- if (fs_options && (fs_options_len > 0)) {
- fs_options[0] = '\0';
- }
-
- p = strtok_r(flags, ",", &savep);
- while (p) {
- /* Look for the flag "p" in the flag list "fl"
- * If not found, the loop exits with fl[i].name being null.
- */
- for (i = 0; fl[i].name; i++) {
- if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
- f |= fl[i].flag;
- if ((fl[i].flag == MF_CRYPT) && flag_vals) {
- /* The encryptable flag is followed by an = and the
- * location of the keys. Get it and return it.
- */
- flag_vals->key_loc = strdup(strchr(p, '=') + 1);
- } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
- /* If the verify flag is followed by an = and the
- * location for the verity state, get it and return it.
- */
- char *start = strchr(p, '=');
- if (start) {
- flag_vals->verity_loc = strdup(start + 1);
- }
- } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
- /* The forceencrypt flag is followed by an = and the
- * location of the keys. Get it and return it.
- */
- flag_vals->key_loc = strdup(strchr(p, '=') + 1);
- } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
- /* The length flag is followed by an = and the
- * size of the partition. Get it and return it.
- */
- flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
- } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
- /* The voldmanaged flag is followed by an = and the
- * label, a colon and the partition number or the
- * word "auto", e.g.
- * voldmanaged=sdcard:3
- * Get and return them.
- */
- char *label_start;
- char *label_end;
- char *part_start;
-
- label_start = strchr(p, '=') + 1;
- label_end = strchr(p, ':');
- if (label_end) {
- flag_vals->label = strndup(label_start,
- (int) (label_end - label_start));
- part_start = strchr(p, ':') + 1;
- if (!strcmp(part_start, "auto")) {
- flag_vals->partnum = -1;
- } else {
- flag_vals->partnum = strtol(part_start, NULL, 0);
- }
- } else {
- ERROR("Warning: voldmanaged= flag malformed\n");
- }
- } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
- flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
- } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
- flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0);
- }
- break;
- }
- }
-
- if (!fl[i].name) {
- if (fs_options) {
- /* It's not a known flag, so it must be a filesystem specific
- * option. Add it to fs_options if it was passed in.
- */
- strlcat(fs_options, p, fs_options_len);
- strlcat(fs_options, ",", fs_options_len);
- } else {
- /* fs_options was not passed in, so if the flag is unknown
- * it's an error.
- */
- ERROR("Warning: unknown flag %s\n", p);
- }
- }
- p = strtok_r(NULL, ",", &savep);
- }
-
- if (fs_options && fs_options[0]) {
- /* remove the last trailing comma from the list of options */
- fs_options[strlen(fs_options) - 1] = '\0';
- }
-
- return f;
-}
-
-struct fstab *fs_mgr_read_fstab(const char *fstab_path)
-{
- FILE *fstab_file;
- int cnt, entries;
- ssize_t len;
- size_t alloc_len = 0;
- char *line = NULL;
- const char *delim = " \t";
- char *save_ptr, *p;
- struct fstab *fstab = NULL;
- struct fs_mgr_flag_values flag_vals;
-#define FS_OPTIONS_LEN 1024
- char tmp_fs_options[FS_OPTIONS_LEN];
-
- fstab_file = fopen(fstab_path, "r");
- if (!fstab_file) {
- ERROR("Cannot open file %s\n", fstab_path);
- return 0;
- }
-
- entries = 0;
- while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
- /* if the last character is a newline, shorten the string by 1 byte */
- if (line[len - 1] == '\n') {
- line[len - 1] = '\0';
- }
- /* Skip any leading whitespace */
- p = line;
- while (isspace(*p)) {
- p++;
- }
- /* ignore comments or empty lines */
- if (*p == '#' || *p == '\0')
- continue;
- entries++;
- }
-
- if (!entries) {
- ERROR("No entries found in fstab\n");
- goto err;
- }
-
- /* Allocate and init the fstab structure */
- fstab = calloc(1, sizeof(struct fstab));
- fstab->num_entries = entries;
- fstab->fstab_filename = strdup(fstab_path);
- fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
-
- fseek(fstab_file, 0, SEEK_SET);
-
- cnt = 0;
- while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
- /* if the last character is a newline, shorten the string by 1 byte */
- if (line[len - 1] == '\n') {
- line[len - 1] = '\0';
- }
-
- /* Skip any leading whitespace */
- p = line;
- while (isspace(*p)) {
- p++;
- }
- /* ignore comments or empty lines */
- if (*p == '#' || *p == '\0')
- continue;
-
- /* If a non-comment entry is greater than the size we allocated, give an
- * error and quit. This can happen in the unlikely case the file changes
- * between the two reads.
- */
- if (cnt >= entries) {
- ERROR("Tried to process more entries than counted\n");
- break;
- }
-
- if (!(p = strtok_r(line, delim, &save_ptr))) {
- ERROR("Error parsing mount source\n");
- goto err;
- }
- fstab->recs[cnt].blk_device = strdup(p);
-
- if (!(p = strtok_r(NULL, delim, &save_ptr))) {
- ERROR("Error parsing mount_point\n");
- goto err;
- }
- fstab->recs[cnt].mount_point = strdup(p);
-
- if (!(p = strtok_r(NULL, delim, &save_ptr))) {
- ERROR("Error parsing fs_type\n");
- goto err;
- }
- fstab->recs[cnt].fs_type = strdup(p);
-
- if (!(p = strtok_r(NULL, delim, &save_ptr))) {
- ERROR("Error parsing mount_flags\n");
- goto err;
- }
- tmp_fs_options[0] = '\0';
- fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
- tmp_fs_options, FS_OPTIONS_LEN);
-
- /* fs_options are optional */
- if (tmp_fs_options[0]) {
- fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
- } else {
- fstab->recs[cnt].fs_options = NULL;
- }
-
- if (!(p = strtok_r(NULL, delim, &save_ptr))) {
- ERROR("Error parsing fs_mgr_options\n");
- goto err;
- }
- fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
- &flag_vals, NULL, 0);
- fstab->recs[cnt].key_loc = flag_vals.key_loc;
- fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
- fstab->recs[cnt].length = flag_vals.part_length;
- fstab->recs[cnt].label = flag_vals.label;
- fstab->recs[cnt].partnum = flag_vals.partnum;
- fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
- fstab->recs[cnt].zram_size = flag_vals.zram_size;
- cnt++;
- }
- fclose(fstab_file);
- free(line);
- return fstab;
-
-err:
- fclose(fstab_file);
- free(line);
- if (fstab)
- fs_mgr_free_fstab(fstab);
- return NULL;
-}
-
-void fs_mgr_free_fstab(struct fstab *fstab)
-{
- int i;
-
- if (!fstab) {
- return;
- }
-
- 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].mount_point);
- free(fstab->recs[i].fs_type);
- free(fstab->recs[i].fs_options);
- free(fstab->recs[i].key_loc);
- free(fstab->recs[i].label);
- }
-
- /* Free the fstab_recs array created by calloc(3) */
- free(fstab->recs);
-
- /* Free the fstab filename */
- free(fstab->fstab_filename);
-
- /* Free fstab */
- free(fstab);
-}
-
-/* Add an entry to the fstab, and return 0 on success or -1 on error */
-int fs_mgr_add_entry(struct fstab *fstab,
- const char *mount_point, const char *fs_type,
- const char *blk_device)
-{
- struct fstab_rec *new_fstab_recs;
- int n = fstab->num_entries;
-
- new_fstab_recs = (struct fstab_rec *)
- realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
-
- if (!new_fstab_recs) {
- return -1;
- }
-
- /* A new entry was added, so initialize it */
- memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
- new_fstab_recs[n].mount_point = strdup(mount_point);
- new_fstab_recs[n].fs_type = strdup(fs_type);
- new_fstab_recs[n].blk_device = strdup(blk_device);
- new_fstab_recs[n].length = 0;
-
- /* Update the fstab struct */
- fstab->recs = new_fstab_recs;
- fstab->num_entries++;
-
- return 0;
-}
-
-/*
- * Returns the 1st matching fstab_rec that follows the start_rec.
- * start_rec is the result of a previous search or NULL.
- */
-struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
-{
- int i;
- if (!fstab) {
- return NULL;
- }
-
- if (start_rec) {
- for (i = 0; i < fstab->num_entries; i++) {
- if (&fstab->recs[i] == start_rec) {
- i++;
- break;
- }
- }
- } else {
- i = 0;
- }
- for (; i < fstab->num_entries; i++) {
- int len = strlen(fstab->recs[i].mount_point);
- if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
- (path[len] == '\0' || path[len] == '/')) {
- return &fstab->recs[i];
- }
- }
- return NULL;
-}
-
-/*
- * Returns the 1st matching mount point.
- * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
- * and give the fstab_rec from the previous search.
- */
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
-{
- return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
-}
-
-int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_VOLDMANAGED;
-}
-
-int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_NONREMOVABLE;
-}
-
-int fs_mgr_is_verified(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_VERIFY;
-}
-
-int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT);
-}
-
-int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
-}
-
-int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
-}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
new file mode 100644
index 0000000..48ddf29
--- /dev/null
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include "fs_mgr_priv.h"
+
+struct fs_mgr_flag_values {
+ char *key_loc;
+ char *verity_loc;
+ long long part_length;
+ char *label;
+ int partnum;
+ int swap_prio;
+ int max_comp_streams;
+ unsigned int zram_size;
+ uint64_t reserved_size;
+ unsigned int file_encryption_mode;
+ unsigned int erase_blk_size;
+ unsigned int logical_blk_size;
+};
+
+struct flag_list {
+ const char *name;
+ unsigned int flag;
+};
+
+static struct flag_list mount_flags[] = {
+ { "noatime", MS_NOATIME },
+ { "noexec", MS_NOEXEC },
+ { "nosuid", MS_NOSUID },
+ { "nodev", MS_NODEV },
+ { "nodiratime", MS_NODIRATIME },
+ { "ro", MS_RDONLY },
+ { "rw", 0 },
+ { "remount", MS_REMOUNT },
+ { "bind", MS_BIND },
+ { "rec", MS_REC },
+ { "unbindable", MS_UNBINDABLE },
+ { "private", MS_PRIVATE },
+ { "slave", MS_SLAVE },
+ { "shared", MS_SHARED },
+ { "defaults", 0 },
+ { 0, 0 },
+};
+
+static struct flag_list fs_mgr_flags[] = {
+ { "wait", MF_WAIT },
+ { "check", MF_CHECK },
+ { "encryptable=", MF_CRYPT },
+ { "forceencrypt=", MF_FORCECRYPT },
+ { "fileencryption=", MF_FILEENCRYPTION },
+ { "forcefdeorfbe=", MF_FORCEFDEORFBE },
+ { "nonremovable", MF_NONREMOVABLE },
+ { "voldmanaged=", MF_VOLDMANAGED},
+ { "length=", MF_LENGTH },
+ { "recoveryonly", MF_RECOVERYONLY },
+ { "swapprio=", MF_SWAPPRIO },
+ { "zramsize=", MF_ZRAMSIZE },
+ { "max_comp_streams=", MF_MAX_COMP_STREAMS },
+ { "verifyatboot", MF_VERIFYATBOOT },
+ { "verify", MF_VERIFY },
+ { "avb", MF_AVB },
+ { "noemulatedsd", MF_NOEMULATEDSD },
+ { "notrim", MF_NOTRIM },
+ { "formattable", MF_FORMATTABLE },
+ { "slotselect", MF_SLOTSELECT },
+ { "nofail", MF_NOFAIL },
+ { "latemount", MF_LATEMOUNT },
+ { "reservedsize=", MF_RESERVEDSIZE },
+ { "quota", MF_QUOTA },
+ { "eraseblk=", MF_ERASEBLKSIZE },
+ { "logicalblk=", MF_LOGICALBLKSIZE },
+ { "defaults", 0 },
+ { 0, 0 },
+};
+
+#define EM_SOFTWARE 1
+#define EM_ICE 2
+
+static struct flag_list encryption_modes[] = {
+ {"software", EM_SOFTWARE},
+ {"ice", EM_ICE},
+ {0, 0}
+};
+
+static uint64_t calculate_zram_size(unsigned int percentage)
+{
+ uint64_t total;
+
+ total = sysconf(_SC_PHYS_PAGES);
+ total *= percentage;
+ total /= 100;
+
+ total *= sysconf(_SC_PAGESIZE);
+
+ return total;
+}
+
+static uint64_t parse_size(const char *arg)
+{
+ char *endptr;
+ uint64_t size = strtoull(arg, &endptr, 10);
+ if (*endptr == 'k' || *endptr == 'K')
+ size *= 1024LL;
+ else if (*endptr == 'm' || *endptr == 'M')
+ size *= 1024LL * 1024LL;
+ else if (*endptr == 'g' || *endptr == 'G')
+ size *= 1024LL * 1024LL * 1024LL;
+
+ return size;
+}
+
+static int parse_flags(char *flags, struct flag_list *fl,
+ struct fs_mgr_flag_values *flag_vals,
+ char *fs_options, int fs_options_len)
+{
+ int f = 0;
+ int i;
+ char *p;
+ char *savep;
+
+ /* initialize flag values. If we find a relevant flag, we'll
+ * update the value */
+ if (flag_vals) {
+ memset(flag_vals, 0, sizeof(*flag_vals));
+ flag_vals->partnum = -1;
+ flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
+ }
+
+ /* initialize fs_options to the null string */
+ if (fs_options && (fs_options_len > 0)) {
+ fs_options[0] = '\0';
+ }
+
+ p = strtok_r(flags, ",", &savep);
+ while (p) {
+ /* Look for the flag "p" in the flag list "fl"
+ * If not found, the loop exits with fl[i].name being null.
+ */
+ for (i = 0; fl[i].name; i++) {
+ if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
+ f |= fl[i].flag;
+ if ((fl[i].flag == MF_CRYPT) && flag_vals) {
+ /* The encryptable flag is followed by an = and the
+ * location of the keys. Get it and return it.
+ */
+ flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+ } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
+ /* If the verify flag is followed by an = and the
+ * location for the verity state, get it and return it.
+ */
+ char *start = strchr(p, '=');
+ if (start) {
+ flag_vals->verity_loc = strdup(start + 1);
+ }
+ } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
+ /* The forceencrypt flag is followed by an = and the
+ * location of the keys. Get it and return it.
+ */
+ flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+ } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
+ /* The forcefdeorfbe flag is followed by an = and the
+ * location of the keys. Get it and return it.
+ */
+ flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+ flag_vals->file_encryption_mode = EM_SOFTWARE;
+ } else if ((fl[i].flag == MF_FILEENCRYPTION) && flag_vals) {
+ /* The fileencryption flag is followed by an = and the
+ * type of the encryption. Get it and return it.
+ */
+ const struct flag_list *j;
+ const char *mode = strchr(p, '=') + 1;
+ for (j = encryption_modes; j->name; ++j) {
+ if (!strcmp(mode, j->name)) {
+ flag_vals->file_encryption_mode = j->flag;
+ }
+ }
+ if (flag_vals->file_encryption_mode == 0) {
+ LERROR << "Unknown file encryption mode: " << mode;
+ }
+ } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
+ /* The length flag is followed by an = and the
+ * size of the partition. Get it and return it.
+ */
+ flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
+ } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
+ /* The voldmanaged flag is followed by an = and the
+ * label, a colon and the partition number or the
+ * word "auto", e.g.
+ * voldmanaged=sdcard:3
+ * Get and return them.
+ */
+ char *label_start;
+ char *label_end;
+ char *part_start;
+
+ label_start = strchr(p, '=') + 1;
+ label_end = strchr(p, ':');
+ if (label_end) {
+ flag_vals->label = strndup(label_start,
+ (int) (label_end - label_start));
+ part_start = strchr(p, ':') + 1;
+ if (!strcmp(part_start, "auto")) {
+ flag_vals->partnum = -1;
+ } else {
+ flag_vals->partnum = strtol(part_start, NULL, 0);
+ }
+ } else {
+ LERROR << "Warning: voldmanaged= flag malformed";
+ }
+ } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
+ flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
+ } else if ((fl[i].flag == MF_MAX_COMP_STREAMS) && flag_vals) {
+ flag_vals->max_comp_streams = strtoll(strchr(p, '=') + 1, NULL, 0);
+ } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
+ int is_percent = !!strrchr(p, '%');
+ unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
+ if (is_percent)
+ flag_vals->zram_size = calculate_zram_size(val);
+ else
+ flag_vals->zram_size = val;
+ } else if ((fl[i].flag == MF_RESERVEDSIZE) && flag_vals) {
+ /* The reserved flag is followed by an = and the
+ * reserved size of the partition. Get it and return it.
+ */
+ flag_vals->reserved_size = parse_size(strchr(p, '=') + 1);
+ } else if ((fl[i].flag == MF_ERASEBLKSIZE) && flag_vals) {
+ /* The erase block size flag is followed by an = and the flash
+ * erase block size. Get it, check that it is a power of 2 and
+ * at least 4096, and return it.
+ */
+ unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+ if (val >= 4096 && (val & (val - 1)) == 0)
+ flag_vals->erase_blk_size = val;
+ } else if ((fl[i].flag == MF_LOGICALBLKSIZE) && flag_vals) {
+ /* The logical block size flag is followed by an = and the flash
+ * logical block size. Get it, check that it is a power of 2 and
+ * at least 4096, and return it.
+ */
+ unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+ if (val >= 4096 && (val & (val - 1)) == 0)
+ flag_vals->logical_blk_size = val;
+ }
+ break;
+ }
+ }
+
+ if (!fl[i].name) {
+ if (fs_options) {
+ /* It's not a known flag, so it must be a filesystem specific
+ * option. Add it to fs_options if it was passed in.
+ */
+ strlcat(fs_options, p, fs_options_len);
+ strlcat(fs_options, ",", fs_options_len);
+ } else {
+ /* fs_options was not passed in, so if the flag is unknown
+ * it's an error.
+ */
+ LERROR << "Warning: unknown flag " << p;
+ }
+ }
+ p = strtok_r(NULL, ",", &savep);
+ }
+
+ if (fs_options && fs_options[0]) {
+ /* remove the last trailing comma from the list of options */
+ fs_options[strlen(fs_options) - 1] = '\0';
+ }
+
+ return f;
+}
+
+struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file)
+{
+ int cnt, entries;
+ ssize_t len;
+ size_t alloc_len = 0;
+ char *line = NULL;
+ const char *delim = " \t";
+ char *save_ptr, *p;
+ struct fstab *fstab = NULL;
+ struct fs_mgr_flag_values flag_vals;
+#define FS_OPTIONS_LEN 1024
+ char tmp_fs_options[FS_OPTIONS_LEN];
+
+ entries = 0;
+ while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
+ /* if the last character is a newline, shorten the string by 1 byte */
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+ /* Skip any leading whitespace */
+ p = line;
+ while (isspace(*p)) {
+ p++;
+ }
+ /* ignore comments or empty lines */
+ if (*p == '#' || *p == '\0')
+ continue;
+ entries++;
+ }
+
+ if (!entries) {
+ LERROR << "No entries found in fstab";
+ goto err;
+ }
+
+ /* Allocate and init the fstab structure */
+ fstab = static_cast<struct fstab *>(calloc(1, sizeof(struct fstab)));
+ fstab->num_entries = entries;
+ fstab->recs = static_cast<struct fstab_rec *>(
+ calloc(fstab->num_entries, sizeof(struct fstab_rec)));
+
+ fseek(fstab_file, 0, SEEK_SET);
+
+ cnt = 0;
+ while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
+ /* if the last character is a newline, shorten the string by 1 byte */
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+
+ /* Skip any leading whitespace */
+ p = line;
+ while (isspace(*p)) {
+ p++;
+ }
+ /* ignore comments or empty lines */
+ if (*p == '#' || *p == '\0')
+ continue;
+
+ /* If a non-comment entry is greater than the size we allocated, give an
+ * error and quit. This can happen in the unlikely case the file changes
+ * between the two reads.
+ */
+ if (cnt >= entries) {
+ LERROR << "Tried to process more entries than counted";
+ break;
+ }
+
+ if (!(p = strtok_r(line, delim, &save_ptr))) {
+ LERROR << "Error parsing mount source";
+ goto err;
+ }
+ fstab->recs[cnt].blk_device = strdup(p);
+
+ if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+ LERROR << "Error parsing mount_point";
+ goto err;
+ }
+ fstab->recs[cnt].mount_point = strdup(p);
+
+ if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+ LERROR << "Error parsing fs_type";
+ goto err;
+ }
+ fstab->recs[cnt].fs_type = strdup(p);
+
+ if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+ LERROR << "Error parsing mount_flags";
+ goto err;
+ }
+ tmp_fs_options[0] = '\0';
+ fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
+ tmp_fs_options, FS_OPTIONS_LEN);
+
+ /* fs_options are optional */
+ if (tmp_fs_options[0]) {
+ fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
+ } else {
+ fstab->recs[cnt].fs_options = NULL;
+ }
+
+ if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+ LERROR << "Error parsing fs_mgr_options";
+ goto err;
+ }
+ fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
+ &flag_vals, NULL, 0);
+ fstab->recs[cnt].key_loc = flag_vals.key_loc;
+ fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
+ fstab->recs[cnt].length = flag_vals.part_length;
+ fstab->recs[cnt].label = flag_vals.label;
+ fstab->recs[cnt].partnum = flag_vals.partnum;
+ fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
+ fstab->recs[cnt].max_comp_streams = flag_vals.max_comp_streams;
+ fstab->recs[cnt].zram_size = flag_vals.zram_size;
+ fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
+ fstab->recs[cnt].file_encryption_mode = flag_vals.file_encryption_mode;
+ fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
+ fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
+ cnt++;
+ }
+ /* If an A/B partition, modify block device to be the real block device */
+ if (fs_mgr_update_for_slotselect(fstab) != 0) {
+ LERROR << "Error updating for slotselect";
+ goto err;
+ }
+ free(line);
+ return fstab;
+
+err:
+ free(line);
+ if (fstab)
+ fs_mgr_free_fstab(fstab);
+ return NULL;
+}
+
+struct fstab *fs_mgr_read_fstab(const char *fstab_path)
+{
+ FILE *fstab_file;
+ struct fstab *fstab;
+
+ fstab_file = fopen(fstab_path, "r");
+ if (!fstab_file) {
+ LERROR << "Cannot open file " << fstab_path;
+ return NULL;
+ }
+ fstab = fs_mgr_read_fstab_file(fstab_file);
+ if (fstab) {
+ fstab->fstab_filename = strdup(fstab_path);
+ }
+ fclose(fstab_file);
+ return fstab;
+}
+
+void fs_mgr_free_fstab(struct fstab *fstab)
+{
+ int i;
+
+ if (!fstab) {
+ return;
+ }
+
+ 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].mount_point);
+ free(fstab->recs[i].fs_type);
+ free(fstab->recs[i].fs_options);
+ free(fstab->recs[i].key_loc);
+ free(fstab->recs[i].label);
+ }
+
+ /* Free the fstab_recs array created by calloc(3) */
+ free(fstab->recs);
+
+ /* Free the fstab filename */
+ free(fstab->fstab_filename);
+
+ /* Free fstab */
+ free(fstab);
+}
+
+/* Add an entry to the fstab, and return 0 on success or -1 on error */
+int fs_mgr_add_entry(struct fstab *fstab,
+ const char *mount_point, const char *fs_type,
+ const char *blk_device)
+{
+ struct fstab_rec *new_fstab_recs;
+ int n = fstab->num_entries;
+
+ new_fstab_recs = (struct fstab_rec *)
+ realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
+
+ if (!new_fstab_recs) {
+ return -1;
+ }
+
+ /* A new entry was added, so initialize it */
+ memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
+ new_fstab_recs[n].mount_point = strdup(mount_point);
+ new_fstab_recs[n].fs_type = strdup(fs_type);
+ new_fstab_recs[n].blk_device = strdup(blk_device);
+ new_fstab_recs[n].length = 0;
+
+ /* Update the fstab struct */
+ fstab->recs = new_fstab_recs;
+ fstab->num_entries++;
+
+ return 0;
+}
+
+/*
+ * Returns the 1st matching fstab_rec that follows the start_rec.
+ * start_rec is the result of a previous search or NULL.
+ */
+struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
+{
+ int i;
+ if (!fstab) {
+ return NULL;
+ }
+
+ if (start_rec) {
+ for (i = 0; i < fstab->num_entries; i++) {
+ if (&fstab->recs[i] == start_rec) {
+ i++;
+ break;
+ }
+ }
+ } else {
+ i = 0;
+ }
+ for (; i < fstab->num_entries; i++) {
+ int len = strlen(fstab->recs[i].mount_point);
+ if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
+ (path[len] == '\0' || path[len] == '/')) {
+ return &fstab->recs[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Returns the 1st matching mount point.
+ * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
+ * and give the fstab_rec from the previous search.
+ */
+struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
+{
+ return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
+}
+
+int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_VOLDMANAGED;
+}
+
+int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_NONREMOVABLE;
+}
+
+int fs_mgr_is_verified(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_VERIFY;
+}
+
+int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
+}
+
+int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
+}
+
+const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab)
+{
+ const struct flag_list *j;
+ for (j = encryption_modes; j->name; ++j) {
+ if (fstab->file_encryption_mode == j->flag) {
+ return j->name;
+ }
+ }
+ return NULL;
+}
+
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_FORCEFDEORFBE;
+}
+
+int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
+}
+
+int fs_mgr_is_notrim(struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_NOTRIM;
+}
+
+int fs_mgr_is_formattable(struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & (MF_FORMATTABLE);
+}
+
+int fs_mgr_is_slotselect(struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_SLOTSELECT;
+}
+
+int fs_mgr_is_nofail(struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_NOFAIL;
+}
+
+int fs_mgr_is_latemount(struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_LATEMOUNT;
+}
+
+int fs_mgr_is_quota(struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_QUOTA;
+}
diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c
deleted file mode 100644
index e5a00d5..0000000
--- a/fs_mgr/fs_mgr_main.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <libgen.h>
-#include "fs_mgr_priv.h"
-
-char *me = "";
-
-static void usage(void)
-{
- ERROR("%s: usage: %s <-a | -n mnt_point blk_dev | -u> <fstab_file>\n", me, me);
- exit(1);
-}
-
-/* Parse the command line. If an error is encountered, print an error message
- * and exit the program, do not return to the caller.
- * Return the number of argv[] entries consumed.
- */
-static void parse_options(int argc, char *argv[], int *a_flag, int *u_flag, int *n_flag,
- char **n_name, char **n_blk_dev)
-{
- me = basename(strdup(argv[0]));
-
- if (argc <= 1) {
- usage();
- }
-
- if (!strcmp(argv[1], "-a")) {
- if (argc != 3) {
- usage();
- }
- *a_flag = 1;
- }
- if (!strcmp(argv[1], "-n")) {
- if (argc != 5) {
- usage();
- }
- *n_flag = 1;
- *n_name = argv[2];
- *n_blk_dev = argv[3];
- }
- if (!strcmp(argv[1], "-u")) {
- if (argc != 3) {
- usage();
- }
- *u_flag = 1;
- }
-
- /* If no flag is specified, it's an error */
- if (!(*a_flag | *n_flag | *u_flag)) {
- usage();
- }
-
- /* If more than one flag is specified, it's an error */
- if ((*a_flag + *n_flag + *u_flag) > 1) {
- usage();
- }
-
- return;
-}
-
-int main(int argc, char *argv[])
-{
- int a_flag=0;
- int u_flag=0;
- int n_flag=0;
- char *n_name=NULL;
- char *n_blk_dev=NULL;
- char *fstab_file=NULL;
- struct fstab *fstab=NULL;
-
- klog_init();
- klog_set_level(6);
-
- parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev);
-
- /* The name of the fstab file is last, after the option */
- fstab_file = argv[argc - 1];
-
- fstab = fs_mgr_read_fstab(fstab_file);
-
- if (a_flag) {
- return fs_mgr_mount_all(fstab);
- } else if (n_flag) {
- return fs_mgr_do_mount(fstab, n_name, n_blk_dev, 0);
- } else if (u_flag) {
- return fs_mgr_unmount_all(fstab);
- } else {
- ERROR("%s: Internal error, unknown option\n", me);
- exit(1);
- }
-
- fs_mgr_free_fstab(fstab);
-
- /* Should not get here */
- exit(1);
-}
-
diff --git a/fs_mgr/fs_mgr_main.cpp b/fs_mgr/fs_mgr_main.cpp
new file mode 100644
index 0000000..f3919d9
--- /dev/null
+++ b/fs_mgr/fs_mgr_main.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "fs_mgr_priv.h"
+
+#ifdef _LIBGEN_H
+#warning "libgen.h must not be included"
+#endif
+
+char *me = nullptr;
+
+static void usage(void)
+{
+ LERROR << me << ": usage: " << me
+ << " <-a | -n mnt_point blk_dev | -u> <fstab_file>";
+ exit(1);
+}
+
+/* Parse the command line. If an error is encountered, print an error message
+ * and exit the program, do not return to the caller.
+ * Return the number of argv[] entries consumed.
+ */
+static void parse_options(int argc, char * const argv[], int *a_flag, int *u_flag, int *n_flag,
+ const char **n_name, const char **n_blk_dev)
+{
+ me = basename(argv[0]);
+
+ if (argc <= 1) {
+ usage();
+ }
+
+ if (!strcmp(argv[1], "-a")) {
+ if (argc != 3) {
+ usage();
+ }
+ *a_flag = 1;
+ }
+ if (!strcmp(argv[1], "-n")) {
+ if (argc != 5) {
+ usage();
+ }
+ *n_flag = 1;
+ *n_name = argv[2];
+ *n_blk_dev = argv[3];
+ }
+ if (!strcmp(argv[1], "-u")) {
+ if (argc != 3) {
+ usage();
+ }
+ *u_flag = 1;
+ }
+
+ /* If no flag is specified, it's an error */
+ if (!(*a_flag | *n_flag | *u_flag)) {
+ usage();
+ }
+
+ /* If more than one flag is specified, it's an error */
+ if ((*a_flag + *n_flag + *u_flag) > 1) {
+ usage();
+ }
+
+ return;
+}
+
+int main(int argc, char * const argv[])
+{
+ int a_flag=0;
+ int u_flag=0;
+ int n_flag=0;
+ const char *n_name=NULL;
+ const char *n_blk_dev=NULL;
+ const char *fstab_file=NULL;
+ struct fstab *fstab=NULL;
+
+ setenv("ANDROID_LOG_TAGS", "*:i", 1); // Set log level to INFO
+ android::base::InitLogging(
+ const_cast<char **>(argv), &android::base::KernelLogger);
+
+ parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev);
+
+ /* The name of the fstab file is last, after the option */
+ fstab_file = argv[argc - 1];
+
+ fstab = fs_mgr_read_fstab(fstab_file);
+
+ if (a_flag) {
+ return fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
+ } else if (n_flag) {
+ return fs_mgr_do_mount(fstab, n_name, (char *)n_blk_dev, 0);
+ } else if (u_flag) {
+ return fs_mgr_unmount_all(fstab);
+ } else {
+ LERROR << me << ": Internal error, unknown option";
+ exit(1);
+ }
+
+ fs_mgr_free_fstab(fstab);
+
+ /* Should not get here */
+ exit(1);
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index d56111a..79c27c4 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -17,12 +17,30 @@
#ifndef __CORE_FS_MGR_PRIV_H
#define __CORE_FS_MGR_PRIV_H
-#include <cutils/klog.h>
+#include <android-base/logging.h>
#include <fs_mgr.h>
-#define INFO(x...) KLOG_INFO("fs_mgr", x)
-#define WARNING(x...) KLOG_WARNING("fs_mgr", x)
-#define ERROR(x...) KLOG_ERROR("fs_mgr", x)
+/* The CHECK() in logging.h will use program invocation name as the tag.
+ * Thus, the log will have prefix "init: " when libfs_mgr is statically
+ * linked in the init process. This might be opaque when debugging.
+ * Appends "in libfs_mgr" at the end of the abort message to explicitly
+ * indicate the check happens in fs_mgr.
+ */
+#define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr "
+
+#define FS_MGR_TAG "[libfs_mgr]"
+
+// Logs a message to kernel
+#define LINFO LOG(INFO) << FS_MGR_TAG
+#define LWARNING LOG(WARNING) << FS_MGR_TAG
+#define LERROR LOG(ERROR) << FS_MGR_TAG
+
+// Logs a message with strerror(errno) at the end
+#define PINFO PLOG(INFO) << FS_MGR_TAG
+#define PWARNING PLOG(WARNING) << FS_MGR_TAG
+#define PERROR PLOG(ERROR) << FS_MGR_TAG
+
+__BEGIN_DECLS
#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
@@ -46,7 +64,7 @@
*
* <fs_mgr_options> is a comma separated list of flags that control the operation of
* the fs_mgr program. The list includes "wait", which will wait till
- * the <source> file exists, and "check", which requests that the fs_mgr
+ * the <source> file exists, and "check", which requests that the fs_mgr
* run an fscheck program on the <source> before mounting the filesystem.
* If check is specifed on a read-only filesystem, it is ignored.
* Also, "encryptable" means that filesystem can be encrypted.
@@ -63,24 +81,40 @@
*
*/
-#define MF_WAIT 0x1
-#define MF_CHECK 0x2
-#define MF_CRYPT 0x4
-#define MF_NONREMOVABLE 0x8
-#define MF_VOLDMANAGED 0x10
-#define MF_LENGTH 0x20
-#define MF_RECOVERYONLY 0x40
-#define MF_SWAPPRIO 0x80
-#define MF_ZRAMSIZE 0x100
-#define MF_VERIFY 0x200
-#define MF_FORCECRYPT 0x400
-#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
- external storage */
-#define MF_FILEENCRYPTION 0x2000
+#define MF_WAIT 0x1
+#define MF_CHECK 0x2
+#define MF_CRYPT 0x4
+#define MF_NONREMOVABLE 0x8
+#define MF_VOLDMANAGED 0x10
+#define MF_LENGTH 0x20
+#define MF_RECOVERYONLY 0x40
+#define MF_SWAPPRIO 0x80
+#define MF_ZRAMSIZE 0x100
+#define MF_VERIFY 0x200
+#define MF_FORCECRYPT 0x400
+#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
+ external storage */
+#define MF_NOTRIM 0x1000
+#define MF_FILEENCRYPTION 0x2000
+#define MF_FORMATTABLE 0x4000
+#define MF_SLOTSELECT 0x8000
+#define MF_FORCEFDEORFBE 0x10000
+#define MF_LATEMOUNT 0x20000
+#define MF_NOFAIL 0x40000
+#define MF_VERIFYATBOOT 0x80000
+#define MF_MAX_COMP_STREAMS 0x100000
+#define MF_RESERVEDSIZE 0x200000
+#define MF_QUOTA 0x400000
+#define MF_ERASEBLKSIZE 0x800000
+#define MF_LOGICALBLKSIZE 0X1000000
+#define MF_AVB 0X2000000
#define DM_BUF_SIZE 4096
int fs_mgr_set_blk_ro(const char *blockdev);
+int fs_mgr_test_access(const char *device);
+int fs_mgr_update_for_slotselect(struct fstab *fstab);
+
+__END_DECLS
#endif /* __CORE_FS_MGR_PRIV_H */
-
diff --git a/fs_mgr/fs_mgr_priv_avb.h b/fs_mgr/fs_mgr_priv_avb.h
new file mode 100644
index 0000000..6d0171c
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_avb.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_MGR_PRIV_AVB_H
+#define __CORE_FS_MGR_PRIV_AVB_H
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#include "fs_mgr.h"
+
+__BEGIN_DECLS
+
+#define FS_MGR_SETUP_AVB_HASHTREE_DISABLED (-2)
+#define FS_MGR_SETUP_AVB_FAIL (-1)
+#define FS_MGR_SETUP_AVB_SUCCESS 0
+
+bool fs_mgr_is_avb_used();
+
+/* Gets AVB metadata through external/avb/libavb for all partitions:
+ * AvbSlotVerifyData.vbmeta_images[] and checks their integrity
+ * against the androidboot.vbmeta.{hash_alg, size, digest} values
+ * from /proc/cmdline.
+ *
+ * Return values:
+ * - FS_MGR_SETUP_AVB_SUCCESS: the metadata cab be trusted.
+ * - FS_MGR_SETUP_AVB_FAIL: any error when reading and verifying the
+ * metadata, e.g. I/O error, digest value mismatch, size mismatch.
+ * - FS_MGR_SETUP_AVB_HASHTREE_DISABLED: to support the existing
+ * 'adb disable-verity' feature in Android. It's very helpful for
+ * developers to make the filesystem writable to allow replacing
+ * binaries on the device.
+ */
+int fs_mgr_load_vbmeta_images(struct fstab *fstab);
+
+void fs_mgr_unload_vbmeta_images();
+
+int fs_mgr_setup_avb(struct fstab_rec *fstab_entry);
+
+__END_DECLS
+
+#endif /* __CORE_FS_MGR_PRIV_AVB_H */
diff --git a/fs_mgr/fs_mgr_priv_dm_ioctl.h b/fs_mgr/fs_mgr_priv_dm_ioctl.h
new file mode 100644
index 0000000..eeae4dd
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_dm_ioctl.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_MGR_PRIV_DM_IOCTL_H
+#define __CORE_FS_MGR_PRIV_DM_IOCTL_H
+
+#include <string>
+#include <linux/dm-ioctl.h>
+
+void fs_mgr_verity_ioctl_init(struct dm_ioctl *io,
+ const std::string &name,
+ unsigned flags);
+
+bool fs_mgr_create_verity_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_get_verity_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);
+
+#endif /* __CORE_FS_MGR_PRIV_DM_IOCTL_H */
diff --git a/fs_mgr/fs_mgr_priv_sha.h b/fs_mgr/fs_mgr_priv_sha.h
new file mode 100644
index 0000000..1abc273
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_sha.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_MGR_PRIV_SHA_H
+#define __CORE_FS_MGR_PRIV_SHA_H
+
+#include <openssl/sha.h>
+
+class SHA256Hasher
+{
+ private:
+ SHA256_CTX sha256_ctx;
+ uint8_t hash[SHA256_DIGEST_LENGTH];
+
+ public:
+ enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH };
+
+ SHA256Hasher()
+ {
+ SHA256_Init(&sha256_ctx);
+ }
+
+ void update(const void *data, size_t data_size)
+ {
+ SHA256_Update(&sha256_ctx, data, data_size);
+ }
+
+ const uint8_t *finalize()
+ {
+ SHA256_Final(hash, &sha256_ctx);
+ return hash;
+ }
+};
+
+class SHA512Hasher
+{
+ private:
+ SHA512_CTX sha512_ctx;
+ uint8_t hash[SHA512_DIGEST_LENGTH];
+
+ public:
+ enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH };
+
+ SHA512Hasher()
+ {
+ SHA512_Init(&sha512_ctx);
+ }
+
+ void update(const uint8_t *data, size_t data_size)
+ {
+ SHA512_Update(&sha512_ctx, data, data_size);
+ }
+
+ const uint8_t *finalize()
+ {
+ SHA512_Final(hash, &sha512_ctx);
+ return hash;
+ }
+};
+
+#endif /* __CORE_FS_MGR_PRIV_SHA_H */
diff --git a/fs_mgr/fs_mgr_priv_verity.h b/fs_mgr/fs_mgr_priv_verity.h
index f90e596..1a6d215 100644
--- a/fs_mgr/fs_mgr_priv_verity.h
+++ b/fs_mgr/fs_mgr_priv_verity.h
@@ -14,7 +14,14 @@
* limitations under the License.
*/
-#define FS_MGR_SETUP_VERITY_DISABLED -2
-#define FS_MGR_SETUP_VERITY_FAIL -1
+#include <sys/cdefs.h>
+
+#define FS_MGR_SETUP_VERITY_DISABLED (-2)
+#define FS_MGR_SETUP_VERITY_FAIL (-1)
#define FS_MGR_SETUP_VERITY_SUCCESS 0
-int fs_mgr_setup_verity(struct fstab_rec *fstab);
+
+__BEGIN_DECLS
+
+int fs_mgr_setup_verity(struct fstab_rec *fstab, bool verify_dev);
+
+__END_DECLS
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
new file mode 100644
index 0000000..94b43e4
--- /dev/null
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cutils/properties.h>
+
+#include "fs_mgr.h"
+#include "fs_mgr_priv.h"
+
+#include "bootloader.h"
+
+// Copies slot_suffix from misc into |out_suffix|. Returns 0 on
+// success, -1 on error or if there is no non-empty slot_suffix.
+static int get_active_slot_suffix_from_misc(struct fstab *fstab,
+ char *out_suffix,
+ size_t suffix_len)
+{
+ int n;
+ int misc_fd;
+ ssize_t num_read;
+ struct bootloader_message_ab msg;
+
+ misc_fd = -1;
+ for (n = 0; n < fstab->num_entries; n++) {
+ if (strcmp(fstab->recs[n].mount_point, "/misc") == 0) {
+ misc_fd = open(fstab->recs[n].blk_device, O_RDONLY);
+ if (misc_fd == -1) {
+ PERROR << "Error opening misc partition '"
+ << fstab->recs[n].blk_device << "'";
+ return -1;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (misc_fd == -1) {
+ LERROR << "Error finding misc partition";
+ return -1;
+ }
+
+ num_read = TEMP_FAILURE_RETRY(read(misc_fd, &msg, sizeof(msg)));
+ // Linux will never return partial reads when reading from block
+ // devices so no need to worry about them.
+ if (num_read != sizeof(msg)) {
+ PERROR << "Error reading bootloader_message";
+ close(misc_fd);
+ return -1;
+ }
+ close(misc_fd);
+ if (msg.slot_suffix[0] == '\0')
+ return -1;
+ strncpy(out_suffix, msg.slot_suffix, suffix_len);
+ return 0;
+}
+
+// Gets slot_suffix from either the kernel cmdline / firmware or the
+// misc partition. Sets |out_suffix| on success and returns 0. Returns
+// -1 if slot_suffix could not be determined.
+static int get_active_slot_suffix(struct fstab *fstab, char *out_suffix,
+ size_t suffix_len)
+{
+ char propbuf[PROPERTY_VALUE_MAX];
+
+ // Get the suffix from the kernel commandline (note that we don't
+ // allow the empty suffix). On bootloaders natively supporting A/B
+ // we'll hit this path every time so don't bother logging it.
+ property_get("ro.boot.slot_suffix", propbuf, "");
+ if (propbuf[0] != '\0') {
+ strncpy(out_suffix, propbuf, suffix_len);
+ return 0;
+ }
+
+ // If we couldn't get the suffix from the kernel cmdline, try the
+ // the misc partition.
+ if (get_active_slot_suffix_from_misc(fstab, out_suffix, suffix_len) == 0) {
+ LINFO << "Using slot suffix '" << out_suffix << "' from misc";
+ return 0;
+ }
+
+ LERROR << "Error determining slot_suffix";
+
+ return -1;
+}
+
+// Updates |fstab| for slot_suffix. Returns 0 on success, -1 on error.
+int fs_mgr_update_for_slotselect(struct fstab *fstab)
+{
+ int n;
+ char suffix[PROPERTY_VALUE_MAX];
+ int got_suffix = 0;
+
+ for (n = 0; n < fstab->num_entries; n++) {
+ if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
+ char *tmp;
+
+ if (!got_suffix) {
+ memset(suffix, '\0', sizeof(suffix));
+ if (get_active_slot_suffix(fstab, suffix,
+ sizeof(suffix) - 1) != 0) {
+ return -1;
+ }
+ got_suffix = 1;
+ }
+
+ if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device,
+ suffix) > 0) {
+ free(fstab->recs[n].blk_device);
+ fstab->recs[n].blk_device = tmp;
+ } else {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
deleted file mode 100644
index a4a99c3..0000000
--- a/fs_mgr/fs_mgr_verity.c
+++ /dev/null
@@ -1,1054 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <libgen.h>
-#include <time.h>
-
-#include <private/android_filesystem_config.h>
-#include <cutils/properties.h>
-#include <logwrap/logwrap.h>
-
-#include "mincrypt/rsa.h"
-#include "mincrypt/sha.h"
-#include "mincrypt/sha256.h"
-
-#include "ext4_sb.h"
-#include "squashfs_utils.h"
-
-#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_verity.h"
-
-#define FSTAB_PREFIX "/fstab."
-
-#define VERITY_METADATA_SIZE 32768
-#define VERITY_TABLE_RSA_KEY "/verity_key"
-
-#define METADATA_MAGIC 0x01564c54
-#define METADATA_TAG_MAX_LENGTH 63
-#define METADATA_EOD "eod"
-
-#define VERITY_LASTSIG_TAG "verity_lastsig"
-
-#define VERITY_STATE_TAG "verity_state"
-#define VERITY_STATE_HEADER 0x83c0ae9d
-#define VERITY_STATE_VERSION 1
-
-#define VERITY_KMSG_RESTART "dm-verity device corrupted"
-#define VERITY_KMSG_BUFSIZE 1024
-
-#define __STRINGIFY(x) #x
-#define STRINGIFY(x) __STRINGIFY(x)
-
-struct verity_state {
- uint32_t header;
- uint32_t version;
- int32_t mode;
-};
-
-extern struct fs_info info;
-
-static RSAPublicKey *load_key(char *path)
-{
- FILE *f;
- RSAPublicKey *key;
-
- key = malloc(sizeof(RSAPublicKey));
- if (!key) {
- ERROR("Can't malloc key\n");
- return NULL;
- }
-
- f = fopen(path, "r");
- if (!f) {
- ERROR("Can't open '%s'\n", path);
- free(key);
- return NULL;
- }
-
- if (!fread(key, sizeof(*key), 1, f)) {
- ERROR("Could not read key!");
- fclose(f);
- free(key);
- return NULL;
- }
-
- if (key->len != RSANUMWORDS) {
- ERROR("Invalid key length %d\n", key->len);
- fclose(f);
- free(key);
- return NULL;
- }
-
- fclose(f);
- return key;
-}
-
-static int verify_table(char *signature, char *table, int table_length)
-{
- RSAPublicKey *key;
- uint8_t hash_buf[SHA256_DIGEST_SIZE];
- int retval = -1;
-
- // Hash the table
- SHA256_hash((uint8_t*)table, table_length, hash_buf);
-
- // Now get the public key from the keyfile
- key = load_key(VERITY_TABLE_RSA_KEY);
- if (!key) {
- ERROR("Couldn't load verity keys");
- goto out;
- }
-
- // verify the result
- if (!RSA_verify(key,
- (uint8_t*) signature,
- RSANUMBYTES,
- (uint8_t*) hash_buf,
- SHA256_DIGEST_SIZE)) {
- ERROR("Couldn't verify table.");
- goto out;
- }
-
- retval = 0;
-
-out:
- free(key);
- return retval;
-}
-
-static int squashfs_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
- struct squashfs_info sq_info;
-
- if (squashfs_parse_sb(blk_device, &sq_info) >= 0) {
- *device_size = sq_info.bytes_used_4K_padded;
- return 0;
- } else {
- return -1;
- }
-}
-
-static int ext4_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
- int data_device;
- struct ext4_super_block sb;
- struct fs_info info;
-
- info.len = 0; /* Only len is set to 0 to ask the device for real size. */
-
- data_device = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
- if (data_device == -1) {
- ERROR("Error opening block device (%s)", strerror(errno));
- return -1;
- }
-
- if (TEMP_FAILURE_RETRY(lseek64(data_device, 1024, SEEK_SET)) < 0) {
- ERROR("Error seeking to superblock");
- close(data_device);
- return -1;
- }
-
- if (TEMP_FAILURE_RETRY(read(data_device, &sb, sizeof(sb))) != sizeof(sb)) {
- ERROR("Error reading superblock");
- close(data_device);
- return -1;
- }
-
- ext4_parse_sb(&sb, &info);
- *device_size = info.len;
-
- close(data_device);
- return 0;
-}
-
-static int get_fs_size(char *fs_type, char *blk_device, uint64_t *device_size) {
- if (!strcmp(fs_type, "ext4")) {
- if (ext4_get_target_device_size(blk_device, device_size) < 0) {
- ERROR("Failed to get ext4 fs size on %s.", blk_device);
- return -1;
- }
- } else if (!strcmp(fs_type, "squashfs")) {
- if (squashfs_get_target_device_size(blk_device, device_size) < 0) {
- ERROR("Failed to get squashfs fs size on %s.", blk_device);
- return -1;
- }
- } else {
- ERROR("%s: Unsupported filesystem for verity.", fs_type);
- return -1;
- }
- return 0;
-}
-
-static int read_verity_metadata(uint64_t device_size, char *block_device, char **signature,
- char **table)
-{
- unsigned magic_number;
- unsigned table_length;
- int protocol_version;
- int device;
- int retval = FS_MGR_SETUP_VERITY_FAIL;
-
- *signature = NULL;
-
- if (table) {
- *table = NULL;
- }
-
- device = TEMP_FAILURE_RETRY(open(block_device, O_RDONLY | O_CLOEXEC));
- if (device == -1) {
- ERROR("Could not open block device %s (%s).\n", block_device, strerror(errno));
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(lseek64(device, device_size, SEEK_SET)) < 0) {
- ERROR("Could not seek to start of verity metadata block.\n");
- goto out;
- }
-
- // check the magic number
- if (TEMP_FAILURE_RETRY(read(device, &magic_number, sizeof(magic_number))) !=
- sizeof(magic_number)) {
- ERROR("Couldn't read magic number!\n");
- goto out;
- }
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
- if (magic_number == VERITY_METADATA_MAGIC_DISABLE) {
- retval = FS_MGR_SETUP_VERITY_DISABLED;
- INFO("Attempt to cleanly disable verity - only works in USERDEBUG");
- goto out;
- }
-#endif
-
- if (magic_number != VERITY_METADATA_MAGIC_NUMBER) {
- ERROR("Couldn't find verity metadata at offset %"PRIu64"!\n", device_size);
- goto out;
- }
-
- // check the protocol version
- if (TEMP_FAILURE_RETRY(read(device, &protocol_version,
- sizeof(protocol_version))) != sizeof(protocol_version)) {
- ERROR("Couldn't read verity metadata protocol version!\n");
- goto out;
- }
- if (protocol_version != 0) {
- ERROR("Got unknown verity metadata protocol version %d!\n", protocol_version);
- goto out;
- }
-
- // get the signature
- *signature = (char*) malloc(RSANUMBYTES);
- if (!*signature) {
- ERROR("Couldn't allocate memory for signature!\n");
- goto out;
- }
- if (TEMP_FAILURE_RETRY(read(device, *signature, RSANUMBYTES)) != RSANUMBYTES) {
- ERROR("Couldn't read signature from verity metadata!\n");
- goto out;
- }
-
- if (!table) {
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
- goto out;
- }
-
- // get the size of the table
- if (TEMP_FAILURE_RETRY(read(device, &table_length, sizeof(table_length))) !=
- sizeof(table_length)) {
- ERROR("Couldn't get the size of the verity table from metadata!\n");
- goto out;
- }
-
- // get the table + null terminator
- *table = malloc(table_length + 1);
- if (!*table) {
- ERROR("Couldn't allocate memory for verity table!\n");
- goto out;
- }
- if (TEMP_FAILURE_RETRY(read(device, *table, table_length)) !=
- (ssize_t)table_length) {
- ERROR("Couldn't read the verity table from metadata!\n");
- goto out;
- }
-
- (*table)[table_length] = 0;
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
-
-out:
- if (device != -1)
- close(device);
-
- if (retval != FS_MGR_SETUP_VERITY_SUCCESS) {
- free(*signature);
- *signature = NULL;
-
- if (table) {
- free(*table);
- *table = NULL;
- }
- }
-
- return retval;
-}
-
-static void verity_ioctl_init(struct dm_ioctl *io, char *name, unsigned flags)
-{
- memset(io, 0, DM_BUF_SIZE);
- io->data_size = DM_BUF_SIZE;
- io->data_start = sizeof(struct dm_ioctl);
- io->version[0] = 4;
- io->version[1] = 0;
- io->version[2] = 0;
- io->flags = flags | DM_READONLY_FLAG;
- if (name) {
- strlcpy(io->name, name, sizeof(io->name));
- }
-}
-
-static int create_verity_device(struct dm_ioctl *io, char *name, int fd)
-{
- verity_ioctl_init(io, name, 1);
- if (ioctl(fd, DM_DEV_CREATE, io)) {
- ERROR("Error creating device mapping (%s)", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int get_verity_device_name(struct dm_ioctl *io, char *name, int fd, char **dev_name)
-{
- verity_ioctl_init(io, name, 0);
- if (ioctl(fd, DM_DEV_STATUS, io)) {
- ERROR("Error fetching verity device number (%s)", strerror(errno));
- return -1;
- }
- int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
- if (asprintf(dev_name, "/dev/block/dm-%u", dev_num) < 0) {
- ERROR("Error getting verity block device name (%s)", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd, char *table,
- int mode)
-{
- char *verity_params;
- char *buffer = (char*) io;
- size_t bufsize;
-
- verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
-
- struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
-
- // set tgt arguments here
- io->target_count = 1;
- tgt->status=0;
- tgt->sector_start=0;
- tgt->length=device_size/512;
- strcpy(tgt->target_type, "verity");
-
- // build the verity params here
- verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
- bufsize = DM_BUF_SIZE - (verity_params - buffer);
-
- if (mode == VERITY_MODE_EIO) {
- // allow operation with older dm-verity drivers that are unaware
- // of the mode parameter by omitting it; this also means that we
- // cannot use logging mode with these drivers, they always cause
- // an I/O error for corrupted blocks
- strcpy(verity_params, table);
- } else if (snprintf(verity_params, bufsize, "%s %d", table, mode) < 0) {
- return -1;
- }
-
- // set next target boundary
- verity_params += strlen(verity_params) + 1;
- verity_params = (char*) (((unsigned long)verity_params + 7) & ~8);
- tgt->next = verity_params - buffer;
-
- // send the ioctl to load the verity table
- if (ioctl(fd, DM_TABLE_LOAD, io)) {
- ERROR("Error loading verity table (%s)", strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-static int resume_verity_table(struct dm_ioctl *io, char *name, int fd)
-{
- verity_ioctl_init(io, name, 0);
- if (ioctl(fd, DM_DEV_SUSPEND, io)) {
- ERROR("Error activating verity device (%s)", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int test_access(char *device) {
- int tries = 25;
- while (tries--) {
- if (!access(device, F_OK) || errno != ENOENT) {
- return 0;
- }
- usleep(40 * 1000);
- }
- return -1;
-}
-
-static int check_verity_restart(const char *fname)
-{
- char buffer[VERITY_KMSG_BUFSIZE + 1];
- int fd;
- int rc = 0;
- ssize_t size;
- struct stat s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- if (errno != ENOENT) {
- ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
- }
- goto out;
- }
-
- if (fstat(fd, &s) == -1) {
- ERROR("Failed to fstat %s (%s)\n", fname, strerror(errno));
- goto out;
- }
-
- size = VERITY_KMSG_BUFSIZE;
-
- if (size > s.st_size) {
- size = s.st_size;
- }
-
- if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
- ERROR("Failed to lseek %jd %s (%s)\n", (intmax_t)(s.st_size - size), fname,
- strerror(errno));
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(read(fd, buffer, size)) != size) {
- ERROR("Failed to read %zd bytes from %s (%s)\n", size, fname,
- strerror(errno));
- goto out;
- }
-
- buffer[size] = '\0';
-
- if (strstr(buffer, VERITY_KMSG_RESTART) != NULL) {
- rc = 1;
- }
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int was_verity_restart()
-{
- static const char *files[] = {
- "/sys/fs/pstore/console-ramoops",
- "/proc/last_kmsg",
- NULL
- };
- int i;
-
- for (i = 0; files[i]; ++i) {
- if (check_verity_restart(files[i])) {
- return 1;
- }
- }
-
- return 0;
-}
-
-static int metadata_add(FILE *fp, long start, const char *tag,
- unsigned int length, off64_t *offset)
-{
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fprintf(fp, "%s %u\n", tag, length) < 0) {
- return -1;
- }
-
- *offset = ftell(fp);
-
- if (fseek(fp, length, SEEK_CUR) < 0 ||
- fprintf(fp, METADATA_EOD " 0\n") < 0) {
- return -1;
- }
-
- return 0;
-}
-
-static int metadata_find(const char *fname, const char *stag,
- unsigned int slength, off64_t *offset)
-{
- FILE *fp = NULL;
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- int rc = -1;
- int n;
- long start = 0x4000; /* skip cryptfs metadata area */
- uint32_t magic;
- unsigned int length = 0;
-
- if (!fname) {
- return -1;
- }
-
- fp = fopen(fname, "r+");
-
- if (!fp) {
- ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
- goto out;
- }
-
- /* check magic */
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fread(&magic, sizeof(magic), 1, fp) != 1) {
- ERROR("Failed to read magic from %s (%s)\n", fname, strerror(errno));
- goto out;
- }
-
- if (magic != METADATA_MAGIC) {
- magic = METADATA_MAGIC;
-
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fwrite(&magic, sizeof(magic), 1, fp) != 1) {
- ERROR("Failed to write magic to %s (%s)\n", fname, strerror(errno));
- goto out;
- }
-
- rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
- if (rc < 0) {
- ERROR("Failed to add metadata to %s: %s\n", fname, strerror(errno));
- }
-
- goto out;
- }
-
- start += sizeof(magic);
-
- while (1) {
- n = fscanf(fp, "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
- tag, &length);
-
- if (n == 2 && strcmp(tag, METADATA_EOD)) {
- /* found a tag */
- start = ftell(fp);
-
- if (!strcmp(tag, stag) && length == slength) {
- *offset = start;
- rc = 0;
- goto out;
- }
-
- start += length;
-
- if (fseek(fp, length, SEEK_CUR) < 0) {
- ERROR("Failed to seek %s (%s)\n", fname, strerror(errno));
- goto out;
- }
- } else {
- rc = metadata_add(fp, start, stag, slength, offset);
- if (rc < 0) {
- ERROR("Failed to write metadata to %s: %s\n", fname,
- strerror(errno));
- }
- goto out;
- }
- }
-
-out:
- if (fp) {
- fflush(fp);
- fclose(fp);
- }
-
- return rc;
-}
-
-static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
-{
- int fd;
- int rc = -1;
- struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- ERROR("Failed to write %zu bytes to %s to offset %" PRIu64 " (%s)\n",
- sizeof(s), fname, offset, strerror(errno));
- goto out;
- }
-
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int read_verity_state(const char *fname, off64_t offset, int *mode)
-{
- int fd = -1;
- int rc = -1;
- struct verity_state s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- ERROR("Failed to read %zu bytes from %s offset %" PRIu64 " (%s)\n",
- sizeof(s), fname, offset, strerror(errno));
- goto out;
- }
-
- if (s.header != VERITY_STATE_HEADER) {
- /* space allocated, but no state written. write default state */
- *mode = VERITY_MODE_DEFAULT;
- rc = write_verity_state(fname, offset, *mode);
- goto out;
- }
-
- if (s.version != VERITY_STATE_VERSION) {
- ERROR("Unsupported verity state version (%u)\n", s.version);
- goto out;
- }
-
- if (s.mode < VERITY_MODE_EIO ||
- s.mode > VERITY_MODE_LAST) {
- ERROR("Unsupported verity mode (%u)\n", s.mode);
- goto out;
- }
-
- *mode = s.mode;
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int compare_last_signature(struct fstab_rec *fstab, int *match)
-{
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- char *signature = NULL;
- int fd = -1;
- int rc = -1;
- uint8_t curr[SHA256_DIGEST_SIZE];
- uint8_t prev[SHA256_DIGEST_SIZE];
- off64_t offset = 0;
- uint64_t device_size;
-
- *match = 1;
-
- // get verity filesystem size
- if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
- ERROR("Failed to get filesystem size\n");
- goto out;
- }
-
- if (read_verity_metadata(device_size, fstab->blk_device, &signature, NULL) < 0) {
- ERROR("Failed to read verity signature from %s\n", fstab->mount_point);
- goto out;
- }
-
- SHA256_hash(signature, RSANUMBYTES, curr);
-
- if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
- basename(fstab->mount_point)) >= (int)sizeof(tag)) {
- ERROR("Metadata tag name too long for %s\n", fstab->mount_point);
- goto out;
- }
-
- if (metadata_find(fstab->verity_loc, tag, SHA256_DIGEST_SIZE,
- &offset) < 0) {
- goto out;
- }
-
- fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDWR | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- ERROR("Failed to open %s: %s\n", fstab->verity_loc, strerror(errno));
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev),
- offset)) != sizeof(prev)) {
- ERROR("Failed to read %zu bytes from %s offset %" PRIu64 " (%s)\n",
- sizeof(prev), fstab->verity_loc, offset, strerror(errno));
- goto out;
- }
-
- *match = !memcmp(curr, prev, SHA256_DIGEST_SIZE);
-
- if (!*match) {
- /* update current signature hash */
- if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
- offset)) != sizeof(curr)) {
- ERROR("Failed to write %zu bytes to %s offset %" PRIu64 " (%s)\n",
- sizeof(curr), fstab->verity_loc, offset, strerror(errno));
- goto out;
- }
- }
-
- rc = 0;
-
-out:
- free(signature);
-
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int get_verity_state_offset(struct fstab_rec *fstab, off64_t *offset)
-{
- char tag[METADATA_TAG_MAX_LENGTH + 1];
-
- if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s",
- basename(fstab->mount_point)) >= (int)sizeof(tag)) {
- ERROR("Metadata tag name too long for %s\n", fstab->mount_point);
- return -1;
- }
-
- return metadata_find(fstab->verity_loc, tag, sizeof(struct verity_state),
- offset);
-}
-
-static int load_verity_state(struct fstab_rec *fstab, int *mode)
-{
- char propbuf[PROPERTY_VALUE_MAX];
- int match = 0;
- off64_t offset = 0;
-
- /* use the kernel parameter if set */
- property_get("ro.boot.veritymode", propbuf, "");
-
- if (*propbuf != '\0') {
- if (!strcmp(propbuf, "enforcing")) {
- *mode = VERITY_MODE_DEFAULT;
- return 0;
- } else if (!strcmp(propbuf, "logging")) {
- *mode = VERITY_MODE_LOGGING;
- return 0;
- } else {
- INFO("Unknown value %s for veritymode; ignoring", propbuf);
- }
- }
-
- if (get_verity_state_offset(fstab, &offset) < 0) {
- /* fall back to stateless behavior */
- *mode = VERITY_MODE_EIO;
- return 0;
- }
-
- if (was_verity_restart()) {
- /* device was restarted after dm-verity detected a corrupted
- * block, so switch to logging mode */
- *mode = VERITY_MODE_LOGGING;
- return write_verity_state(fstab->verity_loc, offset, *mode);
- }
-
- if (!compare_last_signature(fstab, &match) && !match) {
- /* partition has been reflashed, reset dm-verity state */
- *mode = VERITY_MODE_DEFAULT;
- return write_verity_state(fstab->verity_loc, offset, *mode);
- }
-
- return read_verity_state(fstab->verity_loc, offset, mode);
-}
-
-int fs_mgr_load_verity_state(int *mode)
-{
- char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
- char propbuf[PROPERTY_VALUE_MAX];
- int rc = -1;
- int i;
- int current;
- struct fstab *fstab = NULL;
-
- /* return the default mode, unless any of the verified partitions are in
- * logging mode, in which case return that */
- *mode = VERITY_MODE_DEFAULT;
-
- property_get("ro.hardware", propbuf, "");
- snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
-
- fstab = fs_mgr_read_fstab(fstab_filename);
-
- if (!fstab) {
- ERROR("Failed to read %s\n", fstab_filename);
- goto out;
- }
-
- for (i = 0; i < fstab->num_entries; i++) {
- if (!fs_mgr_is_verified(&fstab->recs[i])) {
- continue;
- }
-
- rc = load_verity_state(&fstab->recs[i], ¤t);
- if (rc < 0) {
- continue;
- }
-
- if (current == VERITY_MODE_LOGGING) {
- *mode = current;
- }
- }
-
- rc = 0;
-
-out:
- if (fstab) {
- fs_mgr_free_fstab(fstab);
- }
-
- return rc;
-}
-
-int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
-{
- _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
- bool use_state = true;
- char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
- char *mount_point;
- char propbuf[PROPERTY_VALUE_MAX];
- char *status;
- int fd = -1;
- int i;
- int mode;
- int rc = -1;
- off64_t offset = 0;
- struct dm_ioctl *io = (struct dm_ioctl *) buffer;
- struct fstab *fstab = NULL;
-
- /* check if we need to store the state */
- property_get("ro.boot.veritymode", propbuf, "");
-
- if (*propbuf != '\0') {
- if (fs_mgr_load_verity_state(&mode) == -1) {
- return -1;
- }
- use_state = false; /* state is kept by the bootloader */
- }
-
- fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
-
- if (fd == -1) {
- ERROR("Error opening device mapper (%s)\n", strerror(errno));
- goto out;
- }
-
- property_get("ro.hardware", propbuf, "");
- snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
-
- fstab = fs_mgr_read_fstab(fstab_filename);
-
- if (!fstab) {
- ERROR("Failed to read %s\n", fstab_filename);
- goto out;
- }
-
- for (i = 0; i < fstab->num_entries; i++) {
- if (!fs_mgr_is_verified(&fstab->recs[i])) {
- continue;
- }
-
- if (use_state) {
- if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
- read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
- continue;
- }
- }
-
- mount_point = basename(fstab->recs[i].mount_point);
- verity_ioctl_init(io, mount_point, 0);
-
- if (ioctl(fd, DM_TABLE_STATUS, io)) {
- ERROR("Failed to query DM_TABLE_STATUS for %s (%s)\n", mount_point,
- strerror(errno));
- continue;
- }
-
- status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
-
- if (use_state && *status == 'C') {
- if (write_verity_state(fstab->recs[i].verity_loc, offset,
- VERITY_MODE_LOGGING) < 0) {
- continue;
- }
- }
-
- if (callback) {
- callback(&fstab->recs[i], mount_point, mode, *status);
- }
- }
-
- rc = 0;
-
-out:
- if (fstab) {
- fs_mgr_free_fstab(fstab);
- }
-
- if (fd) {
- close(fd);
- }
-
- return rc;
-}
-
-int fs_mgr_setup_verity(struct fstab_rec *fstab) {
-
- int retval = FS_MGR_SETUP_VERITY_FAIL;
- int fd = -1;
- int mode;
-
- char *verity_blk_name = 0;
- char *verity_table = 0;
- char *verity_table_signature = 0;
- uint64_t device_size = 0;
-
- _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
- struct dm_ioctl *io = (struct dm_ioctl *) buffer;
- char *mount_point = basename(fstab->mount_point);
-
- // get verity filesystem size
- if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
- return retval;
- }
-
- // read the verity block at the end of the block device
- // send error code up the chain so we can detect attempts to disable verity
- retval = read_verity_metadata(device_size,
- fstab->blk_device,
- &verity_table_signature,
- &verity_table);
- if (retval < 0) {
- goto out;
- }
-
- retval = FS_MGR_SETUP_VERITY_FAIL;
-
- // get the device mapper fd
- if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
- ERROR("Error opening device mapper (%s)", strerror(errno));
- goto out;
- }
-
- // create the device
- if (create_verity_device(io, mount_point, fd) < 0) {
- ERROR("Couldn't create verity device!");
- goto out;
- }
-
- // get the name of the device file
- if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) {
- ERROR("Couldn't get verity device number!");
- goto out;
- }
-
- // verify the signature on the table
- if (verify_table(verity_table_signature,
- verity_table,
- strlen(verity_table)) < 0) {
- goto out;
- }
-
- if (load_verity_state(fstab, &mode) < 0) {
- /* if accessing or updating the state failed, switch to the default
- * safe mode. This makes sure the device won't end up in an endless
- * restart loop, and no corrupted data will be exposed to userspace
- * without a warning. */
- mode = VERITY_MODE_EIO;
- }
-
- INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, mode);
-
- // load the verity mapping table
- if (load_verity_table(io, mount_point, device_size, fd, verity_table,
- mode) < 0) {
- goto out;
- }
-
- // activate the device
- if (resume_verity_table(io, mount_point, fd) < 0) {
- goto out;
- }
-
- // mark the underlying block device as read-only
- fs_mgr_set_blk_ro(fstab->blk_device);
-
- // assign the new verity block device as the block device
- free(fstab->blk_device);
- fstab->blk_device = verity_blk_name;
- verity_blk_name = 0;
-
- // make sure we've set everything up properly
- if (test_access(fstab->blk_device) < 0) {
- goto out;
- }
-
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- free(verity_table);
- free(verity_table_signature);
- free(verity_blk_name);
-
- return retval;
-}
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
new file mode 100644
index 0000000..1ec4540
--- /dev/null
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -0,0 +1,1044 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <crypto_utils/android_pubkey.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+#include <private/android_filesystem_config.h>
+
+#include "fec/io.h"
+
+#include "fs_mgr.h"
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_dm_ioctl.h"
+#include "fs_mgr_priv_verity.h"
+
+#define FSTAB_PREFIX "/fstab."
+
+#define VERITY_TABLE_RSA_KEY "/verity_key"
+#define VERITY_TABLE_HASH_IDX 8
+#define VERITY_TABLE_SALT_IDX 9
+
+#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
+#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
+#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
+
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+ "use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 \
+ " fec_roots %u " VERITY_TABLE_OPT_IGNZERO
+#define VERITY_TABLE_OPT_FEC_ARGS 9
+
+#define METADATA_MAGIC 0x01564c54
+#define METADATA_TAG_MAX_LENGTH 63
+#define METADATA_EOD "eod"
+
+#define VERITY_LASTSIG_TAG "verity_lastsig"
+
+#define VERITY_STATE_TAG "verity_state"
+#define VERITY_STATE_HEADER 0x83c0ae9d
+#define VERITY_STATE_VERSION 1
+
+#define VERITY_KMSG_RESTART "dm-verity device corrupted"
+#define VERITY_KMSG_BUFSIZE 1024
+
+#define READ_BUF_SIZE 4096
+
+#define __STRINGIFY(x) #x
+#define STRINGIFY(x) __STRINGIFY(x)
+
+struct verity_state {
+ uint32_t header;
+ uint32_t version;
+ int32_t mode;
+};
+
+extern struct fs_info info;
+
+static RSA *load_key(const char *path)
+{
+ uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
+
+ FILE* f = fopen(path, "r");
+ if (!f) {
+ LERROR << "Can't open " << path;
+ return NULL;
+ }
+
+ if (!fread(key_data, sizeof(key_data), 1, f)) {
+ LERROR << "Could not read key!";
+ fclose(f);
+ return NULL;
+ }
+
+ fclose(f);
+
+ RSA* key = NULL;
+ if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
+ LERROR << "Could not parse key!";
+ return NULL;
+ }
+
+ return key;
+}
+
+static int verify_table(const uint8_t *signature, size_t signature_size,
+ const char *table, uint32_t table_length)
+{
+ RSA *key;
+ uint8_t hash_buf[SHA256_DIGEST_LENGTH];
+ int retval = -1;
+
+ // Hash the table
+ SHA256((uint8_t*)table, table_length, hash_buf);
+
+ // Now get the public key from the keyfile
+ key = load_key(VERITY_TABLE_RSA_KEY);
+ if (!key) {
+ LERROR << "Couldn't load verity keys";
+ goto out;
+ }
+
+ // verify the result
+ if (!RSA_verify(NID_sha256, hash_buf, sizeof(hash_buf), signature,
+ signature_size, key)) {
+ LERROR << "Couldn't verify table";
+ goto out;
+ }
+
+ retval = 0;
+
+out:
+ RSA_free(key);
+ return retval;
+}
+
+static int verify_verity_signature(const struct fec_verity_metadata& verity)
+{
+ if (verify_table(verity.signature, sizeof(verity.signature),
+ verity.table, verity.table_length) == 0 ||
+ verify_table(verity.ecc_signature, sizeof(verity.ecc_signature),
+ verity.table, verity.table_length) == 0) {
+ return 0;
+ }
+
+ return -1;
+}
+
+static int invalidate_table(char *table, size_t table_length)
+{
+ size_t n = 0;
+ size_t idx = 0;
+ size_t cleared = 0;
+
+ while (n < table_length) {
+ if (table[n++] == ' ') {
+ ++idx;
+ }
+
+ if (idx != VERITY_TABLE_HASH_IDX && idx != VERITY_TABLE_SALT_IDX) {
+ continue;
+ }
+
+ while (n < table_length && table[n] != ' ') {
+ table[n++] = '0';
+ }
+
+ if (++cleared == 2) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+struct verity_table_params {
+ char *table;
+ int mode;
+ struct fec_ecc_metadata ecc;
+ const char *ecc_dev;
+};
+
+typedef bool (*format_verity_table_func)(char *buf, const size_t bufsize,
+ const struct verity_table_params *params);
+
+static bool format_verity_table(char *buf, const size_t bufsize,
+ const struct verity_table_params *params)
+{
+ const char *mode_flag = NULL;
+ int res = -1;
+
+ if (params->mode == VERITY_MODE_RESTART) {
+ mode_flag = VERITY_TABLE_OPT_RESTART;
+ } else if (params->mode == VERITY_MODE_LOGGING) {
+ mode_flag = VERITY_TABLE_OPT_LOGGING;
+ }
+
+ if (params->ecc.valid) {
+ if (mode_flag) {
+ res = snprintf(buf, bufsize,
+ "%s %u %s " VERITY_TABLE_OPT_FEC_FORMAT,
+ params->table, 1 + VERITY_TABLE_OPT_FEC_ARGS, mode_flag, params->ecc_dev,
+ params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+ } else {
+ res = snprintf(buf, bufsize,
+ "%s %u " VERITY_TABLE_OPT_FEC_FORMAT,
+ params->table, VERITY_TABLE_OPT_FEC_ARGS, params->ecc_dev,
+ params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+ }
+ } else if (mode_flag) {
+ res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table,
+ mode_flag);
+ } else {
+ res = snprintf(buf, bufsize, "%s 1 " VERITY_TABLE_OPT_IGNZERO, params->table);
+ }
+
+ if (res < 0 || (size_t)res >= bufsize) {
+ LERROR << "Error building verity table; insufficient buffer size?";
+ return false;
+ }
+
+ return true;
+}
+
+static bool format_legacy_verity_table(char *buf, const size_t bufsize,
+ const struct verity_table_params *params)
+{
+ int res;
+
+ if (params->mode == VERITY_MODE_EIO) {
+ res = strlcpy(buf, params->table, bufsize);
+ } else {
+ res = snprintf(buf, bufsize, "%s %d", params->table, params->mode);
+ }
+
+ if (res < 0 || (size_t)res >= bufsize) {
+ LERROR << "Error building verity table; insufficient buffer size?";
+ return false;
+ }
+
+ return true;
+}
+
+static int load_verity_table(struct dm_ioctl *io, const std::string &name,
+ uint64_t device_size, int fd,
+ const struct verity_table_params *params, format_verity_table_func format)
+{
+ char *verity_params;
+ char *buffer = (char*) io;
+ size_t bufsize;
+
+ fs_mgr_verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
+
+ struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+ // set tgt arguments
+ io->target_count = 1;
+ tgt->status = 0;
+ tgt->sector_start = 0;
+ tgt->length = device_size / 512;
+ strcpy(tgt->target_type, "verity");
+
+ // build the verity params
+ verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+ bufsize = DM_BUF_SIZE - (verity_params - buffer);
+
+ if (!format(verity_params, bufsize, params)) {
+ LERROR << "Failed to format verity parameters";
+ return -1;
+ }
+
+ LINFO << "loading verity table: '" << verity_params << "'";
+
+ // set next target boundary
+ verity_params += strlen(verity_params) + 1;
+ verity_params = (char*)(((uintptr_t)verity_params + 7) & ~7);
+ tgt->next = verity_params - buffer;
+
+ // send the ioctl to load the verity table
+ if (ioctl(fd, DM_TABLE_LOAD, io)) {
+ PERROR << "Error loading verity table";
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_verity_restart(const char *fname)
+{
+ char buffer[VERITY_KMSG_BUFSIZE + 1];
+ int fd;
+ int rc = 0;
+ ssize_t size;
+ struct stat s;
+
+ fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
+
+ if (fd == -1) {
+ if (errno != ENOENT) {
+ PERROR << "Failed to open " << fname;
+ }
+ goto out;
+ }
+
+ if (fstat(fd, &s) == -1) {
+ PERROR << "Failed to fstat " << fname;
+ goto out;
+ }
+
+ size = VERITY_KMSG_BUFSIZE;
+
+ if (size > s.st_size) {
+ size = s.st_size;
+ }
+
+ if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
+ PERROR << "Failed to lseek " << (intmax_t)(s.st_size - size) << " " << fname;
+ goto out;
+ }
+
+ if (!android::base::ReadFully(fd, buffer, size)) {
+ PERROR << "Failed to read " << size << " bytes from " << fname;
+ goto out;
+ }
+
+ buffer[size] = '\0';
+
+ if (strstr(buffer, VERITY_KMSG_RESTART) != NULL) {
+ rc = 1;
+ }
+
+out:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return rc;
+}
+
+static int was_verity_restart()
+{
+ static const char *files[] = {
+ "/sys/fs/pstore/console-ramoops",
+ "/proc/last_kmsg",
+ NULL
+ };
+ int i;
+
+ for (i = 0; files[i]; ++i) {
+ if (check_verity_restart(files[i])) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int metadata_add(FILE *fp, long start, const char *tag,
+ unsigned int length, off64_t *offset)
+{
+ if (fseek(fp, start, SEEK_SET) < 0 ||
+ fprintf(fp, "%s %u\n", tag, length) < 0) {
+ return -1;
+ }
+
+ *offset = ftell(fp);
+
+ if (fseek(fp, length, SEEK_CUR) < 0 ||
+ fprintf(fp, METADATA_EOD " 0\n") < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int metadata_find(const char *fname, const char *stag,
+ unsigned int slength, off64_t *offset)
+{
+ FILE *fp = NULL;
+ char tag[METADATA_TAG_MAX_LENGTH + 1];
+ int rc = -1;
+ int n;
+ long start = 0x4000; /* skip cryptfs metadata area */
+ uint32_t magic;
+ unsigned int length = 0;
+
+ if (!fname) {
+ return -1;
+ }
+
+ fp = fopen(fname, "r+");
+
+ if (!fp) {
+ PERROR << "Failed to open " << fname;
+ goto out;
+ }
+
+ /* check magic */
+ if (fseek(fp, start, SEEK_SET) < 0 ||
+ fread(&magic, sizeof(magic), 1, fp) != 1) {
+ PERROR << "Failed to read magic from " << fname;
+ goto out;
+ }
+
+ if (magic != METADATA_MAGIC) {
+ magic = METADATA_MAGIC;
+
+ if (fseek(fp, start, SEEK_SET) < 0 ||
+ fwrite(&magic, sizeof(magic), 1, fp) != 1) {
+ PERROR << "Failed to write magic to " << fname;
+ goto out;
+ }
+
+ rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
+ if (rc < 0) {
+ PERROR << "Failed to add metadata to " << fname;
+ }
+
+ goto out;
+ }
+
+ start += sizeof(magic);
+
+ while (1) {
+ n = fscanf(fp, "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
+ tag, &length);
+
+ if (n == 2 && strcmp(tag, METADATA_EOD)) {
+ /* found a tag */
+ start = ftell(fp);
+
+ if (!strcmp(tag, stag) && length == slength) {
+ *offset = start;
+ rc = 0;
+ goto out;
+ }
+
+ start += length;
+
+ if (fseek(fp, length, SEEK_CUR) < 0) {
+ PERROR << "Failed to seek " << fname;
+ goto out;
+ }
+ } else {
+ rc = metadata_add(fp, start, stag, slength, offset);
+ if (rc < 0) {
+ PERROR << "Failed to write metadata to " << fname;
+ }
+ goto out;
+ }
+ }
+
+out:
+ if (fp) {
+ fflush(fp);
+ fclose(fp);
+ }
+
+ return rc;
+}
+
+static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
+{
+ int fd;
+ int rc = -1;
+ struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
+
+ fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
+
+ if (fd == -1) {
+ PERROR << "Failed to open " << fname;
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
+ PERROR << "Failed to write " << sizeof(s) << " bytes to " << fname
+ << " to offset " << offset;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return rc;
+}
+
+static int read_verity_state(const char *fname, off64_t offset, int *mode)
+{
+ int fd = -1;
+ int rc = -1;
+ struct verity_state s;
+
+ fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
+
+ if (fd == -1) {
+ PERROR << "Failed to open " << fname;
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
+ PERROR << "Failed to read " << sizeof(s) << " bytes from " << fname
+ << " offset " << offset;
+ goto out;
+ }
+
+ if (s.header != VERITY_STATE_HEADER) {
+ /* space allocated, but no state written. write default state */
+ *mode = VERITY_MODE_DEFAULT;
+ rc = write_verity_state(fname, offset, *mode);
+ goto out;
+ }
+
+ if (s.version != VERITY_STATE_VERSION) {
+ LERROR << "Unsupported verity state version (" << s.version << ")";
+ goto out;
+ }
+
+ if (s.mode < VERITY_MODE_EIO ||
+ s.mode > VERITY_MODE_LAST) {
+ LERROR << "Unsupported verity mode (" << s.mode << ")";
+ goto out;
+ }
+
+ *mode = s.mode;
+ rc = 0;
+
+out:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return rc;
+}
+
+static int read_partition(const char *path, uint64_t size)
+{
+ char buf[READ_BUF_SIZE];
+ ssize_t size_read;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC)));
+
+ if (fd == -1) {
+ PERROR << "Failed to open " << path;
+ return -errno;
+ }
+
+ while (size) {
+ size_read = TEMP_FAILURE_RETRY(read(fd, buf, READ_BUF_SIZE));
+ if (size_read == -1) {
+ PERROR << "Error in reading partition " << path;
+ return -errno;
+ }
+ size -= size_read;
+ }
+
+ return 0;
+}
+
+static int compare_last_signature(struct fstab_rec *fstab, int *match)
+{
+ char tag[METADATA_TAG_MAX_LENGTH + 1];
+ int fd = -1;
+ int rc = -1;
+ off64_t offset = 0;
+ struct fec_handle *f = NULL;
+ struct fec_verity_metadata verity;
+ uint8_t curr[SHA256_DIGEST_LENGTH];
+ uint8_t prev[SHA256_DIGEST_LENGTH];
+
+ *match = 1;
+
+ if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+ FEC_DEFAULT_ROOTS) == -1) {
+ PERROR << "Failed to open '" << fstab->blk_device << "'";
+ return rc;
+ }
+
+ // read verity metadata
+ if (fec_verity_get_metadata(f, &verity) == -1) {
+ PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+ goto out;
+ }
+
+ SHA256(verity.signature, sizeof(verity.signature), curr);
+
+ if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
+ basename(fstab->mount_point)) >= (int)sizeof(tag)) {
+ LERROR << "Metadata tag name too long for " << fstab->mount_point;
+ goto out;
+ }
+
+ if (metadata_find(fstab->verity_loc, tag, SHA256_DIGEST_LENGTH,
+ &offset) < 0) {
+ goto out;
+ }
+
+ fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDWR | O_SYNC | O_CLOEXEC));
+
+ if (fd == -1) {
+ PERROR << "Failed to open " << fstab->verity_loc;
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev),
+ offset)) != sizeof(prev)) {
+ PERROR << "Failed to read " << sizeof(prev) << " bytes from "
+ << fstab->verity_loc << " offset " << offset;
+ goto out;
+ }
+
+ *match = !memcmp(curr, prev, SHA256_DIGEST_LENGTH);
+
+ if (!*match) {
+ /* update current signature hash */
+ if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
+ offset)) != sizeof(curr)) {
+ PERROR << "Failed to write " << sizeof(curr) << " bytes to "
+ << fstab->verity_loc << " offset " << offset;
+ goto out;
+ }
+ }
+
+ rc = 0;
+
+out:
+ fec_close(f);
+ return rc;
+}
+
+static int get_verity_state_offset(struct fstab_rec *fstab, off64_t *offset)
+{
+ char tag[METADATA_TAG_MAX_LENGTH + 1];
+
+ if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s",
+ basename(fstab->mount_point)) >= (int)sizeof(tag)) {
+ LERROR << "Metadata tag name too long for " << fstab->mount_point;
+ return -1;
+ }
+
+ return metadata_find(fstab->verity_loc, tag, sizeof(struct verity_state),
+ offset);
+}
+
+static int load_verity_state(struct fstab_rec *fstab, int *mode)
+{
+ char propbuf[PROPERTY_VALUE_MAX];
+ int match = 0;
+ off64_t offset = 0;
+
+ /* unless otherwise specified, use EIO mode */
+ *mode = VERITY_MODE_EIO;
+
+ /* use the kernel parameter if set */
+ property_get("ro.boot.veritymode", propbuf, "");
+
+ if (*propbuf != '\0') {
+ if (!strcmp(propbuf, "enforcing")) {
+ *mode = VERITY_MODE_DEFAULT;
+ }
+ return 0;
+ }
+
+ if (get_verity_state_offset(fstab, &offset) < 0) {
+ /* fall back to stateless behavior */
+ return 0;
+ }
+
+ if (was_verity_restart()) {
+ /* device was restarted after dm-verity detected a corrupted
+ * block, so use EIO mode */
+ return write_verity_state(fstab->verity_loc, offset, *mode);
+ }
+
+ if (!compare_last_signature(fstab, &match) && !match) {
+ /* partition has been reflashed, reset dm-verity state */
+ *mode = VERITY_MODE_DEFAULT;
+ return write_verity_state(fstab->verity_loc, offset, *mode);
+ }
+
+ return read_verity_state(fstab->verity_loc, offset, mode);
+}
+
+int fs_mgr_load_verity_state(int *mode)
+{
+ char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+ char propbuf[PROPERTY_VALUE_MAX];
+ int rc = -1;
+ int i;
+ int current;
+ struct fstab *fstab = NULL;
+
+ /* return the default mode, unless any of the verified partitions are in
+ * logging mode, in which case return that */
+ *mode = VERITY_MODE_DEFAULT;
+
+ property_get("ro.hardware", propbuf, "");
+ snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
+
+ fstab = fs_mgr_read_fstab(fstab_filename);
+
+ if (!fstab) {
+ LERROR << "Failed to read " << fstab_filename;
+ goto out;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ if (!fs_mgr_is_verified(&fstab->recs[i])) {
+ continue;
+ }
+
+ rc = load_verity_state(&fstab->recs[i], ¤t);
+ if (rc < 0) {
+ continue;
+ }
+
+ if (current != VERITY_MODE_DEFAULT) {
+ *mode = current;
+ break;
+ }
+ }
+
+ rc = 0;
+
+out:
+ if (fstab) {
+ fs_mgr_free_fstab(fstab);
+ }
+
+ return rc;
+}
+
+int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
+{
+ alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
+ bool system_root = false;
+ char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+ std::string mount_point;
+ char propbuf[PROPERTY_VALUE_MAX];
+ const char *status;
+ int fd = -1;
+ int i;
+ int mode;
+ int rc = -1;
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+ struct fstab *fstab = NULL;
+
+ if (!callback) {
+ return -1;
+ }
+
+ if (fs_mgr_load_verity_state(&mode) == -1) {
+ return -1;
+ }
+
+ fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+
+ if (fd == -1) {
+ PERROR << "Error opening device mapper";
+ goto out;
+ }
+
+ property_get("ro.hardware", propbuf, "");
+ snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
+
+ property_get("ro.build.system_root_image", propbuf, "");
+ system_root = !strcmp(propbuf, "true");
+
+ fstab = fs_mgr_read_fstab(fstab_filename);
+
+ if (!fstab) {
+ LERROR << "Failed to read " << fstab_filename;
+ goto out;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ if (!fs_mgr_is_verified(&fstab->recs[i])) {
+ continue;
+ }
+
+ if (system_root && !strcmp(fstab->recs[i].mount_point, "/")) {
+ mount_point = "system";
+ } else {
+ mount_point = basename(fstab->recs[i].mount_point);
+ }
+
+ fs_mgr_verity_ioctl_init(io, mount_point, 0);
+
+ if (ioctl(fd, DM_TABLE_STATUS, io)) {
+ if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
+ status = "V";
+ } else {
+ PERROR << "Failed to query DM_TABLE_STATUS for "
+ << mount_point.c_str();
+ continue;
+ }
+ }
+
+ status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
+
+ if (*status == 'C' || *status == 'V') {
+ callback(&fstab->recs[i], mount_point.c_str(), mode, *status);
+ }
+ }
+
+ rc = 0;
+
+out:
+ if (fstab) {
+ fs_mgr_free_fstab(fstab);
+ }
+
+ if (fd) {
+ close(fd);
+ }
+
+ return rc;
+}
+
+static void update_verity_table_blk_device(char *blk_device, char **table)
+{
+ std::string result, word;
+ auto tokens = android::base::Split(*table, " ");
+
+ for (const auto& token : tokens) {
+ if (android::base::StartsWith(token, "/dev/block/") &&
+ android::base::StartsWith(blk_device, token.c_str())) {
+ word = blk_device;
+ } else {
+ word = token;
+ }
+
+ if (result.empty()) {
+ result = word;
+ } else {
+ result += " " + word;
+ }
+ }
+
+ if (result.empty()) {
+ return;
+ }
+
+ free(*table);
+ *table = strdup(result.c_str());
+}
+
+int fs_mgr_setup_verity(struct fstab_rec *fstab, bool verify_dev)
+{
+ int retval = FS_MGR_SETUP_VERITY_FAIL;
+ int fd = -1;
+ std::string verity_blk_name;
+ struct fec_handle *f = NULL;
+ struct fec_verity_metadata verity;
+ struct verity_table_params params = { .table = NULL };
+
+ alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+ const std::string mount_point(basename(fstab->mount_point));
+ bool verified_at_boot = false;
+
+ if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+ FEC_DEFAULT_ROOTS) < 0) {
+ PERROR << "Failed to open '" << fstab->blk_device << "'";
+ return retval;
+ }
+
+ // read verity metadata
+ if (fec_verity_get_metadata(f, &verity) < 0) {
+ PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+ goto out;
+ }
+
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+ if (verity.disabled) {
+ retval = FS_MGR_SETUP_VERITY_DISABLED;
+ LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG";
+ goto out;
+ }
+#endif
+
+ // read ecc metadata
+ if (fec_ecc_get_metadata(f, ¶ms.ecc) < 0) {
+ params.ecc.valid = false;
+ }
+
+ params.ecc_dev = fstab->blk_device;
+
+ // get the device mapper fd
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ PERROR << "Error opening device mapper";
+ goto out;
+ }
+
+ // create the device
+ if (!fs_mgr_create_verity_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)) {
+ LERROR << "Couldn't get verity device number!";
+ goto out;
+ }
+
+ if (load_verity_state(fstab, ¶ms.mode) < 0) {
+ /* if accessing or updating the state failed, switch to the default
+ * safe mode. This makes sure the device won't end up in an endless
+ * restart loop, and no corrupted data will be exposed to userspace
+ * without a warning. */
+ params.mode = VERITY_MODE_EIO;
+ }
+
+ if (!verity.table) {
+ goto out;
+ }
+
+ params.table = strdup(verity.table);
+ if (!params.table) {
+ goto out;
+ }
+
+ // verify the signature on the table
+ if (verify_verity_signature(verity) < 0) {
+ if (params.mode == VERITY_MODE_LOGGING) {
+ // the user has been warned, allow mounting without dm-verity
+ retval = FS_MGR_SETUP_VERITY_SUCCESS;
+ goto out;
+ }
+
+ // invalidate root hash and salt to trigger device-specific recovery
+ if (invalidate_table(params.table, verity.table_length) < 0) {
+ goto out;
+ }
+ }
+
+ LINFO << "Enabling dm-verity for " << mount_point.c_str()
+ << " (mode " << params.mode << ")";
+
+ if (fstab->fs_mgr_flags & MF_SLOTSELECT) {
+ // Update the verity params using the actual block device path
+ update_verity_table_blk_device(fstab->blk_device, ¶ms.table);
+ }
+
+ // load the verity mapping table
+ if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_verity_table) == 0) {
+ goto loaded;
+ }
+
+ if (params.ecc.valid) {
+ // kernel may not support error correction, try without
+ LINFO << "Disabling error correction for " << mount_point.c_str();
+ params.ecc.valid = false;
+
+ if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_verity_table) == 0) {
+ goto loaded;
+ }
+ }
+
+ // try the legacy format for backwards compatibility
+ if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_legacy_verity_table) == 0) {
+ goto loaded;
+ }
+
+ if (params.mode != VERITY_MODE_EIO) {
+ // as a last resort, EIO mode should always be supported
+ LINFO << "Falling back to EIO mode for " << mount_point.c_str();
+ params.mode = VERITY_MODE_EIO;
+
+ if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_legacy_verity_table) == 0) {
+ goto loaded;
+ }
+ }
+
+ LERROR << "Failed to load verity table for " << mount_point.c_str();
+ goto out;
+
+loaded:
+
+ // activate the device
+ if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+ goto out;
+ }
+
+ // mark the underlying block device as read-only
+ fs_mgr_set_blk_ro(fstab->blk_device);
+
+ // Verify the entire partition in one go
+ // If there is an error, allow it to mount as a normal verity partition.
+ if (fstab->fs_mgr_flags & MF_VERIFYATBOOT) {
+ LINFO << "Verifying partition " << fstab->blk_device << " at boot";
+ int err = read_partition(verity_blk_name.c_str(), verity.data_size);
+ if (!err) {
+ LINFO << "Verified verity partition "
+ << fstab->blk_device << " at boot";
+ verified_at_boot = true;
+ }
+ }
+
+ // assign the new verity block device as the block device
+ if (!verified_at_boot) {
+ free(fstab->blk_device);
+ fstab->blk_device = strdup(verity_blk_name.c_str());
+ } else if (!fs_mgr_destroy_verity_device(io, mount_point, fd)) {
+ LERROR << "Failed to remove verity device " << mount_point.c_str();
+ goto out;
+ }
+
+ // make sure we've set everything up properly
+ if (verify_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
+ goto out;
+ }
+
+ retval = FS_MGR_SETUP_VERITY_SUCCESS;
+
+out:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ fec_close(f);
+ free(params.table);
+
+ return retval;
+}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index c58a888..d959798 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -17,7 +17,9 @@
#ifndef __CORE_FS_MGR_H
#define __CORE_FS_MGR_H
+#include <stdio.h>
#include <stdint.h>
+#include <stdbool.h>
#include <linux/dm-ioctl.h>
// Magic number at start of verity metadata
@@ -40,6 +42,13 @@
VERITY_MODE_DEFAULT = VERITY_MODE_RESTART
};
+// Mount modes
+enum mount_mode {
+ MOUNT_MODE_DEFAULT = 0,
+ MOUNT_MODE_EARLY = 1,
+ MOUNT_MODE_LATE = 2
+};
+
/*
* The entries must be kept in the same order as they were seen in the fstab.
* Unless explicitly requested, a lookup on mount point should always
@@ -64,28 +73,35 @@
char *label;
int partnum;
int swap_prio;
+ int max_comp_streams;
unsigned int zram_size;
+ uint64_t reserved_size;
+ unsigned int file_encryption_mode;
+ unsigned int erase_blk_size;
+ unsigned int logical_blk_size;
};
// Callback function for verity status
typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
const char *mount_point, int mode, int status);
+struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file);
struct fstab *fs_mgr_read_fstab(const char *fstab_path);
void fs_mgr_free_fstab(struct fstab *fstab);
-#define FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED 5
-#define FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED 4
-#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 3
-#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 2
-#define FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED 1
-#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 0
-#define FS_MGR_MNTALL_FAIL -1
-int fs_mgr_mount_all(struct fstab *fstab);
+#define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
+#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
+#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 3
+#define FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED 2
+#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
+#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
+#define FS_MGR_MNTALL_FAIL (-1)
+int fs_mgr_mount_all(struct fstab *fstab, int mount_mode);
-#define FS_MGR_DOMNT_FAILED -1
-#define FS_MGR_DOMNT_BUSY -2
-int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
+#define FS_MGR_DOMNT_FAILED (-1)
+#define FS_MGR_DOMNT_BUSY (-2)
+
+int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
char *tmp_mount_point);
int fs_mgr_do_tmpfs_mount(char *n_name);
int fs_mgr_unmount_all(struct fstab *fstab);
@@ -102,11 +118,25 @@
int fs_mgr_is_verified(const struct fstab_rec *fstab);
int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
+const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab);
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
+int fs_mgr_is_notrim(struct fstab_rec *fstab);
+int fs_mgr_is_formattable(struct fstab_rec *fstab);
+int fs_mgr_is_nofail(struct fstab_rec *fstab);
+int fs_mgr_is_latemount(struct fstab_rec *fstab);
+int fs_mgr_is_quota(struct fstab_rec *fstab);
int fs_mgr_swapon_all(struct fstab *fstab);
+
+int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
+
+#define FS_MGR_EARLY_SETUP_VERITY_NO_VERITY -2
+#define FS_MGR_EARLY_SETUP_VERITY_FAIL -1
+#define FS_MGR_EARLY_SETUP_VERITY_SUCCESS 0
+int fs_mgr_early_setup_verity(struct fstab_rec *fstab);
+
#ifdef __cplusplus
}
#endif
#endif /* __CORE_FS_MGR_H */
-
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
new file mode 100644
index 0000000..3f78955
--- /dev/null
+++ b/gatekeeperd/Android.mk
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
+LOCAL_SRC_FILES := \
+ SoftGateKeeperDevice.cpp \
+ IGateKeeperService.cpp \
+ gatekeeperd.cpp \
+ IUserManager.cpp
+
+LOCAL_MODULE := gatekeeperd
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libgatekeeper \
+ liblog \
+ libhardware \
+ libbase \
+ libutils \
+ libcrypto \
+ libkeystore_binder
+LOCAL_STATIC_LIBRARIES := libscrypt_static
+LOCAL_C_INCLUDES := external/scrypt/lib/crypto
+LOCAL_INIT_RC := gatekeeperd.rc
+include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp
new file mode 100644
index 0000000..95fbfd1
--- /dev/null
+++ b/gatekeeperd/IGateKeeperService.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+#define LOG_TAG "GateKeeperService"
+#include <utils/Log.h>
+
+#include "IGateKeeperService.h"
+
+namespace android {
+
+const android::String16 IGateKeeperService::descriptor("android.service.gatekeeper.IGateKeeperService");
+const android::String16& IGateKeeperService::getInterfaceDescriptor() const {
+ return IGateKeeperService::descriptor;
+}
+
+status_t BnGateKeeperService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ switch(code) {
+ case ENROLL: {
+ CHECK_INTERFACE(IGateKeeperService, data, reply);
+ uint32_t uid = data.readInt32();
+
+ ssize_t currentPasswordHandleSize = data.readInt32();
+ const uint8_t *currentPasswordHandle =
+ static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
+ if (!currentPasswordHandle) currentPasswordHandleSize = 0;
+
+ ssize_t currentPasswordSize = data.readInt32();
+ const uint8_t *currentPassword =
+ static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
+ if (!currentPassword) currentPasswordSize = 0;
+
+ ssize_t desiredPasswordSize = data.readInt32();
+ const uint8_t *desiredPassword =
+ static_cast<const uint8_t *>(data.readInplace(desiredPasswordSize));
+ if (!desiredPassword) desiredPasswordSize = 0;
+
+ uint8_t *out = NULL;
+ uint32_t outSize = 0;
+ int ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize,
+ currentPassword, currentPasswordSize, desiredPassword,
+ desiredPasswordSize, &out, &outSize);
+
+ reply->writeNoException();
+ reply->writeInt32(1);
+ if (ret == 0 && outSize > 0 && out != NULL) {
+ reply->writeInt32(GATEKEEPER_RESPONSE_OK);
+ reply->writeInt32(0);
+ reply->writeInt32(outSize);
+ reply->writeInt32(outSize);
+ void *buf = reply->writeInplace(outSize);
+ memcpy(buf, out, outSize);
+ delete[] out;
+ } else if (ret > 0) {
+ reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
+ reply->writeInt32(ret);
+ } else {
+ reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
+ }
+ return NO_ERROR;
+ }
+ case VERIFY: {
+ CHECK_INTERFACE(IGateKeeperService, data, reply);
+ uint32_t uid = data.readInt32();
+ ssize_t currentPasswordHandleSize = data.readInt32();
+ const uint8_t *currentPasswordHandle =
+ static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
+ if (!currentPasswordHandle) currentPasswordHandleSize = 0;
+
+ ssize_t currentPasswordSize = data.readInt32();
+ const uint8_t *currentPassword =
+ static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
+ if (!currentPassword) currentPasswordSize = 0;
+
+ bool request_reenroll = false;
+ int ret = verify(uid, (uint8_t *) currentPasswordHandle,
+ currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize,
+ &request_reenroll);
+
+ reply->writeNoException();
+ reply->writeInt32(1);
+ if (ret == 0) {
+ reply->writeInt32(GATEKEEPER_RESPONSE_OK);
+ reply->writeInt32(request_reenroll ? 1 : 0);
+ reply->writeInt32(0); // no payload returned from this call
+ } else if (ret > 0) {
+ reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
+ reply->writeInt32(ret);
+ } else {
+ reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
+ }
+ return NO_ERROR;
+ }
+ case VERIFY_CHALLENGE: {
+ CHECK_INTERFACE(IGateKeeperService, data, reply);
+ uint32_t uid = data.readInt32();
+ uint64_t challenge = data.readInt64();
+ ssize_t currentPasswordHandleSize = data.readInt32();
+ const uint8_t *currentPasswordHandle =
+ static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
+ if (!currentPasswordHandle) currentPasswordHandleSize = 0;
+
+ ssize_t currentPasswordSize = data.readInt32();
+ const uint8_t *currentPassword =
+ static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
+ if (!currentPassword) currentPasswordSize = 0;
+
+
+ uint8_t *out = NULL;
+ uint32_t outSize = 0;
+ bool request_reenroll = false;
+ int ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle,
+ currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize,
+ &out, &outSize, &request_reenroll);
+ reply->writeNoException();
+ reply->writeInt32(1);
+ if (ret == 0 && outSize > 0 && out != NULL) {
+ reply->writeInt32(GATEKEEPER_RESPONSE_OK);
+ reply->writeInt32(request_reenroll ? 1 : 0);
+ reply->writeInt32(outSize);
+ reply->writeInt32(outSize);
+ void *buf = reply->writeInplace(outSize);
+ memcpy(buf, out, outSize);
+ delete[] out;
+ } else if (ret > 0) {
+ reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
+ reply->writeInt32(ret);
+ } else {
+ reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
+ }
+ return NO_ERROR;
+ }
+ case GET_SECURE_USER_ID: {
+ CHECK_INTERFACE(IGateKeeperService, data, reply);
+ uint32_t uid = data.readInt32();
+ uint64_t sid = getSecureUserId(uid);
+ reply->writeNoException();
+ reply->writeInt64(sid);
+ return NO_ERROR;
+ }
+ case CLEAR_SECURE_USER_ID: {
+ CHECK_INTERFACE(IGateKeeperService, data, reply);
+ uint32_t uid = data.readInt32();
+ clearSecureUserId(uid);
+ reply->writeNoException();
+ return NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+};
+
+
+}; // namespace android
diff --git a/gatekeeperd/IGateKeeperService.h b/gatekeeperd/IGateKeeperService.h
new file mode 100644
index 0000000..f070486
--- /dev/null
+++ b/gatekeeperd/IGateKeeperService.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IGATEKEEPER_SERVICE_H_
+#define IGATEKEEPER_SERVICE_H_
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+/*
+ * This must be kept manually in sync with frameworks/base's IGateKeeperService.aidl
+ */
+class IGateKeeperService : public IInterface {
+public:
+ enum {
+ ENROLL = IBinder::FIRST_CALL_TRANSACTION + 0,
+ VERIFY = IBinder::FIRST_CALL_TRANSACTION + 1,
+ VERIFY_CHALLENGE = IBinder::FIRST_CALL_TRANSACTION + 2,
+ GET_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 3,
+ CLEAR_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 4,
+ };
+
+ enum {
+ GATEKEEPER_RESPONSE_OK = 0,
+ GATEKEEPER_RESPONSE_RETRY = 1,
+ GATEKEEPER_RESPONSE_ERROR = -1,
+ };
+
+ // DECLARE_META_INTERFACE - C++ client interface not needed
+ static const android::String16 descriptor;
+ virtual const android::String16& getInterfaceDescriptor() const;
+ IGateKeeperService() {}
+ virtual ~IGateKeeperService() {}
+
+ /**
+ * Enrolls a password with the GateKeeper. Returns 0 on success, negative on failure.
+ * Returns:
+ * - 0 on success
+ * - A timestamp T > 0 if the call has failed due to throttling and should not
+ * be reattempted until T milliseconds have elapsed
+ * - -1 on failure
+ */
+ virtual int enroll(uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) = 0;
+
+ /**
+ * Verifies a password previously enrolled with the GateKeeper.
+ * Returns:
+ * - 0 on success
+ * - A timestamp T > 0 if the call has failed due to throttling and should not
+ * be reattempted until T milliseconds have elapsed
+ * - -1 on failure
+ */
+ virtual int verify(uint32_t uid, const uint8_t *enrolled_password_handle,
+ uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ bool *request_reenroll) = 0;
+
+ /**
+ * Verifies a password previously enrolled with the GateKeeper.
+ * Returns:
+ * - 0 on success
+ * - A timestamp T > 0 if the call has failed due to throttling and should not
+ * be reattempted until T milliseconds have elapsed
+ * - -1 on failure
+ */
+ virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) = 0;
+ /**
+ * Returns the secure user ID for the provided android user
+ */
+ virtual uint64_t getSecureUserId(uint32_t uid) = 0;
+
+ /**
+ * Clears the secure user ID associated with the user.
+ */
+ virtual void clearSecureUserId(uint32_t uid) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnGateKeeperService: public BnInterface<IGateKeeperService> {
+public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0);
+};
+
+} // namespace android
+
+#endif
+
diff --git a/gatekeeperd/IUserManager.cpp b/gatekeeperd/IUserManager.cpp
new file mode 100644
index 0000000..8167d19
--- /dev/null
+++ b/gatekeeperd/IUserManager.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IUserManager"
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+
+#include "IUserManager.h"
+
+namespace android {
+
+class BpUserManager : public BpInterface<IUserManager>
+{
+public:
+ explicit BpUserManager(const sp<IBinder>& impl) :
+ BpInterface<IUserManager>(impl) {
+ }
+ virtual int32_t getCredentialOwnerProfile(int32_t user_id) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IUserManager::getInterfaceDescriptor());
+ data.writeInt32(user_id);
+ status_t rc = remote()->transact(GET_CREDENTIAL_OWNER_PROFILE, data, &reply, 0);
+ if (rc != NO_ERROR) {
+ ALOGE("%s: failed (%d)\n", __func__, rc);
+ return -1;
+ }
+
+ int32_t exception = reply.readExceptionCode();
+ if (exception != 0) {
+ ALOGE("%s: got exception (%d)\n", __func__, exception);
+ return -1;
+ }
+
+ return reply.readInt32();
+ }
+
+};
+
+IMPLEMENT_META_INTERFACE(UserManager, "android.os.IUserManager");
+
+}; // namespace android
+
diff --git a/gatekeeperd/IUserManager.h b/gatekeeperd/IUserManager.h
new file mode 100644
index 0000000..640e9b5
--- /dev/null
+++ b/gatekeeperd/IUserManager.h
@@ -0,0 +1,46 @@
+/*
+ * 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 IUSERMANAGER_H_
+#define IUSERMANAGER_H_
+
+#include <inttypes.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+/*
+* Communication channel to UserManager
+*/
+class IUserManager : public IInterface {
+ public:
+ // must be kept in sync with IUserManager.aidl
+ enum {
+ GET_CREDENTIAL_OWNER_PROFILE = IBinder::FIRST_CALL_TRANSACTION + 0,
+ };
+
+ virtual int32_t getCredentialOwnerProfile(int32_t user_id) = 0;
+
+ DECLARE_META_INTERFACE(UserManager);
+};
+
+}; // namespace android
+
+#endif // IUSERMANAGER_H_
+
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
new file mode 100644
index 0000000..cb02a6f
--- /dev/null
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef SOFT_GATEKEEPER_H_
+#define SOFT_GATEKEEPER_H_
+
+extern "C" {
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+
+#include <crypto_scrypt.h>
+}
+
+#include <android-base/memory.h>
+#include <UniquePtr.h>
+#include <gatekeeper/gatekeeper.h>
+
+#include <iostream>
+#include <unordered_map>
+
+namespace gatekeeper {
+
+struct fast_hash_t {
+ uint64_t salt;
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+};
+
+class SoftGateKeeper : public GateKeeper {
+public:
+ static const uint32_t SIGNATURE_LENGTH_BYTES = 32;
+
+ // scrypt params
+ static const uint64_t N = 16384;
+ static const uint32_t r = 8;
+ static const uint32_t p = 1;
+
+ static const int MAX_UINT_32_CHARS = 11;
+
+ SoftGateKeeper() {
+ key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]);
+ memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES);
+ }
+
+ virtual ~SoftGateKeeper() {
+ }
+
+ virtual bool GetAuthTokenKey(const uint8_t **auth_token_key,
+ uint32_t *length) const {
+ if (auth_token_key == NULL || length == NULL) return false;
+ uint8_t *auth_token_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
+ memcpy(auth_token_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
+
+ *auth_token_key = auth_token_key_copy;
+ *length = SIGNATURE_LENGTH_BYTES;
+ return true;
+ }
+
+ virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) {
+ if (password_key == NULL || length == NULL) return;
+ uint8_t *password_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
+ memcpy(password_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
+
+ *password_key = password_key_copy;
+ *length = SIGNATURE_LENGTH_BYTES;
+ }
+
+ virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length,
+ const uint8_t *, uint32_t, const uint8_t *password,
+ uint32_t password_length, salt_t salt) const {
+ if (signature == NULL) return;
+ crypto_scrypt(password, password_length, reinterpret_cast<uint8_t *>(&salt),
+ sizeof(salt), N, r, p, signature, signature_length);
+ }
+
+ virtual void GetRandom(void *random, uint32_t requested_length) const {
+ if (random == NULL) return;
+ RAND_pseudo_bytes((uint8_t *) random, requested_length);
+ }
+
+ virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length,
+ const uint8_t *, uint32_t, const uint8_t *, const uint32_t) const {
+ if (signature == NULL) return;
+ memset(signature, 0, signature_length);
+ }
+
+ virtual uint64_t GetMillisecondsSinceBoot() const {
+ struct timespec time;
+ int res = clock_gettime(CLOCK_BOOTTIME, &time);
+ if (res < 0) return 0;
+ return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000);
+ }
+
+ virtual bool IsHardwareBacked() const {
+ return false;
+ }
+
+ virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record,
+ bool /* secure */) {
+ failure_record_t *stored = &failure_map_[uid];
+ if (user_id != stored->secure_user_id) {
+ stored->secure_user_id = user_id;
+ stored->last_checked_timestamp = 0;
+ stored->failure_counter = 0;
+ }
+ memcpy(record, stored, sizeof(*record));
+ return true;
+ }
+
+ virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) {
+ failure_record_t *stored = &failure_map_[uid];
+ stored->secure_user_id = user_id;
+ stored->last_checked_timestamp = 0;
+ stored->failure_counter = 0;
+ return true;
+ }
+
+ virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record, bool /* secure */) {
+ failure_map_[uid] = *record;
+ return true;
+ }
+
+ fast_hash_t ComputeFastHash(const SizedBuffer &password, uint64_t salt) {
+ fast_hash_t fast_hash;
+ size_t digest_size = password.length + sizeof(salt);
+ std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]);
+ memcpy(digest.get(), &salt, sizeof(salt));
+ memcpy(digest.get() + sizeof(salt), password.buffer.get(), password.length);
+
+ SHA256(digest.get(), digest_size, (uint8_t *) &fast_hash.digest);
+
+ fast_hash.salt = salt;
+ return fast_hash;
+ }
+
+ bool VerifyFast(const fast_hash_t &fast_hash, const SizedBuffer &password) {
+ fast_hash_t computed = ComputeFastHash(password, fast_hash.salt);
+ return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0;
+ }
+
+ bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
+ uint64_t user_id = android::base::get_unaligned<secure_id_t>(&expected_handle->user_id);
+ FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
+ if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
+ return true;
+ } else {
+ if (GateKeeper::DoVerify(expected_handle, password)) {
+ uint64_t salt;
+ GetRandom(&salt, sizeof(salt));
+ fast_hash_map_[user_id] = ComputeFastHash(password, salt);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+private:
+
+ typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
+ typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
+
+ UniquePtr<uint8_t[]> key_;
+ FailureRecordMap failure_map_;
+ FastHashMap fast_hash_map_;
+};
+}
+
+#endif // SOFT_GATEKEEPER_H_
diff --git a/gatekeeperd/SoftGateKeeperDevice.cpp b/gatekeeperd/SoftGateKeeperDevice.cpp
new file mode 100644
index 0000000..f5e2ce6
--- /dev/null
+++ b/gatekeeperd/SoftGateKeeperDevice.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "SoftGateKeeper.h"
+#include "SoftGateKeeperDevice.h"
+
+namespace android {
+
+int SoftGateKeeperDevice::enroll(uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
+
+ if (enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
+ desired_password == NULL || desired_password_length == 0)
+ return -EINVAL;
+
+ // Current password and current password handle go together
+ if (current_password_handle == NULL || current_password_handle_length == 0 ||
+ current_password == NULL || current_password_length == 0) {
+ current_password_handle = NULL;
+ current_password_handle_length = 0;
+ current_password = NULL;
+ current_password_length = 0;
+ }
+
+ SizedBuffer desired_password_buffer(desired_password_length);
+ memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
+
+ SizedBuffer current_password_handle_buffer(current_password_handle_length);
+ if (current_password_handle) {
+ memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
+ current_password_handle_length);
+ }
+
+ SizedBuffer current_password_buffer(current_password_length);
+ if (current_password) {
+ memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
+ }
+
+ EnrollRequest request(uid, ¤t_password_handle_buffer, &desired_password_buffer,
+ ¤t_password_buffer);
+ EnrollResponse response;
+
+ impl_->Enroll(request, &response);
+
+ if (response.error == ERROR_RETRY) {
+ return response.retry_timeout;
+ } else if (response.error != ERROR_NONE) {
+ return -EINVAL;
+ }
+
+ *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
+ *enrolled_password_handle_length = response.enrolled_password_handle.length;
+ return 0;
+}
+
+int SoftGateKeeperDevice::verify(uint32_t uid,
+ uint64_t challenge, const uint8_t *enrolled_password_handle,
+ uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
+ uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
+ bool *request_reenroll) {
+
+ if (enrolled_password_handle == NULL ||
+ provided_password == NULL) {
+ return -EINVAL;
+ }
+
+ SizedBuffer password_handle_buffer(enrolled_password_handle_length);
+ memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
+ enrolled_password_handle_length);
+ SizedBuffer provided_password_buffer(provided_password_length);
+ memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
+
+ VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
+ VerifyResponse response;
+
+ impl_->Verify(request, &response);
+
+ if (response.error == ERROR_RETRY) {
+ return response.retry_timeout;
+ } else if (response.error != ERROR_NONE) {
+ return -EINVAL;
+ }
+
+ if (auth_token != NULL && auth_token_length != NULL) {
+ *auth_token = response.auth_token.buffer.release();
+ *auth_token_length = response.auth_token.length;
+ }
+
+ if (request_reenroll != NULL) {
+ *request_reenroll = response.request_reenroll;
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
new file mode 100644
index 0000000..3463c29
--- /dev/null
+++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SOFT_GATEKEEPER_DEVICE_H_
+#define SOFT_GATEKEEPER_DEVICE_H_
+
+#include "SoftGateKeeper.h"
+
+#include <UniquePtr.h>
+
+using namespace gatekeeper;
+
+namespace android {
+
+/**
+ * Software based GateKeeper implementation
+ */
+class SoftGateKeeperDevice {
+public:
+ SoftGateKeeperDevice() {
+ impl_.reset(new SoftGateKeeper());
+ }
+
+ // Wrappers to translate the gatekeeper HAL API to the Kegyuard Messages API.
+
+ /**
+ * Enrolls password_payload, which should be derived from a user selected pin or password,
+ * with the authentication factor private key used only for enrolling authentication
+ * factor data.
+ *
+ * Returns: 0 on success or an error code less than 0 on error.
+ * On error, enrolled_password_handle will not be allocated.
+ */
+ int enroll(uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
+
+ /**
+ * Verifies provided_password matches enrolled_password_handle.
+ *
+ * Implementations of this module may retain the result of this call
+ * to attest to the recency of authentication.
+ *
+ * On success, writes the address of a verification token to auth_token,
+ * usable to attest password verification to other trusted services. Clients
+ * may pass NULL for this value.
+ *
+ * Returns: 0 on success or an error code less than 0 on error
+ * On error, verification token will not be allocated
+ */
+ int verify(uint32_t uid, uint64_t challenge,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
+private:
+ UniquePtr<SoftGateKeeper> impl_;
+};
+
+} // namespace gatekeeper
+
+#endif //SOFT_GATEKEEPER_DEVICE_H_
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
new file mode 100644
index 0000000..d4a92e5
--- /dev/null
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "gatekeeperd"
+
+#include "IGateKeeperService.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
+#include <gatekeeper/password_handle.h> // for password_handle_t
+#include <hardware/gatekeeper.h>
+#include <hardware/hw_auth_token.h>
+#include <keystore/IKeystoreService.h>
+#include <keystore/keystore.h> // For error code
+#include <log/log.h>
+#include <utils/Log.h>
+#include <utils/String16.h>
+
+#include "SoftGateKeeperDevice.h"
+#include "IUserManager.h"
+
+namespace android {
+
+static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SECURE_STORAGE");
+static const String16 DUMP_PERMISSION("android.permission.DUMP");
+
+class GateKeeperProxy : public BnGateKeeperService {
+public:
+ GateKeeperProxy() {
+ int ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &module);
+ device = NULL;
+
+ if (ret < 0) {
+ ALOGW("falling back to software GateKeeper");
+ soft_device.reset(new SoftGateKeeperDevice());
+ } else {
+ ret = gatekeeper_open(module, &device);
+ if (ret < 0)
+ LOG_ALWAYS_FATAL_IF(ret < 0, "Unable to open GateKeeper HAL");
+ }
+
+ if (mark_cold_boot()) {
+ ALOGI("cold boot: clearing state");
+ if (device != NULL && device->delete_all_users != NULL) {
+ device->delete_all_users(device);
+ }
+ }
+ }
+
+ virtual ~GateKeeperProxy() {
+ if (device) gatekeeper_close(device);
+ }
+
+ void store_sid(uint32_t uid, uint64_t sid) {
+ char filename[21];
+ snprintf(filename, sizeof(filename), "%u", uid);
+ int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ ALOGE("could not open file: %s: %s", filename, strerror(errno));
+ return;
+ }
+ write(fd, &sid, sizeof(sid));
+ close(fd);
+ }
+
+ bool mark_cold_boot() {
+ const char *filename = ".coldboot";
+ if (access(filename, F_OK) == -1) {
+ int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ ALOGE("could not open file: %s : %s", filename, strerror(errno));
+ return false;
+ }
+ close(fd);
+ return true;
+ }
+ return false;
+ }
+
+ void maybe_store_sid(uint32_t uid, uint64_t sid) {
+ char filename[21];
+ snprintf(filename, sizeof(filename), "%u", uid);
+ if (access(filename, F_OK) == -1) {
+ store_sid(uid, sid);
+ }
+ }
+
+ uint64_t read_sid(uint32_t uid) {
+ char filename[21];
+ uint64_t sid;
+ snprintf(filename, sizeof(filename), "%u", uid);
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0) return 0;
+ read(fd, &sid, sizeof(sid));
+ close(fd);
+ return sid;
+ }
+
+ void clear_sid(uint32_t uid) {
+ char filename[21];
+ snprintf(filename, sizeof(filename), "%u", uid);
+ if (remove(filename) < 0) {
+ ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
+ store_sid(uid, 0);
+ }
+ }
+
+ virtual int enroll(uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int calling_pid = ipc->getCallingPid();
+ const int calling_uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+ return PERMISSION_DENIED;
+ }
+
+ // need a desired password to enroll
+ if (desired_password_length == 0) return -EINVAL;
+
+ int ret;
+ if (device) {
+ const gatekeeper::password_handle_t *handle =
+ reinterpret_cast<const gatekeeper::password_handle_t *>(current_password_handle);
+
+ if (handle != NULL && handle->version != 0 && !handle->hardware_backed) {
+ // handle is being re-enrolled from a software version. HAL probably won't accept
+ // the handle as valid, so we nullify it and enroll from scratch
+ current_password_handle = NULL;
+ current_password_handle_length = 0;
+ current_password = NULL;
+ current_password_length = 0;
+ }
+
+ ret = device->enroll(device, uid, current_password_handle, current_password_handle_length,
+ current_password, current_password_length,
+ desired_password, desired_password_length,
+ enrolled_password_handle, enrolled_password_handle_length);
+ } else {
+ ret = soft_device->enroll(uid,
+ current_password_handle, current_password_handle_length,
+ current_password, current_password_length,
+ desired_password, desired_password_length,
+ enrolled_password_handle, enrolled_password_handle_length);
+ }
+
+ if (ret == 0) {
+ gatekeeper::password_handle_t *handle =
+ reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
+ store_sid(uid, handle->user_id);
+ bool rr;
+
+ // immediately verify this password so we don't ask the user to enter it again
+ // if they just created it.
+ verify(uid, *enrolled_password_handle, sizeof(password_handle_t), desired_password,
+ desired_password_length, &rr);
+ }
+
+ return ret;
+ }
+
+ virtual int verify(uint32_t uid,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) {
+ uint8_t *auth_token;
+ uint32_t auth_token_length;
+ return verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
+ provided_password, provided_password_length,
+ &auth_token, &auth_token_length, request_reenroll);
+ }
+
+ virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int calling_pid = ipc->getCallingPid();
+ const int calling_uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+ return PERMISSION_DENIED;
+ }
+
+ // can't verify if we're missing either param
+ if ((enrolled_password_handle_length | provided_password_length) == 0)
+ return -EINVAL;
+
+ int ret;
+ if (device) {
+ const gatekeeper::password_handle_t *handle =
+ reinterpret_cast<const gatekeeper::password_handle_t *>(enrolled_password_handle);
+ // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to
+ // a HAL if there was none before
+ if (handle->version == 0 || handle->hardware_backed) {
+ ret = device->verify(device, uid, challenge,
+ enrolled_password_handle, enrolled_password_handle_length,
+ provided_password, provided_password_length, auth_token, auth_token_length,
+ request_reenroll);
+ } else {
+ // upgrade scenario, a HAL has been added to this device where there was none before
+ SoftGateKeeperDevice soft_dev;
+ ret = soft_dev.verify(uid, challenge,
+ enrolled_password_handle, enrolled_password_handle_length,
+ provided_password, provided_password_length, auth_token, auth_token_length,
+ request_reenroll);
+
+ if (ret == 0) {
+ // success! re-enroll with HAL
+ *request_reenroll = true;
+ }
+ }
+ } else {
+ ret = soft_device->verify(uid, challenge,
+ enrolled_password_handle, enrolled_password_handle_length,
+ provided_password, provided_password_length, auth_token, auth_token_length,
+ request_reenroll);
+ }
+
+ if (ret == 0 && *auth_token != NULL && *auth_token_length > 0) {
+ // TODO: cache service?
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+ if (service != NULL) {
+ status_t ret = service->addAuthToken(*auth_token, *auth_token_length);
+ if (ret != ResponseCode::NO_ERROR) {
+ ALOGE("Falure sending auth token to KeyStore: %d", ret);
+ }
+ } else {
+ ALOGE("Unable to communicate with KeyStore");
+ }
+ }
+
+ if (ret == 0) {
+ maybe_store_sid(uid, reinterpret_cast<const gatekeeper::password_handle_t *>(
+ enrolled_password_handle)->user_id);
+ }
+
+ return ret;
+ }
+
+ virtual uint64_t getSecureUserId(uint32_t uid) {
+ uint64_t sid = read_sid(uid);
+ if (sid == 0) {
+ // might be a work profile, look up the parent
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("user"));
+ sp<IUserManager> um = interface_cast<IUserManager>(binder);
+ int32_t parent = um->getCredentialOwnerProfile(uid);
+ if (parent < 0) {
+ return 0;
+ } else if (parent != (int32_t) uid) {
+ return read_sid(parent);
+ }
+ }
+ return sid;
+
+ }
+
+ virtual void clearSecureUserId(uint32_t uid) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int calling_pid = ipc->getCallingPid();
+ const int calling_uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+ ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
+ return;
+ }
+ clear_sid(uid);
+
+ if (device != NULL && device->delete_user != NULL) {
+ device->delete_user(device, uid);
+ }
+ }
+
+ virtual status_t dump(int fd, const Vector<String16> &) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(DUMP_PERMISSION, pid, uid)) {
+ return PERMISSION_DENIED;
+ }
+
+ if (device == NULL) {
+ const char *result = "Device not available";
+ write(fd, result, strlen(result) + 1);
+ } else {
+ const char *result = "OK";
+ write(fd, result, strlen(result) + 1);
+ }
+
+ return NO_ERROR;
+ }
+
+private:
+ gatekeeper_device_t *device;
+ UniquePtr<SoftGateKeeperDevice> soft_device;
+ const hw_module_t *module;
+};
+}// namespace android
+
+int main(int argc, char* argv[]) {
+ ALOGI("Starting gatekeeperd...");
+ if (argc < 2) {
+ ALOGE("A directory must be specified!");
+ return 1;
+ }
+ if (chdir(argv[1]) == -1) {
+ ALOGE("chdir: %s: %s", argv[1], strerror(errno));
+ return 1;
+ }
+
+ android::sp<android::IServiceManager> sm = android::defaultServiceManager();
+ android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy();
+ android::status_t ret = sm->addService(
+ android::String16("android.service.gatekeeper.IGateKeeperService"), proxy);
+ if (ret != android::OK) {
+ ALOGE("Couldn't register binder service!");
+ return -1;
+ }
+
+ /*
+ * We're the only thread in existence, so we're just going to process
+ * Binder transaction as a single-threaded program.
+ */
+ android::IPCThreadState::self()->joinThreadPool();
+ return 0;
+}
diff --git a/gatekeeperd/gatekeeperd.rc b/gatekeeperd/gatekeeperd.rc
new file mode 100644
index 0000000..8b126d5
--- /dev/null
+++ b/gatekeeperd/gatekeeperd.rc
@@ -0,0 +1,4 @@
+service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper
+ class late_start
+ user system
+ writepid /dev/cpuset/system-background/tasks
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
new file mode 100644
index 0000000..a62b1d4
--- /dev/null
+++ b/gatekeeperd/tests/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := gatekeeperd-unit-tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
+LOCAL_STATIC_LIBRARIES := libscrypt_static
+LOCAL_C_INCLUDES := external/scrypt/lib/crypto
+LOCAL_SRC_FILES := \
+ gatekeeper_test.cpp
+include $(BUILD_NATIVE_TEST)
+
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
new file mode 100644
index 0000000..47a8bfa
--- /dev/null
+++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <iostream>
+
+#include <gtest/gtest.h>
+#include <hardware/hw_auth_token.h>
+#include <UniquePtr.h>
+
+#include "../SoftGateKeeper.h"
+
+using ::gatekeeper::SizedBuffer;
+using ::testing::Test;
+using ::gatekeeper::EnrollRequest;
+using ::gatekeeper::EnrollResponse;
+using ::gatekeeper::VerifyRequest;
+using ::gatekeeper::VerifyResponse;
+using ::gatekeeper::SoftGateKeeper;
+using ::gatekeeper::secure_id_t;
+
+static void do_enroll(SoftGateKeeper &gatekeeper, EnrollResponse *response) {
+ SizedBuffer password;
+
+ password.buffer.reset(new uint8_t[16]);
+ password.length = 16;
+ memset(password.buffer.get(), 0, 16);
+ EnrollRequest request(0, NULL, &password, NULL);
+
+ gatekeeper.Enroll(request, response);
+}
+
+TEST(GateKeeperTest, EnrollSuccess) {
+ SoftGateKeeper gatekeeper;
+ EnrollResponse response;
+ do_enroll(gatekeeper, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+}
+
+TEST(GateKeeperTest, EnrollBogusData) {
+ SoftGateKeeper gatekeeper;
+ SizedBuffer password;
+ EnrollResponse response;
+
+ EnrollRequest request(0, NULL, &password, NULL);
+
+ gatekeeper.Enroll(request, &response);
+
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
+}
+
+TEST(GateKeeperTest, VerifySuccess) {
+ SoftGateKeeper gatekeeper;
+ SizedBuffer provided_password;
+ EnrollResponse enroll_response;
+
+ provided_password.buffer.reset(new uint8_t[16]);
+ provided_password.length = 16;
+ memset(provided_password.buffer.get(), 0, 16);
+
+ do_enroll(gatekeeper, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+ VerifyRequest request(0, 1, &enroll_response.enrolled_password_handle,
+ &provided_password);
+ VerifyResponse response;
+
+ gatekeeper.Verify(request, &response);
+
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+
+ hw_auth_token_t *auth_token =
+ reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
+
+ ASSERT_EQ((uint32_t) HW_AUTH_PASSWORD, ntohl(auth_token->authenticator_type));
+ ASSERT_EQ((uint64_t) 1, auth_token->challenge);
+ ASSERT_NE(~((uint32_t) 0), auth_token->timestamp);
+ ASSERT_NE((uint64_t) 0, auth_token->user_id);
+ ASSERT_NE((uint64_t) 0, auth_token->authenticator_id);
+}
+
+TEST(GateKeeperTest, TrustedReEnroll) {
+ SoftGateKeeper gatekeeper;
+ SizedBuffer provided_password;
+ EnrollResponse enroll_response;
+ SizedBuffer password_handle;
+
+ // do_enroll enrolls an all 0 password
+ provided_password.buffer.reset(new uint8_t[16]);
+ provided_password.length = 16;
+ memset(provided_password.buffer.get(), 0, 16);
+ do_enroll(gatekeeper, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+
+ // keep a copy of the handle
+ password_handle.buffer.reset(new uint8_t[enroll_response.enrolled_password_handle.length]);
+ password_handle.length = enroll_response.enrolled_password_handle.length;
+ memcpy(password_handle.buffer.get(), enroll_response.enrolled_password_handle.buffer.get(),
+ password_handle.length);
+
+ // verify first password
+ VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle,
+ &provided_password);
+ VerifyResponse response;
+ gatekeeper.Verify(request, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+ hw_auth_token_t *auth_token =
+ reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
+
+ secure_id_t secure_id = auth_token->user_id;
+
+ // enroll new password
+ provided_password.buffer.reset(new uint8_t[16]);
+ provided_password.length = 16;
+ memset(provided_password.buffer.get(), 0, 16);
+ SizedBuffer password;
+ password.buffer.reset(new uint8_t[16]);
+ memset(password.buffer.get(), 1, 16);
+ password.length = 16;
+ EnrollRequest enroll_request(0, &password_handle, &password, &provided_password);
+ gatekeeper.Enroll(enroll_request, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+
+ // verify new password
+ password.buffer.reset(new uint8_t[16]);
+ memset(password.buffer.get(), 1, 16);
+ password.length = 16;
+ VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle,
+ &password);
+ gatekeeper.Verify(new_request, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+ ASSERT_EQ(secure_id,
+ reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id);
+}
+
+
+TEST(GateKeeperTest, UntrustedReEnroll) {
+ SoftGateKeeper gatekeeper;
+ SizedBuffer provided_password;
+ EnrollResponse enroll_response;
+
+ // do_enroll enrolls an all 0 password
+ provided_password.buffer.reset(new uint8_t[16]);
+ provided_password.length = 16;
+ memset(provided_password.buffer.get(), 0, 16);
+ do_enroll(gatekeeper, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+
+ // verify first password
+ VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle,
+ &provided_password);
+ VerifyResponse response;
+ gatekeeper.Verify(request, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+ hw_auth_token_t *auth_token =
+ reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
+
+ secure_id_t secure_id = auth_token->user_id;
+
+ // enroll new password
+ SizedBuffer password;
+ password.buffer.reset(new uint8_t[16]);
+ memset(password.buffer.get(), 1, 16);
+ password.length = 16;
+ EnrollRequest enroll_request(0, NULL, &password, NULL);
+ gatekeeper.Enroll(enroll_request, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+
+ // verify new password
+ password.buffer.reset(new uint8_t[16]);
+ memset(password.buffer.get(), 1, 16);
+ password.length = 16;
+ VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle,
+ &password);
+ gatekeeper.Verify(new_request, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+ ASSERT_NE(secure_id,
+ reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id);
+}
+
+
+TEST(GateKeeperTest, VerifyBogusData) {
+ SoftGateKeeper gatekeeper;
+ SizedBuffer provided_password;
+ SizedBuffer password_handle;
+ VerifyResponse response;
+
+ VerifyRequest request(0, 0, &provided_password, &password_handle);
+
+ gatekeeper.Verify(request, &response);
+
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 07e1d73..7c5e35b 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -6,16 +6,67 @@
LOCAL_SRC_FILES := healthd_board_default.cpp
LOCAL_MODULE := libhealthd.default
LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libbinder
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := libbinder
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
+LOCAL_SRC_FILES := BatteryMonitor.cpp
+LOCAL_MODULE := libbatterymonitor
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libutils libbase libbinder
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ healthd_mode_android.cpp \
+ healthd_mode_charger.cpp \
+ AnimationParser.cpp \
+ BatteryPropertiesRegistrar.cpp \
+
+LOCAL_MODULE := libhealthd_internal
+LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/include \
+
+LOCAL_STATIC_LIBRARIES := \
+ libbatterymonitor \
+ libbatteryservice \
+ libbinder \
+ libminui \
+ libpng \
+ libz \
+ libutils \
+ libbase \
+ libcutils \
+ liblog \
+ libm \
+ libc \
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
+LOCAL_CHARGER_NO_UI := true
+endif
+ifdef BRILLO
+LOCAL_CHARGER_NO_UI := true
+endif
LOCAL_SRC_FILES := \
healthd.cpp \
healthd_mode_android.cpp \
- healthd_mode_charger.cpp \
- BatteryMonitor.cpp \
- BatteryPropertiesRegistrar.cpp
+ BatteryPropertiesRegistrar.cpp \
+
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_SRC_FILES += healthd_mode_charger.cpp
+endif
LOCAL_MODULE := healthd
LOCAL_MODULE_TAGS := optional
@@ -33,9 +84,34 @@
LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
endif
-LOCAL_C_INCLUDES := bootable/recovery
+ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_CFLAGS += -DCHARGER_NO_UI
+endif
-LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
+LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+
+LOCAL_STATIC_LIBRARIES := \
+ libhealthd_internal \
+ libbatterymonitor \
+ libbatteryservice \
+ libbinder \
+ libbase \
+
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_STATIC_LIBRARIES += \
+ libminui \
+ libpng \
+ libz \
+
+endif
+
+
+LOCAL_STATIC_LIBRARIES += \
+ libutils \
+ libcutils \
+ liblog \
+ libm \
+ libc \
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
LOCAL_STATIC_LIBRARIES += libsuspend
@@ -50,9 +126,10 @@
include $(BUILD_EXECUTABLE)
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
define _add-charger-image
include $$(CLEAR_VARS)
-LOCAL_MODULE := system_core_charger_$(notdir $(1))
+LOCAL_MODULE := system_core_charger_res_images_$(notdir $(1))
LOCAL_MODULE_STEM := $(notdir $(1))
_img_modules += $$(LOCAL_MODULE)
LOCAL_SRC_FILES := $1
@@ -75,3 +152,4 @@
_add-charger-image :=
_img_modules :=
+endif # LOCAL_CHARGER_NO_UI
diff --git a/healthd/AnimationParser.cpp b/healthd/AnimationParser.cpp
new file mode 100644
index 0000000..864038b
--- /dev/null
+++ b/healthd/AnimationParser.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AnimationParser.h"
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <cutils/klog.h>
+
+#include "animation.h"
+
+#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
+#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
+#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
+
+namespace android {
+
+// Lines consisting of only whitespace or whitespace followed by '#' can be ignored.
+bool can_ignore_line(const char* str) {
+ for (int i = 0; str[i] != '\0' && str[i] != '#'; i++) {
+ if (!isspace(str[i])) return false;
+ }
+ return true;
+}
+
+bool remove_prefix(const std::string& line, const char* prefix, const char** rest) {
+ const char* str = line.c_str();
+ int start;
+ char c;
+
+ std::string format = base::StringPrintf(" %s%%n%%c", prefix);
+ if (sscanf(str, format.c_str(), &start, &c) != 1) {
+ return false;
+ }
+
+ *rest = &str[start];
+ return true;
+}
+
+bool parse_text_field(const char* in, animation::text_field* field) {
+ int* x = &field->pos_x;
+ int* y = &field->pos_y;
+ int* r = &field->color_r;
+ int* g = &field->color_g;
+ int* b = &field->color_b;
+ int* a = &field->color_a;
+
+ int start = 0, end = 0;
+
+ if (sscanf(in, "c c %d %d %d %d %n%*s%n", r, g, b, a, &start, &end) == 4) {
+ *x = CENTER_VAL;
+ *y = CENTER_VAL;
+ } else if (sscanf(in, "c %d %d %d %d %d %n%*s%n", y, r, g, b, a, &start, &end) == 5) {
+ *x = CENTER_VAL;
+ } else if (sscanf(in, "%d c %d %d %d %d %n%*s%n", x, r, g, b, a, &start, &end) == 5) {
+ *y = CENTER_VAL;
+ } else if (sscanf(in, "%d %d %d %d %d %d %n%*s%n", x, y, r, g, b, a, &start, &end) != 6) {
+ return false;
+ }
+
+ if (end == 0) return false;
+
+ field->font_file.assign(&in[start], end - start);
+
+ return true;
+}
+
+bool parse_animation_desc(const std::string& content, animation* anim) {
+ static constexpr const char* animation_prefix = "animation: ";
+ static constexpr const char* fail_prefix = "fail: ";
+ static constexpr const char* clock_prefix = "clock_display: ";
+ static constexpr const char* percent_prefix = "percent_display: ";
+ static constexpr const char* frame_prefix = "frame: ";
+
+ std::vector<animation::frame> frames;
+
+ for (const auto& line : base::Split(content, "\n")) {
+ animation::frame frame;
+ const char* rest;
+
+ if (can_ignore_line(line.c_str())) {
+ continue;
+ } else if (remove_prefix(line, animation_prefix, &rest)) {
+ int start = 0, end = 0;
+ if (sscanf(rest, "%d %d %n%*s%n", &anim->num_cycles, &anim->first_frame_repeats,
+ &start, &end) != 2 ||
+ end == 0) {
+ LOGE("Bad animation format: %s\n", line.c_str());
+ return false;
+ } else {
+ anim->animation_file.assign(&rest[start], end - start);
+ }
+ } else if (remove_prefix(line, fail_prefix, &rest)) {
+ anim->fail_file.assign(rest);
+ } else if (remove_prefix(line, clock_prefix, &rest)) {
+ if (!parse_text_field(rest, &anim->text_clock)) {
+ LOGE("Bad clock_display format: %s\n", line.c_str());
+ return false;
+ }
+ } else if (remove_prefix(line, percent_prefix, &rest)) {
+ if (!parse_text_field(rest, &anim->text_percent)) {
+ LOGE("Bad percent_display format: %s\n", line.c_str());
+ return false;
+ }
+ } else if (sscanf(line.c_str(), " frame: %d %d %d",
+ &frame.disp_time, &frame.min_level, &frame.max_level) == 3) {
+ frames.push_back(std::move(frame));
+ } else {
+ LOGE("Malformed animation description line: %s\n", line.c_str());
+ return false;
+ }
+ }
+
+ if (anim->animation_file.empty() || frames.empty()) {
+ LOGE("Bad animation description. Provide the 'animation: ' line and at least one 'frame: ' "
+ "line.\n");
+ return false;
+ }
+
+ anim->num_frames = frames.size();
+ anim->frames = new animation::frame[frames.size()];
+ std::copy(frames.begin(), frames.end(), anim->frames);
+
+ return true;
+}
+
+} // namespace android
diff --git a/healthd/AnimationParser.h b/healthd/AnimationParser.h
new file mode 100644
index 0000000..bc00845
--- /dev/null
+++ b/healthd/AnimationParser.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HEALTHD_ANIMATION_PARSER_H
+#define HEALTHD_ANIMATION_PARSER_H
+
+#include "animation.h"
+
+namespace android {
+
+bool parse_animation_desc(const std::string& content, animation* anim);
+
+bool can_ignore_line(const char* str);
+bool remove_prefix(const std::string& str, const char* prefix, const char** rest);
+bool parse_text_field(const char* in, animation::text_field* field);
+} // namespace android
+
+#endif // HEALTHD_ANIMATION_PARSER_H
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index ed773d0..0c90a54 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -16,8 +16,8 @@
#define LOG_TAG "healthd"
-#include "healthd.h"
-#include "BatteryMonitor.h"
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
#include <dirent.h>
#include <errno.h>
@@ -26,11 +26,14 @@
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
#include <cutils/properties.h>
-#include <log/log_read.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -39,6 +42,9 @@
#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
#define FAKE_BATTERY_CAPACITY 42
#define FAKE_BATTERY_TEMPERATURE 424
+#define ALWAYS_PLUGGED_CAPACITY 100
+#define MILLION 1.0e6
+#define DEFAULT_VBUS_VOLTAGE 5000000
namespace android {
@@ -56,6 +62,30 @@
return -1;
}
+static void initBatteryProperties(BatteryProperties* props) {
+ props->chargerAcOnline = false;
+ props->chargerUsbOnline = false;
+ props->chargerWirelessOnline = false;
+ props->maxChargingCurrent = 0;
+ props->maxChargingVoltage = 0;
+ props->batteryStatus = BATTERY_STATUS_UNKNOWN;
+ props->batteryHealth = BATTERY_HEALTH_UNKNOWN;
+ props->batteryPresent = false;
+ props->batteryLevel = 0;
+ props->batteryVoltage = 0;
+ props->batteryTemperature = 0;
+ props->batteryCurrent = 0;
+ props->batteryCycleCount = 0;
+ props->batteryFullCharge = 0;
+ props->batteryChargeCounter = 0;
+ props->batteryTechnology.clear();
+}
+
+BatteryMonitor::BatteryMonitor() : mHealthdConfig(nullptr), mBatteryDevicePresent(false),
+ mAlwaysPluggedDevice(false), mBatteryFixedCapacity(0), mBatteryFixedTemperature(0) {
+ initBatteryProperties(&props);
+}
+
int BatteryMonitor::getBatteryStatus(const char* status) {
int ret;
struct sysfsStringEnumMap batteryStatusMap[] = {
@@ -98,34 +128,15 @@
return ret;
}
-int BatteryMonitor::readFromFile(const String8& path, char* buf, size_t size) {
- char *cp = NULL;
-
- if (path.isEmpty())
- return -1;
- int fd = open(path.string(), O_RDONLY, 0);
- if (fd == -1) {
- KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path.string());
- return -1;
+int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
+ if (android::base::ReadFileToString(String8::std_string(path), buf)) {
+ *buf = android::base::Trim(*buf);
}
-
- ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
- if (count > 0)
- cp = (char *)memrchr(buf, '\n', count);
-
- if (cp)
- *cp = '\0';
- else
- buf[0] = '\0';
-
- close(fd);
- return count;
+ return buf->length();
}
BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
- const int SIZE = 128;
- char buf[SIZE];
- int length = readFromFile(path, buf, SIZE);
+ std::string buf;
BatteryMonitor::PowerSupplyType ret;
struct sysfsStringEnumMap supplyTypeMap[] = {
{ "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
@@ -134,55 +145,53 @@
{ "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
{ "USB", ANDROID_POWER_SUPPLY_TYPE_USB },
{ "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC },
{ "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
{ "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
{ "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
{ NULL, 0 },
};
- if (length <= 0)
+ if (readFromFile(path, &buf) <= 0)
return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
- ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf, supplyTypeMap);
- if (ret < 0)
+ ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf.c_str(), supplyTypeMap);
+ if (ret < 0) {
+ KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ }
return ret;
}
bool BatteryMonitor::getBooleanField(const String8& path) {
- const int SIZE = 16;
- char buf[SIZE];
-
+ std::string buf;
bool value = false;
- if (readFromFile(path, buf, SIZE) > 0) {
- if (buf[0] != '0') {
+
+ if (readFromFile(path, &buf) > 0)
+ if (buf[0] != '0')
value = true;
- }
- }
return value;
}
int BatteryMonitor::getIntField(const String8& path) {
- const int SIZE = 128;
- char buf[SIZE];
-
+ std::string buf;
int value = 0;
- if (readFromFile(path, buf, SIZE) > 0) {
- value = strtol(buf, NULL, 0);
- }
+
+ if (readFromFile(path, &buf) > 0)
+ android::base::ParseInt(buf, &value);
+
return value;
}
bool BatteryMonitor::update(void) {
bool logthis;
- props.chargerAcOnline = false;
- props.chargerUsbOnline = false;
- props.chargerWirelessOnline = false;
- props.batteryStatus = BATTERY_STATUS_UNKNOWN;
- props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
+ initBatteryProperties(&props);
if (!mHealthdConfig->batteryPresentPath.isEmpty())
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -194,49 +203,87 @@
getIntField(mHealthdConfig->batteryCapacityPath);
props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000;
+
+ if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+ props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
+
+ if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+ props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
+
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+ props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
+
props.batteryTemperature = mBatteryFixedTemperature ?
mBatteryFixedTemperature :
getIntField(mHealthdConfig->batteryTemperaturePath);
- const int SIZE = 128;
- char buf[SIZE];
- String8 btech;
+ // For devices which do not have battery and are always plugged
+ // into power souce.
+ if (mAlwaysPluggedDevice) {
+ props.chargerAcOnline = true;
+ props.batteryPresent = true;
+ props.batteryStatus = BATTERY_STATUS_CHARGING;
+ props.batteryHealth = BATTERY_HEALTH_GOOD;
+ }
- if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
- props.batteryStatus = getBatteryStatus(buf);
+ std::string buf;
- if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
- props.batteryHealth = getBatteryHealth(buf);
+ if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
+ props.batteryStatus = getBatteryStatus(buf.c_str());
- if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0)
- props.batteryTechnology = String8(buf);
+ if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
+ props.batteryHealth = getBatteryHealth(buf.c_str());
+
+ if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
+ props.batteryTechnology = String8(buf.c_str());
unsigned int i;
+ double MaxPower = 0;
for (i = 0; i < mChargerNames.size(); i++) {
String8 path;
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
+ if (getIntField(path)) {
+ path.clear();
+ path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ switch(readPowerSupplyType(path)) {
+ case ANDROID_POWER_SUPPLY_TYPE_AC:
+ props.chargerAcOnline = true;
+ break;
+ case ANDROID_POWER_SUPPLY_TYPE_USB:
+ props.chargerUsbOnline = true;
+ break;
+ case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+ props.chargerWirelessOnline = true;
+ break;
+ default:
+ KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
+ mChargerNames[i].string());
+ }
+ path.clear();
+ path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ int ChargingCurrent =
+ (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
- if (readFromFile(path, buf, SIZE) > 0) {
- if (buf[0] != '0') {
- path.clear();
- path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
- switch(readPowerSupplyType(path)) {
- case ANDROID_POWER_SUPPLY_TYPE_AC:
- props.chargerAcOnline = true;
- break;
- case ANDROID_POWER_SUPPLY_TYPE_USB:
- props.chargerUsbOnline = true;
- break;
- case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
- props.chargerWirelessOnline = true;
- break;
- default:
- KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
- mChargerNames[i].string());
- }
+ path.clear();
+ path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+
+ int ChargingVoltage =
+ (access(path.string(), R_OK) == 0) ? getIntField(path) :
+ DEFAULT_VBUS_VOLTAGE;
+
+ double power = ((double)ChargingCurrent / MILLION) *
+ ((double)ChargingVoltage / MILLION);
+ if (MaxPower < power) {
+ props.maxChargingCurrent = ChargingCurrent;
+ props.maxChargingVoltage = ChargingVoltage;
+ MaxPower = power;
}
}
}
@@ -245,7 +292,7 @@
if (logthis) {
char dmesgline[256];
-
+ size_t len;
if (props.batteryPresent) {
snprintf(dmesgline, sizeof(dmesgline),
"battery l=%d v=%d t=%s%d.%d h=%d st=%d",
@@ -255,43 +302,31 @@
abs(props.batteryTemperature % 10), props.batteryHealth,
props.batteryStatus);
+ len = strlen(dmesgline);
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
- int c = getIntField(mHealthdConfig->batteryCurrentNowPath);
- char b[20];
+ len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+ " c=%d", props.batteryCurrent);
+ }
- snprintf(b, sizeof(b), " c=%d", c / 1000);
- strlcat(dmesgline, b, sizeof(dmesgline));
+ if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+ " fc=%d", props.batteryFullCharge);
+ }
+
+ if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+ " cc=%d", props.batteryCycleCount);
}
} else {
- snprintf(dmesgline, sizeof(dmesgline),
+ len = snprintf(dmesgline, sizeof(dmesgline),
"battery none");
}
- size_t len = strlen(dmesgline);
snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
props.chargerAcOnline ? "a" : "",
props.chargerUsbOnline ? "u" : "",
props.chargerWirelessOnline ? "w" : "");
- log_time realtime(CLOCK_REALTIME);
- time_t t = realtime.tv_sec;
- struct tm *tmp = gmtime(&t);
- if (tmp) {
- static const char fmt[] = " %Y-%m-%d %H:%M:%S.XXXXXXXXX UTC";
- len = strlen(dmesgline);
- if ((len < (sizeof(dmesgline) - sizeof(fmt) - 8)) // margin
- && strftime(dmesgline + len, sizeof(dmesgline) - len,
- fmt, tmp)) {
- char *usec = strchr(dmesgline + len, 'X');
- if (usec) {
- len = usec - dmesgline;
- snprintf(dmesgline + len, sizeof(dmesgline) - len,
- "%09u", realtime.tv_nsec);
- usec[9] = ' ';
- }
- }
- }
-
KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
}
@@ -300,6 +335,16 @@
props.chargerWirelessOnline;
}
+int BatteryMonitor::getChargeStatus() {
+ int result = BATTERY_STATUS_UNKNOWN;
+ if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+ std::string buf;
+ if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
+ result = getBatteryStatus(buf.c_str());
+ }
+ return result;
+}
+
status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
status_t ret = BAD_VALUE;
@@ -365,9 +410,10 @@
int v;
char vs[128];
- snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n",
+ snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
props.chargerAcOnline, props.chargerUsbOnline,
- props.chargerWirelessOnline);
+ props.chargerWirelessOnline, props.maxChargingCurrent,
+ props.maxChargingVoltage);
write(fd, vs, strlen(vs));
snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
props.batteryStatus, props.batteryHealth, props.batteryPresent);
@@ -394,6 +440,21 @@
snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
write(fd, vs, strlen(vs));
}
+
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrent);
+ write(fd, vs, strlen(vs));
+ }
+
+ if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
+ write(fd, vs, strlen(vs));
+ }
+
+ if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullCharge);
+ write(fd, vs, strlen(vs));
+ }
}
void BatteryMonitor::init(struct healthd_config *hc) {
@@ -401,13 +462,13 @@
char pval[PROPERTY_VALUE_MAX];
mHealthdConfig = hc;
- DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
if (dir == NULL) {
KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
} else {
struct dirent* entry;
- while ((entry = readdir(dir))) {
+ while ((entry = readdir(dir.get()))) {
const char* name = entry->d_name;
if (!strcmp(name, ".") || !strcmp(name, ".."))
@@ -476,6 +537,14 @@
}
}
+ if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/charge_full",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryFullChargePath = path;
+ }
+
if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/current_now",
@@ -484,6 +553,14 @@
mHealthdConfig->batteryCurrentNowPath = path;
}
+ if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/cycle_count",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCycleCountPath = path;
+ }
+
if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/current_avg",
@@ -529,15 +606,17 @@
break;
}
}
- closedir(dir);
}
- if (!mChargerNames.size())
- KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
+ // Typically the case for devices which do not have a battery and
+ // and are always plugged into AC mains.
if (!mBatteryDevicePresent) {
KLOG_WARNING(LOG_TAG, "No battery devices found\n");
hc->periodic_chores_interval_fast = -1;
hc->periodic_chores_interval_slow = -1;
+ mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
+ mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
+ mAlwaysPluggedDevice = true;
} else {
if (mHealthdConfig->batteryStatusPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
@@ -553,6 +632,12 @@
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
if (mHealthdConfig->batteryTechnologyPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
+ if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
+ if (mHealthdConfig->batteryFullChargePath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
+ if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
}
if (property_get("ro.boot.fake_battery", pval, NULL) > 0
diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h
deleted file mode 100644
index 3425f27..0000000
--- a/healthd/BatteryMonitor.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef HEALTHD_BATTERYMONITOR_H
-#define HEALTHD_BATTERYMONITOR_H
-
-#include <batteryservice/BatteryService.h>
-#include <binder/IInterface.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
-#include "healthd.h"
-
-namespace android {
-
-class BatteryMonitor {
- public:
-
- enum PowerSupplyType {
- ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,
- ANDROID_POWER_SUPPLY_TYPE_AC,
- ANDROID_POWER_SUPPLY_TYPE_USB,
- ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
- ANDROID_POWER_SUPPLY_TYPE_BATTERY
- };
-
- void init(struct healthd_config *hc);
- bool update(void);
- status_t getProperty(int id, struct BatteryProperty *val);
- void dumpState(int fd);
-
- private:
- struct healthd_config *mHealthdConfig;
- Vector<String8> mChargerNames;
- bool mBatteryDevicePresent;
- int mBatteryFixedCapacity;
- int mBatteryFixedTemperature;
- struct BatteryProperties props;
-
- int getBatteryStatus(const char* status);
- int getBatteryHealth(const char* status);
- int readFromFile(const String8& path, char* buf, size_t size);
- PowerSupplyType readPowerSupplyType(const String8& path);
- bool getBooleanField(const String8& path);
- int getIntField(const String8& path);
-};
-
-}; // namespace android
-
-#endif // HEALTHD_BATTERY_MONTIOR_H
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index 09667a1..d28ba41 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -26,15 +26,16 @@
#include <utils/Mutex.h>
#include <utils/String16.h>
-#include "healthd.h"
+#include <healthd/healthd.h>
namespace android {
-void BatteryPropertiesRegistrar::publish() {
- defaultServiceManager()->addService(String16("batteryproperties"), this);
+void BatteryPropertiesRegistrar::publish(
+ const sp<BatteryPropertiesRegistrar>& service) {
+ defaultServiceManager()->addService(String16("batteryproperties"), service);
}
-void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) {
+void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& props) {
Mutex::Autolock _l(mRegistrationLock);
for (size_t i = 0; i < mListeners.size(); i++) {
mListeners[i]->batteryPropertiesChanged(props);
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
index 8853874..095f3d3 100644
--- a/healthd/BatteryPropertiesRegistrar.h
+++ b/healthd/BatteryPropertiesRegistrar.h
@@ -30,8 +30,8 @@
class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
public IBinder::DeathRecipient {
public:
- void publish();
- void notifyListeners(struct BatteryProperties props);
+ void publish(const sp<BatteryPropertiesRegistrar>& service);
+ void notifyListeners(const struct BatteryProperties& props);
private:
Mutex mRegistrationLock;
diff --git a/healthd/animation.h b/healthd/animation.h
new file mode 100644
index 0000000..562b689
--- /dev/null
+++ b/healthd/animation.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HEALTHD_ANIMATION_H
+#define HEALTHD_ANIMATION_H
+
+#include <inttypes.h>
+#include <string>
+
+struct GRSurface;
+struct GRFont;
+
+namespace android {
+
+#define CENTER_VAL INT_MAX
+
+struct animation {
+ struct frame {
+ int disp_time;
+ int min_level;
+ int max_level;
+
+ GRSurface* surface;
+ };
+
+ struct text_field {
+ std::string font_file;
+ int pos_x;
+ int pos_y;
+ int color_r;
+ int color_g;
+ int color_b;
+ int color_a;
+
+ GRFont* font;
+ };
+
+ std::string animation_file;
+ std::string fail_file;
+
+ text_field text_clock;
+ text_field text_percent;
+
+ bool run;
+
+ frame* frames;
+ int cur_frame;
+ int num_frames;
+ int first_frame_repeats; // Number of times to repeat the first frame in the current cycle
+
+ int cur_cycle;
+ int num_cycles; // Number of cycles to complete before blanking the screen
+
+ int cur_level; // current battery level being animated (0-100)
+ int cur_status; // current battery status - see BatteryService.h for BATTERY_STATUS_*
+};
+
+}
+
+#endif // HEALTHD_ANIMATION_H
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 1fee855..20a6bf6 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -17,8 +17,8 @@
#define LOG_TAG "healthd"
#define KLOG_LEVEL 6
-#include "healthd.h"
-#include "BatteryMonitor.h"
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
#include <errno.h>
#include <libgen.h>
@@ -52,7 +52,10 @@
.batteryCurrentNowPath = String8(String8::kEmptyString),
.batteryCurrentAvgPath = String8(String8::kEmptyString),
.batteryChargeCounterPath = String8(String8::kEmptyString),
+ .batteryFullChargePath = String8(String8::kEmptyString),
+ .batteryCycleCountPath = String8(String8::kEmptyString),
.energyCounter = NULL,
+ .boot_min_cap = 0,
.screen_on = NULL,
};
@@ -106,10 +109,17 @@
};
static struct healthd_mode_ops charger_ops = {
+#ifdef CHARGER_NO_UI
+ .init = healthd_mode_nop_init,
+ .preparetowait = healthd_mode_nop_preparetowait,
+ .heartbeat = healthd_mode_nop_heartbeat,
+ .battery_update = healthd_mode_nop_battery_update,
+#else
.init = healthd_mode_charger_init,
.preparetowait = healthd_mode_charger_preparetowait,
.heartbeat = healthd_mode_charger_heartbeat,
.battery_update = healthd_mode_charger_battery_update,
+#endif
};
static struct healthd_mode_ops recovery_ops = {
diff --git a/healthd/healthd.h b/healthd/healthd.h
deleted file mode 100644
index 4704f0b..0000000
--- a/healthd/healthd.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _HEALTHD_H_
-#define _HEALTHD_H_
-
-#include <batteryservice/BatteryService.h>
-#include <sys/types.h>
-#include <utils/Errors.h>
-#include <utils/String8.h>
-
-// periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
-// which healthd wakes up to poll health state and perform periodic chores,
-// in units of seconds:
-//
-// periodic_chores_interval_fast is used while the device is not in
-// suspend, or in suspend and connected to a charger (to watch for battery
-// overheat due to charging). The default value is 60 (1 minute). Value
-// -1 turns off periodic chores (and wakeups) in these conditions.
-//
-// periodic_chores_interval_slow is used when the device is in suspend and
-// not connected to a charger (to watch for a battery drained to zero
-// remaining capacity). The default value is 600 (10 minutes). Value -1
-// tuns off periodic chores (and wakeups) in these conditions.
-//
-// power_supply sysfs attribute file paths. Set these to specific paths
-// to use for the associated battery parameters. healthd will search for
-// appropriate power_supply attribute files to use for any paths left empty:
-//
-// batteryStatusPath: charging status (POWER_SUPPLY_PROP_STATUS)
-// batteryHealthPath: battery health (POWER_SUPPLY_PROP_HEALTH)
-// batteryPresentPath: battery present (POWER_SUPPLY_PROP_PRESENT)
-// batteryCapacityPath: remaining capacity (POWER_SUPPLY_PROP_CAPACITY)
-// batteryVoltagePath: battery voltage (POWER_SUPPLY_PROP_VOLTAGE_NOW)
-// batteryTemperaturePath: battery temperature (POWER_SUPPLY_PROP_TEMP)
-// batteryTechnologyPath: battery technology (POWER_SUPPLY_PROP_TECHNOLOGY)
-// batteryCurrentNowPath: battery current (POWER_SUPPLY_PROP_CURRENT_NOW)
-// batteryChargeCounterPath: battery accumulated charge
-// (POWER_SUPPLY_PROP_CHARGE_COUNTER)
-
-struct healthd_config {
- int periodic_chores_interval_fast;
- int periodic_chores_interval_slow;
-
- android::String8 batteryStatusPath;
- android::String8 batteryHealthPath;
- android::String8 batteryPresentPath;
- android::String8 batteryCapacityPath;
- android::String8 batteryVoltagePath;
- android::String8 batteryTemperaturePath;
- android::String8 batteryTechnologyPath;
- android::String8 batteryCurrentNowPath;
- android::String8 batteryCurrentAvgPath;
- android::String8 batteryChargeCounterPath;
-
- int (*energyCounter)(int64_t *);
- bool (*screen_on)(android::BatteryProperties *props);
-};
-
-// Global helper functions
-
-int healthd_register_event(int fd, void (*handler)(uint32_t));
-void healthd_battery_update();
-android::status_t healthd_get_property(int id,
- struct android::BatteryProperty *val);
-void healthd_dump_battery_state(int fd);
-
-struct healthd_mode_ops {
- void (*init)(struct healthd_config *config);
- int (*preparetowait)(void);
- void (*heartbeat)(void);
- void (*battery_update)(struct android::BatteryProperties *props);
-};
-
-extern struct healthd_mode_ops *healthd_mode_ops;
-
-// Charger mode
-
-void healthd_mode_charger_init(struct healthd_config *config);
-int healthd_mode_charger_preparetowait(void);
-void healthd_mode_charger_heartbeat(void);
-void healthd_mode_charger_battery_update(
- struct android::BatteryProperties *props);
-
-// The following are implemented in libhealthd_board to handle board-specific
-// behavior.
-//
-// healthd_board_init() is called at startup time to modify healthd's
-// configuration according to board-specific requirements. config
-// points to the healthd configuration values described above. To use default
-// values, this function can simply return without modifying the fields of the
-// config parameter.
-
-void healthd_board_init(struct healthd_config *config);
-
-// Process updated battery property values. This function is called when
-// the kernel sends updated battery status via a uevent from the power_supply
-// subsystem, or when updated values are polled by healthd, as for periodic
-// poll of battery state.
-//
-// props are the battery properties read from the kernel. These values may
-// be modified in this call, prior to sending the modified values to the
-// Android runtime.
-//
-// Return 0 to indicate the usual kernel log battery status heartbeat message
-// is to be logged, or non-zero to prevent logging this information.
-
-int healthd_board_battery_update(struct android::BatteryProperties *props);
-
-#endif /* _HEALTHD_H_ */
diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp
index ed4ddb4..eb55773 100644
--- a/healthd/healthd_board_default.cpp
+++ b/healthd/healthd_board_default.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <healthd.h>
+#include <healthd/healthd.h>
void healthd_board_init(struct healthd_config*)
{
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
index fd153a2..323ef52 100644
--- a/healthd/healthd_mode_android.cpp
+++ b/healthd/healthd_mode_android.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "healthd-android"
-#include "healthd.h"
+#include <healthd/healthd.h>
#include "BatteryPropertiesRegistrar.h"
#include <binder/IPCThreadState.h>
@@ -58,5 +58,5 @@
}
gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
- gBatteryPropertiesRegistrar->publish();
+ gBatteryPropertiesRegistrar->publish(gBatteryPropertiesRegistrar);
}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 78f8403..2f69372 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -30,6 +30,11 @@
#include <time.h>
#include <unistd.h>
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
#include <sys/socket.h>
#include <linux/netlink.h>
@@ -39,14 +44,18 @@
#include <cutils/misc.h>
#include <cutils/uevent.h>
#include <cutils/properties.h>
+#include <minui/minui.h>
#ifdef CHARGER_ENABLE_SUSPEND
#include <suspend/autosuspend.h>
#endif
-#include "minui/minui.h"
+#include "animation.h"
+#include "AnimationParser.h"
-#include "healthd.h"
+#include <healthd/healthd.h>
+
+using namespace android;
char *locale;
@@ -58,7 +67,7 @@
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#define MSEC_PER_SEC (1000LL)
#define NSEC_PER_MSEC (1000000LL)
@@ -67,8 +76,6 @@
#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
-#define BATTERY_FULL_THRESH 95
-
#define LAST_KMSG_PATH "/proc/last_kmsg"
#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops"
#define LAST_KMSG_MAX_SZ (32 * 1024)
@@ -77,34 +84,14 @@
#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
+static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
+
struct key_state {
bool pending;
bool down;
int64_t timestamp;
};
-struct frame {
- int disp_time;
- int min_capacity;
- bool level_only;
-
- GRSurface* surface;
-};
-
-struct animation {
- bool run;
-
- struct frame *frames;
- int cur_frame;
- int num_frames;
-
- int cur_cycle;
- int num_cycles;
-
- /* current capacity being animated */
- int capacity;
-};
-
struct charger {
bool have_battery_state;
bool charger_connected;
@@ -116,57 +103,87 @@
struct animation *batt_anim;
GRSurface* surf_unknown;
+ int boot_min_cap;
};
-static struct frame batt_anim_frames[] = {
- {
- .disp_time = 750,
- .min_capacity = 0,
- .level_only = false,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_capacity = 20,
- .level_only = false,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_capacity = 40,
- .level_only = false,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_capacity = 60,
- .level_only = false,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_capacity = 80,
- .level_only = true,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_capacity = BATTERY_FULL_THRESH,
- .level_only = false,
- .surface = NULL,
- },
-};
+static const struct animation BASE_ANIMATION = {
+ .text_clock = {
+ .pos_x = 0,
+ .pos_y = 0,
-static struct animation battery_animation = {
+ .color_r = 255,
+ .color_g = 255,
+ .color_b = 255,
+ .color_a = 255,
+
+ .font = nullptr,
+ },
+ .text_percent = {
+ .pos_x = 0,
+ .pos_y = 0,
+
+ .color_r = 255,
+ .color_g = 255,
+ .color_b = 255,
+ .color_a = 255,
+ },
+
.run = false,
- .frames = batt_anim_frames,
+
+ .frames = nullptr,
.cur_frame = 0,
- .num_frames = ARRAY_SIZE(batt_anim_frames),
+ .num_frames = 0,
+ .first_frame_repeats = 2,
+
.cur_cycle = 0,
.num_cycles = 3,
- .capacity = 0,
+
+ .cur_level = 0,
+ .cur_status = BATTERY_STATUS_UNKNOWN,
};
+
+static struct animation::frame default_animation_frames[] = {
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 19,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 39,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 59,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 79,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 80,
+ .max_level = 95,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 100,
+ .surface = NULL,
+ },
+};
+
+static struct animation battery_animation = BASE_ANIMATION;
+
static struct charger charger_state;
static struct healthd_config *healthd_config;
static struct android::BatteryProperties *batt_prop;
@@ -256,13 +273,13 @@
static int draw_text(const char *str, int x, int y)
{
- int str_len_px = gr_measure(str);
+ int str_len_px = gr_measure(gr_sys_font(), str);
if (x < 0)
x = (gr_fb_width() - str_len_px) / 2;
if (y < 0)
y = (gr_fb_height() - char_height) / 2;
- gr_text(x, y, str, 0);
+ gr_text(gr_sys_font(), x, y, str, 0);
return y + char_height;
}
@@ -272,8 +289,79 @@
gr_color(0xa4, 0xc6, 0x39, 255);
}
+// Negative x or y coordinates position the text away from the opposite edge that positive ones do.
+void determine_xy(const animation::text_field& field, const int length, int* x, int* y)
+{
+ *x = field.pos_x;
+ *y = field.pos_y;
+
+ int str_len_px = length * field.font->char_width;
+ if (field.pos_x == CENTER_VAL) {
+ *x = (gr_fb_width() - str_len_px) / 2;
+ } else if (field.pos_x >= 0) {
+ *x = field.pos_x;
+ } else { // position from max edge
+ *x = gr_fb_width() + field.pos_x - str_len_px;
+ }
+
+ if (field.pos_y == CENTER_VAL) {
+ *y = (gr_fb_height() - field.font->char_height) / 2;
+ } else if (field.pos_y >= 0) {
+ *y = field.pos_y;
+ } else { // position from max edge
+ *y = gr_fb_height() + field.pos_y - field.font->char_height;
+ }
+}
+
+static void draw_clock(const animation& anim)
+{
+ static constexpr char CLOCK_FORMAT[] = "%H:%M";
+ static constexpr int CLOCK_LENGTH = 6;
+
+ const animation::text_field& field = anim.text_clock;
+
+ if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) return;
+
+ time_t rawtime;
+ time(&rawtime);
+ struct tm* time_info = localtime(&rawtime);
+
+ char clock_str[CLOCK_LENGTH];
+ size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
+ if (length != CLOCK_LENGTH - 1) {
+ LOGE("Could not format time\n");
+ return;
+ }
+
+ int x, y;
+ determine_xy(field, length, &x, &y);
+
+ LOGV("drawing clock %s %d %d\n", clock_str, x, y);
+ gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+ gr_text(field.font, x, y, clock_str, false);
+}
+
+static void draw_percent(const animation& anim)
+{
+ if (anim.cur_level <= 0 || anim.cur_status != BATTERY_STATUS_CHARGING) return;
+
+ const animation::text_field& field = anim.text_percent;
+ if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
+ return;
+ }
+
+ std::string str = base::StringPrintf("%d%%", anim.cur_level);
+
+ int x, y;
+ determine_xy(field, str.size(), &x, &y);
+
+ LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
+ gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+ gr_text(field.font, x, y, str.c_str(), false);
+}
+
/* returns the last y-offset of where the surface ends */
-static int draw_surface_centered(struct charger* /*charger*/, GRSurface* surface)
+static int draw_surface_centered(GRSurface* surface)
{
int w;
int h;
@@ -294,7 +382,7 @@
{
int y;
if (charger->surf_unknown) {
- draw_surface_centered(charger, charger->surf_unknown);
+ draw_surface_centered(charger->surf_unknown);
} else {
android_green();
y = draw_text("Charging!", -1, -1);
@@ -302,17 +390,19 @@
}
}
-static void draw_battery(struct charger *charger)
+static void draw_battery(const struct charger* charger)
{
- struct animation *batt_anim = charger->batt_anim;
- struct frame *frame = &batt_anim->frames[batt_anim->cur_frame];
+ const struct animation& anim = *charger->batt_anim;
+ const struct animation::frame& frame = anim.frames[anim.cur_frame];
- if (batt_anim->num_frames != 0) {
- draw_surface_centered(charger, frame->surface);
+ if (anim.num_frames != 0) {
+ draw_surface_centered(frame.surface);
LOGV("drawing frame #%d min_cap=%d time=%d\n",
- batt_anim->cur_frame, frame->min_capacity,
- frame->disp_time);
+ anim.cur_frame, frame.min_level,
+ frame.disp_time);
}
+ draw_clock(anim);
+ draw_percent(anim);
}
static void redraw_screen(struct charger *charger)
@@ -322,7 +412,7 @@
clear_screen();
/* try to display *something* */
- if (batt_anim->capacity < 0 || batt_anim->num_frames == 0)
+ if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
draw_unknown(charger);
else
draw_battery(charger);
@@ -341,16 +431,33 @@
anim->run = false;
}
+static void init_status_display(struct animation* anim)
+{
+ int res;
+
+ if (!anim->text_clock.font_file.empty()) {
+ if ((res =
+ gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {
+ LOGE("Could not load time font (%d)\n", res);
+ }
+ }
+
+ if (!anim->text_percent.font_file.empty()) {
+ if ((res =
+ gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {
+ LOGE("Could not load percent font (%d)\n", res);
+ }
+ }
+}
+
static void update_screen_state(struct charger *charger, int64_t now)
{
struct animation *batt_anim = charger->batt_anim;
int disp_time;
- if (!batt_anim->run || now < charger->next_screen_transition)
- return;
+ if (!batt_anim->run || now < charger->next_screen_transition) return;
if (!minui_inited) {
-
if (healthd_config && healthd_config->screen_on) {
if (!healthd_config->screen_on(batt_prop)) {
LOGV("[%" PRId64 "] leave screen off\n", now);
@@ -363,7 +470,8 @@
}
gr_init();
- gr_font_size(&char_width, &char_height);
+ gr_font_size(gr_sys_font(), &char_width, &char_height);
+ init_status_display(batt_anim);
#ifndef CHARGER_DISABLE_INIT_BLANK
gr_fb_blank(true);
@@ -372,7 +480,7 @@
}
/* animation is over, blank screen and leave */
- if (batt_anim->cur_cycle == batt_anim->num_cycles) {
+ if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
reset_animation(batt_anim);
charger->next_screen_transition = -1;
gr_fb_blank(true);
@@ -388,21 +496,24 @@
if (batt_anim->cur_frame == 0) {
LOGV("[%" PRId64 "] animation starting\n", now);
- if (batt_prop && batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
- int i;
+ if (batt_prop) {
+ batt_anim->cur_level = batt_prop->batteryLevel;
+ batt_anim->cur_status = batt_prop->batteryStatus;
+ if (batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
+ /* find first frame given current battery level */
+ for (int i = 0; i < batt_anim->num_frames; i++) {
+ if (batt_anim->cur_level >= batt_anim->frames[i].min_level &&
+ batt_anim->cur_level <= batt_anim->frames[i].max_level) {
+ batt_anim->cur_frame = i;
+ break;
+ }
+ }
- /* find first frame given current capacity */
- for (i = 1; i < batt_anim->num_frames; i++) {
- if (batt_prop->batteryLevel < batt_anim->frames[i].min_capacity)
- break;
+ // repeat the first frame first_frame_repeats times
+ disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
+ batt_anim->first_frame_repeats;
}
- batt_anim->cur_frame = i - 1;
-
- /* show the first frame for twice as long */
- disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2;
}
- if (batt_prop)
- batt_anim->capacity = batt_prop->batteryLevel;
}
/* unblank the screen on first cycle */
@@ -415,8 +526,8 @@
/* if we don't have anim frames, we only have one image, so just bump
* the cycle counter and exit
*/
- if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) {
- LOGV("[%" PRId64 "] animation missing or unknown battery status\n", now);
+ if (batt_anim->num_frames == 0 || batt_anim->cur_level < 0) {
+ LOGW("[%" PRId64 "] animation missing or unknown battery status\n", now);
charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
batt_anim->cur_cycle++;
return;
@@ -431,12 +542,11 @@
if (charger->charger_connected) {
batt_anim->cur_frame++;
- /* if the frame is used for level-only, that is only show it when it's
- * the current level, skip it during the animation.
- */
while (batt_anim->cur_frame < batt_anim->num_frames &&
- batt_anim->frames[batt_anim->cur_frame].level_only)
+ (batt_anim->cur_level < batt_anim->frames[batt_anim->cur_frame].min_level ||
+ batt_anim->cur_level > batt_anim->frames[batt_anim->cur_frame].max_level)) {
batt_anim->cur_frame++;
+ }
if (batt_anim->cur_frame >= batt_anim->num_frames) {
batt_anim->cur_cycle++;
batt_anim->cur_frame = 0;
@@ -455,9 +565,8 @@
}
}
-static int set_key_callback(int code, int value, void *data)
+static int set_key_callback(struct charger *charger, int code, int value)
{
- struct charger *charger = (struct charger *)data;
int64_t now = curr_time_ms();
int down = !!value;
@@ -492,7 +601,7 @@
{
if (ev->type != EV_KEY)
return;
- set_key_callback(ev->code, ev->value, charger);
+ set_key_callback(charger, ev->code, ev->value);
}
static void set_next_key_check(struct charger *charger,
@@ -520,19 +629,29 @@
LOGW("[%" PRId64 "] booting from charger mode\n", now);
property_set("sys.boot_from_charger_mode", "1");
} else {
- LOGW("[%" PRId64 "] rebooting\n", now);
- android_reboot(ANDROID_RB_RESTART, 0, 0);
+ if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
+ LOGW("[%" PRId64 "] rebooting\n", now);
+ android_reboot(ANDROID_RB_RESTART, 0, 0);
+ } else {
+ LOGV("[%" PRId64 "] ignore power-button press, battery level "
+ "less than minimum\n", now);
+ }
}
} else {
/* if the key is pressed but timeout hasn't expired,
* make sure we wake up at the right-ish time to check
*/
set_next_key_check(charger, key, POWER_ON_KEY_TIME);
+
+ /* Turn on the display and kick animation on power-key press
+ * rather than on key release
+ */
+ kick_animation(charger->batt_anim);
+ request_suspend(false);
}
} else {
/* if the power key got released, force screen state cycle */
if (key->pending) {
- request_suspend(false);
kick_animation(charger->batt_anim);
}
}
@@ -555,6 +674,11 @@
return;
if (!charger->charger_connected) {
+
+ /* Last cycle would have stopped at the extreme top of battery-icon
+ * Need to show the correct level corresponding to capacity.
+ */
+ kick_animation(charger->batt_anim);
request_suspend(false);
if (charger->next_pwr_check == -1) {
charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
@@ -634,9 +758,8 @@
return (int)timeout;
}
-static int input_callback(int fd, unsigned int epevents, void *data)
+static int input_callback(struct charger *charger, int fd, unsigned int epevents)
{
- struct charger *charger = (struct charger *)data;
struct input_event ev;
int ret;
@@ -656,6 +779,52 @@
ev_dispatch();
}
+animation* init_animation()
+{
+ bool parse_success;
+
+ std::string content;
+ if (base::ReadFileToString(animation_desc_path, &content)) {
+ parse_success = parse_animation_desc(content, &battery_animation);
+ } else {
+ LOGW("Could not open animation description at %s\n", animation_desc_path);
+ parse_success = false;
+ }
+
+ if (!parse_success) {
+ LOGW("Could not parse animation description. Using default animation.\n");
+ battery_animation = BASE_ANIMATION;
+ battery_animation.animation_file.assign("charger/battery_scale");
+ battery_animation.frames = default_animation_frames;
+ battery_animation.num_frames = ARRAY_SIZE(default_animation_frames);
+ }
+ if (battery_animation.fail_file.empty()) {
+ battery_animation.fail_file.assign("charger/battery_fail");
+ }
+
+ LOGV("Animation Description:\n");
+ LOGV(" animation: %d %d '%s' (%d)\n",
+ battery_animation.num_cycles, battery_animation.first_frame_repeats,
+ battery_animation.animation_file.c_str(), battery_animation.num_frames);
+ LOGV(" fail_file: '%s'\n", battery_animation.fail_file.c_str());
+ LOGV(" clock: %d %d %d %d %d %d '%s'\n",
+ battery_animation.text_clock.pos_x, battery_animation.text_clock.pos_y,
+ battery_animation.text_clock.color_r, battery_animation.text_clock.color_g,
+ battery_animation.text_clock.color_b, battery_animation.text_clock.color_a,
+ battery_animation.text_clock.font_file.c_str());
+ LOGV(" percent: %d %d %d %d %d %d '%s'\n",
+ battery_animation.text_percent.pos_x, battery_animation.text_percent.pos_y,
+ battery_animation.text_percent.color_r, battery_animation.text_percent.color_g,
+ battery_animation.text_percent.color_b, battery_animation.text_percent.color_a,
+ battery_animation.text_percent.font_file.c_str());
+ for (int i = 0; i < battery_animation.num_frames; i++) {
+ LOGV(" frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
+ battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
+ }
+
+ return &battery_animation;
+}
+
void healthd_mode_charger_init(struct healthd_config* config)
{
int ret;
@@ -667,42 +836,52 @@
LOGW("--------------- STARTING CHARGER MODE ---------------\n");
- ret = ev_init(input_callback, charger);
+ ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1,
+ std::placeholders::_2));
if (!ret) {
epollfd = ev_get_epollfd();
healthd_register_event(epollfd, charger_event_handler);
}
- ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
- if (ret < 0) {
- LOGE("Cannot load battery_fail image\n");
- charger->surf_unknown = NULL;
- }
+ struct animation* anim = init_animation();
+ charger->batt_anim = anim;
- charger->batt_anim = &battery_animation;
-
- GRSurface** scale_frames;
- int scale_count;
- ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_frames);
+ ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
if (ret < 0) {
- LOGE("Cannot load battery_scale image\n");
- charger->batt_anim->num_frames = 0;
- charger->batt_anim->num_cycles = 1;
- } else if (scale_count != charger->batt_anim->num_frames) {
- LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n",
- scale_count, charger->batt_anim->num_frames);
- charger->batt_anim->num_frames = 0;
- charger->batt_anim->num_cycles = 1;
- } else {
- for (i = 0; i < charger->batt_anim->num_frames; i++) {
- charger->batt_anim->frames[i].surface = scale_frames[i];
+ LOGE("Cannot load custom battery_fail image. Reverting to built in.\n");
+ ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
+ if (ret < 0) {
+ LOGE("Cannot load built in battery_fail image\n");
+ charger->surf_unknown = NULL;
}
}
- ev_sync_key_state(set_key_callback, charger);
+ GRSurface** scale_frames;
+ int scale_count;
+ int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text
+ // chunk). We are using hard-coded frame.disp_time instead.
+ ret = res_create_multi_display_surface(anim->animation_file.c_str(),
+ &scale_count, &scale_fps, &scale_frames);
+ if (ret < 0) {
+ LOGE("Cannot load battery_scale image\n");
+ anim->num_frames = 0;
+ anim->num_cycles = 1;
+ } else if (scale_count != anim->num_frames) {
+ LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n",
+ scale_count, anim->num_frames);
+ anim->num_frames = 0;
+ anim->num_cycles = 1;
+ } else {
+ for (i = 0; i < anim->num_frames; i++) {
+ anim->frames[i].surface = scale_frames[i];
+ }
+ }
+ ev_sync_key_state(std::bind(&set_key_callback, charger, std::placeholders::_1,
+ std::placeholders::_2));
charger->next_screen_transition = -1;
charger->next_key_check = -1;
charger->next_pwr_check = -1;
healthd_config = config;
+ charger->boot_min_cap = config->boot_min_cap;
}
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
new file mode 100644
index 0000000..8865a7d
--- /dev/null
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HEALTHD_BATTERYMONITOR_H
+#define HEALTHD_BATTERYMONITOR_H
+
+#include <batteryservice/BatteryService.h>
+#include <binder/IInterface.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <healthd/healthd.h>
+
+namespace android {
+
+class BatteryMonitor {
+ public:
+
+ enum PowerSupplyType {
+ ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,
+ ANDROID_POWER_SUPPLY_TYPE_AC,
+ ANDROID_POWER_SUPPLY_TYPE_USB,
+ ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
+ ANDROID_POWER_SUPPLY_TYPE_BATTERY
+ };
+
+ BatteryMonitor();
+ void init(struct healthd_config *hc);
+ bool update(void);
+ int getChargeStatus();
+ status_t getProperty(int id, struct BatteryProperty *val);
+ void dumpState(int fd);
+
+ private:
+ struct healthd_config *mHealthdConfig;
+ Vector<String8> mChargerNames;
+ bool mBatteryDevicePresent;
+ bool mAlwaysPluggedDevice;
+ int mBatteryFixedCapacity;
+ int mBatteryFixedTemperature;
+ struct BatteryProperties props;
+
+ int getBatteryStatus(const char* status);
+ int getBatteryHealth(const char* status);
+ int readFromFile(const String8& path, std::string* buf);
+ PowerSupplyType readPowerSupplyType(const String8& path);
+ bool getBooleanField(const String8& path);
+ int getIntField(const String8& path);
+};
+
+}; // namespace android
+
+#endif // HEALTHD_BATTERY_MONTIOR_H
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
new file mode 100644
index 0000000..34ea55f
--- /dev/null
+++ b/healthd/include/healthd/healthd.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _HEALTHD_H_
+#define _HEALTHD_H_
+
+#include <batteryservice/BatteryService.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+// periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
+// which healthd wakes up to poll health state and perform periodic chores,
+// in units of seconds:
+//
+// periodic_chores_interval_fast is used while the device is not in
+// suspend, or in suspend and connected to a charger (to watch for battery
+// overheat due to charging). The default value is 60 (1 minute). Value
+// -1 turns off periodic chores (and wakeups) in these conditions.
+//
+// periodic_chores_interval_slow is used when the device is in suspend and
+// not connected to a charger (to watch for a battery drained to zero
+// remaining capacity). The default value is 600 (10 minutes). Value -1
+// tuns off periodic chores (and wakeups) in these conditions.
+//
+// power_supply sysfs attribute file paths. Set these to specific paths
+// to use for the associated battery parameters. healthd will search for
+// appropriate power_supply attribute files to use for any paths left empty:
+//
+// batteryStatusPath: charging status (POWER_SUPPLY_PROP_STATUS)
+// batteryHealthPath: battery health (POWER_SUPPLY_PROP_HEALTH)
+// batteryPresentPath: battery present (POWER_SUPPLY_PROP_PRESENT)
+// batteryCapacityPath: remaining capacity (POWER_SUPPLY_PROP_CAPACITY)
+// batteryVoltagePath: battery voltage (POWER_SUPPLY_PROP_VOLTAGE_NOW)
+// batteryTemperaturePath: battery temperature (POWER_SUPPLY_PROP_TEMP)
+// batteryTechnologyPath: battery technology (POWER_SUPPLY_PROP_TECHNOLOGY)
+// batteryCurrentNowPath: battery current (POWER_SUPPLY_PROP_CURRENT_NOW)
+// batteryChargeCounterPath: battery accumulated charge
+// (POWER_SUPPLY_PROP_CHARGE_COUNTER)
+
+struct healthd_config {
+ int periodic_chores_interval_fast;
+ int periodic_chores_interval_slow;
+
+ android::String8 batteryStatusPath;
+ android::String8 batteryHealthPath;
+ android::String8 batteryPresentPath;
+ android::String8 batteryCapacityPath;
+ android::String8 batteryVoltagePath;
+ android::String8 batteryTemperaturePath;
+ android::String8 batteryTechnologyPath;
+ android::String8 batteryCurrentNowPath;
+ android::String8 batteryCurrentAvgPath;
+ android::String8 batteryChargeCounterPath;
+ android::String8 batteryFullChargePath;
+ android::String8 batteryCycleCountPath;
+
+ int (*energyCounter)(int64_t *);
+ int boot_min_cap;
+ bool (*screen_on)(android::BatteryProperties *props);
+};
+
+// Global helper functions
+
+int healthd_register_event(int fd, void (*handler)(uint32_t));
+void healthd_battery_update();
+android::status_t healthd_get_property(int id,
+ struct android::BatteryProperty *val);
+void healthd_dump_battery_state(int fd);
+
+struct healthd_mode_ops {
+ void (*init)(struct healthd_config *config);
+ int (*preparetowait)(void);
+ void (*heartbeat)(void);
+ void (*battery_update)(struct android::BatteryProperties *props);
+};
+
+extern struct healthd_mode_ops *healthd_mode_ops;
+
+// Charger mode
+
+void healthd_mode_charger_init(struct healthd_config *config);
+int healthd_mode_charger_preparetowait(void);
+void healthd_mode_charger_heartbeat(void);
+void healthd_mode_charger_battery_update(
+ struct android::BatteryProperties *props);
+
+// The following are implemented in libhealthd_board to handle board-specific
+// behavior.
+//
+// healthd_board_init() is called at startup time to modify healthd's
+// configuration according to board-specific requirements. config
+// points to the healthd configuration values described above. To use default
+// values, this function can simply return without modifying the fields of the
+// config parameter.
+
+void healthd_board_init(struct healthd_config *config);
+
+// Process updated battery property values. This function is called when
+// the kernel sends updated battery status via a uevent from the power_supply
+// subsystem, or when updated values are polled by healthd, as for periodic
+// poll of battery state.
+//
+// props are the battery properties read from the kernel. These values may
+// be modified in this call, prior to sending the modified values to the
+// Android runtime.
+//
+// Return 0 to indicate the usual kernel log battery status heartbeat message
+// is to be logged, or non-zero to prevent logging this information.
+
+int healthd_board_battery_update(struct android::BatteryProperties *props);
+
+#endif /* _HEALTHD_H_ */
diff --git a/healthd/tests/Android.mk b/healthd/tests/Android.mk
new file mode 100644
index 0000000..87e8862
--- /dev/null
+++ b/healthd/tests/Android.mk
@@ -0,0 +1,21 @@
+# Copyright 2016 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ AnimationParser_test.cpp \
+
+LOCAL_MODULE := healthd_test
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_LIBRARIES := \
+ libhealthd_internal \
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libbase \
+ libcutils \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/healthd/tests/AnimationParser_test.cpp b/healthd/tests/AnimationParser_test.cpp
new file mode 100644
index 0000000..2fc3185
--- /dev/null
+++ b/healthd/tests/AnimationParser_test.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AnimationParser.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+TEST(AnimationParserTest, Test_can_ignore_line) {
+ EXPECT_TRUE(can_ignore_line(""));
+ EXPECT_TRUE(can_ignore_line(" "));
+ EXPECT_TRUE(can_ignore_line("#"));
+ EXPECT_TRUE(can_ignore_line(" # comment"));
+
+ EXPECT_FALSE(can_ignore_line("text"));
+ EXPECT_FALSE(can_ignore_line("text # comment"));
+ EXPECT_FALSE(can_ignore_line(" text"));
+ EXPECT_FALSE(can_ignore_line(" text # comment"));
+}
+
+TEST(AnimationParserTest, Test_remove_prefix) {
+ static const char TEST_STRING[] = "abcdef";
+ const char* rest = nullptr;
+ EXPECT_FALSE(remove_prefix(TEST_STRING, "def", &rest));
+ // Ignore strings that only consist of the prefix
+ EXPECT_FALSE(remove_prefix(TEST_STRING, TEST_STRING, &rest));
+
+ EXPECT_TRUE(remove_prefix(TEST_STRING, "abc", &rest));
+ EXPECT_STREQ("def", rest);
+
+ EXPECT_TRUE(remove_prefix(" abcdef", "abc", &rest));
+ EXPECT_STREQ("def", rest);
+}
+
+TEST(AnimationParserTest, Test_parse_text_field) {
+ static const char TEST_FILE_NAME[] = "font_file";
+ static const int TEST_X = 3;
+ static const int TEST_Y = 6;
+ static const int TEST_R = 1;
+ static const int TEST_G = 2;
+ static const int TEST_B = 4;
+ static const int TEST_A = 8;
+
+ static const char TEST_XCENT_YCENT[] = "c c 1 2 4 8 font_file ";
+ static const char TEST_XCENT_YVAL[] = "c 6 1 2 4 8 font_file ";
+ static const char TEST_XVAL_YCENT[] = "3 c 1 2 4 8 font_file ";
+ static const char TEST_XVAL_YVAL[] = "3 6 1 2 4 8 font_file ";
+ static const char TEST_BAD_MISSING[] = "c c 1 2 4 font_file";
+ static const char TEST_BAD_NO_FILE[] = "c c 1 2 4 8";
+
+ animation::text_field out;
+
+ EXPECT_TRUE(parse_text_field(TEST_XCENT_YCENT, &out));
+ EXPECT_EQ(CENTER_VAL, out.pos_x);
+ EXPECT_EQ(CENTER_VAL, out.pos_y);
+ EXPECT_EQ(TEST_R, out.color_r);
+ EXPECT_EQ(TEST_G, out.color_g);
+ EXPECT_EQ(TEST_B, out.color_b);
+ EXPECT_EQ(TEST_A, out.color_a);
+ EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());
+
+ EXPECT_TRUE(parse_text_field(TEST_XCENT_YVAL, &out));
+ EXPECT_EQ(CENTER_VAL, out.pos_x);
+ EXPECT_EQ(TEST_Y, out.pos_y);
+ EXPECT_EQ(TEST_R, out.color_r);
+ EXPECT_EQ(TEST_G, out.color_g);
+ EXPECT_EQ(TEST_B, out.color_b);
+ EXPECT_EQ(TEST_A, out.color_a);
+ EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());
+
+ EXPECT_TRUE(parse_text_field(TEST_XVAL_YCENT, &out));
+ EXPECT_EQ(TEST_X, out.pos_x);
+ EXPECT_EQ(CENTER_VAL, out.pos_y);
+ EXPECT_EQ(TEST_R, out.color_r);
+ EXPECT_EQ(TEST_G, out.color_g);
+ EXPECT_EQ(TEST_B, out.color_b);
+ EXPECT_EQ(TEST_A, out.color_a);
+ EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());
+
+ EXPECT_TRUE(parse_text_field(TEST_XVAL_YVAL, &out));
+ EXPECT_EQ(TEST_X, out.pos_x);
+ EXPECT_EQ(TEST_Y, out.pos_y);
+ EXPECT_EQ(TEST_R, out.color_r);
+ EXPECT_EQ(TEST_G, out.color_g);
+ EXPECT_EQ(TEST_B, out.color_b);
+ EXPECT_EQ(TEST_A, out.color_a);
+ EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());
+
+ EXPECT_FALSE(parse_text_field(TEST_BAD_MISSING, &out));
+ EXPECT_FALSE(parse_text_field(TEST_BAD_NO_FILE, &out));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_basic) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Basic animation
+ animation: 5 1 test/animation_file
+ frame: 1000 0 100
+ )desc";
+ animation anim;
+
+ EXPECT_TRUE(parse_animation_desc(TEST_ANIMATION, &anim));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_bad_no_animation_line) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Bad animation
+ frame: 1000 90 10
+ )desc";
+ animation anim;
+
+ EXPECT_FALSE(parse_animation_desc(TEST_ANIMATION, &anim));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_bad_no_frame) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Bad animation
+ animation: 5 1 test/animation_file
+ )desc";
+ animation anim;
+
+ EXPECT_FALSE(parse_animation_desc(TEST_ANIMATION, &anim));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_bad_animation_line_format) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Bad animation
+ animation: 5 1
+ frame: 1000 90 10
+ )desc";
+ animation anim;
+
+ EXPECT_FALSE(parse_animation_desc(TEST_ANIMATION, &anim));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_full) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Full animation
+ animation: 5 1 test/animation_file
+ clock_display: 11 12 13 14 15 16 test/time_font
+ percent_display: 21 22 23 24 25 26 test/percent_font
+
+ frame: 10 20 30
+ frame: 40 50 60
+ )desc";
+ animation anim;
+
+ EXPECT_TRUE(parse_animation_desc(TEST_ANIMATION, &anim));
+
+ EXPECT_EQ(5, anim.num_cycles);
+ EXPECT_EQ(1, anim.first_frame_repeats);
+ EXPECT_STREQ("test/animation_file", anim.animation_file.c_str());
+
+ EXPECT_EQ(11, anim.text_clock.pos_x);
+ EXPECT_EQ(12, anim.text_clock.pos_y);
+ EXPECT_EQ(13, anim.text_clock.color_r);
+ EXPECT_EQ(14, anim.text_clock.color_g);
+ EXPECT_EQ(15, anim.text_clock.color_b);
+ EXPECT_EQ(16, anim.text_clock.color_a);
+ EXPECT_STREQ("test/time_font", anim.text_clock.font_file.c_str());
+
+ EXPECT_EQ(21, anim.text_percent.pos_x);
+ EXPECT_EQ(22, anim.text_percent.pos_y);
+ EXPECT_EQ(23, anim.text_percent.color_r);
+ EXPECT_EQ(24, anim.text_percent.color_g);
+ EXPECT_EQ(25, anim.text_percent.color_b);
+ EXPECT_EQ(26, anim.text_percent.color_a);
+ EXPECT_STREQ("test/percent_font", anim.text_percent.font_file.c_str());
+
+ EXPECT_EQ(2, anim.num_frames);
+
+ EXPECT_EQ(10, anim.frames[0].disp_time);
+ EXPECT_EQ(20, anim.frames[0].min_level);
+ EXPECT_EQ(30, anim.frames[0].max_level);
+
+ EXPECT_EQ(40, anim.frames[1].disp_time);
+ EXPECT_EQ(50, anim.frames[1].min_level);
+ EXPECT_EQ(60, anim.frames[1].max_level);
+}
diff --git a/include/android/log.h b/include/android/log.h
deleted file mode 100644
index 1c171b7..0000000
--- a/include/android/log.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_LOG_H
-#define _ANDROID_LOG_H
-
-/******************************************************************
- *
- * IMPORTANT NOTICE:
- *
- * This file is part of Android's set of stable system headers
- * exposed by the Android NDK (Native Development Kit) since
- * platform release 1.5
- *
- * Third-party source AND binary code relies on the definitions
- * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
- *
- * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
- * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
- * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
- * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
- */
-
-/*
- * Support routines to send messages to the Android in-kernel log buffer,
- * which can later be accessed through the 'logcat' utility.
- *
- * Each log message must have
- * - a priority
- * - a log tag
- * - some text
- *
- * The tag normally corresponds to the component that emits the log message,
- * and should be reasonably small.
- *
- * Log message text may be truncated to less than an implementation-specific
- * limit (e.g. 1023 characters max).
- *
- * Note that a newline character ("\n") will be appended automatically to your
- * log message, if not already there. It is not possible to send several messages
- * and have them appear on a single line in logcat.
- *
- * PLEASE USE LOGS WITH MODERATION:
- *
- * - Sending log messages eats CPU and slow down your application and the
- * system.
- *
- * - The circular log buffer is pretty small (<64KB), sending many messages
- * might push off other important log messages from the rest of the system.
- *
- * - In release builds, only send log messages to account for exceptional
- * conditions.
- *
- * NOTE: These functions MUST be implemented by /system/lib/liblog.so
- */
-
-#include <stdarg.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Android log priority values, in ascending priority order.
- */
-typedef enum android_LogPriority {
- ANDROID_LOG_UNKNOWN = 0,
- ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
- ANDROID_LOG_VERBOSE,
- ANDROID_LOG_DEBUG,
- ANDROID_LOG_INFO,
- ANDROID_LOG_WARN,
- ANDROID_LOG_ERROR,
- ANDROID_LOG_FATAL,
- ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
-} android_LogPriority;
-
-/*
- * Send a simple string to the log.
- */
-int __android_log_write(int prio, const char *tag, const char *text);
-
-/*
- * Send a formatted string to the log, used like printf(fmt,...)
- */
-int __android_log_print(int prio, const char *tag, const char *fmt, ...)
-#if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
- __attribute__ ((format(gnu_printf, 3, 4)))
-#else
- __attribute__ ((format(printf, 3, 4)))
-#endif
-#else
- __attribute__ ((format(printf, 3, 4)))
-#endif
-#endif
- ;
-
-/*
- * A variant of __android_log_print() that takes a va_list to list
- * additional parameters.
- */
-int __android_log_vprint(int prio, const char *tag,
- const char *fmt, va_list ap);
-
-/*
- * Log an assertion failure and abort the process to have a chance
- * to inspect it if a debugger is attached. This uses the FATAL priority.
- */
-void __android_log_assert(const char *cond, const char *tag,
- const char *fmt, ...)
-#if defined(__GNUC__)
- __attribute__ ((noreturn))
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
- __attribute__ ((format(gnu_printf, 3, 4)))
-#else
- __attribute__ ((format(printf, 3, 4)))
-#endif
-#else
- __attribute__ ((format(printf, 3, 4)))
-#endif
-#endif
- ;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ANDROID_LOG_H */
diff --git a/include/android/log.h b/include/android/log.h
new file mode 120000
index 0000000..736c448
--- /dev/null
+++ b/include/android/log.h
@@ -0,0 +1 @@
+../../liblog/include/android/log.h
\ No newline at end of file
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index 290682a..c896ab8 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -34,6 +34,24 @@
typedef uint32_t word_t;
#endif
+enum BacktraceUnwindError : uint32_t {
+ BACKTRACE_UNWIND_NO_ERROR,
+ // Something failed while trying to perform the setup to begin the unwind.
+ BACKTRACE_UNWIND_ERROR_SETUP_FAILED,
+ // There is no map information to use with the unwind.
+ BACKTRACE_UNWIND_ERROR_MAP_MISSING,
+ // An error occurred that indicates a programming error.
+ BACKTRACE_UNWIND_ERROR_INTERNAL,
+ // The thread to unwind has disappeared before the unwind can begin.
+ BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST,
+ // The thread to unwind has not responded to a signal in a timely manner.
+ BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT,
+ // Attempt to do an unsupported operation.
+ BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION,
+ // Attempt to do an offline unwind without a context.
+ BACKTRACE_UNWIND_ERROR_NO_CONTEXT,
+};
+
struct backtrace_frame_data_t {
size_t num; // The current fame number.
uintptr_t pc; // The absolute pc.
@@ -52,6 +70,12 @@
typedef ucontext ucontext_t;
#endif
+struct backtrace_stackinfo_t {
+ uint64_t start;
+ uint64_t end;
+ const uint8_t* data;
+};
+
class Backtrace {
public:
// Create the correct Backtrace object based on what is to be unwound.
@@ -66,6 +90,14 @@
// If map is not NULL, the map is still owned by the caller.
static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
+ // Create an offline Backtrace object that can be used to do an unwind without a process
+ // that is still running. If cache_file is set to true, then elf information will be cached
+ // for this call. The cached information survives until the calling process ends. This means
+ // that subsequent calls to create offline Backtrace objects will continue to use the same
+ // cache. It also assumes that the elf files used for each offline unwind are the same.
+ static Backtrace* CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
+ const backtrace_stackinfo_t& stack, bool cache_file = false);
+
virtual ~Backtrace();
// Get the current stack trace and store in the backtrace_ structure.
@@ -113,6 +145,10 @@
BacktraceMap* GetMap() { return map_; }
+ BacktraceUnwindError GetError() { return error_; }
+
+ std::string GetErrorString(BacktraceUnwindError error);
+
protected:
Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
@@ -131,6 +167,8 @@
bool map_shared_;
std::vector<backtrace_frame_data_t> frames_;
+
+ BacktraceUnwindError error_;
};
#endif // _BACKTRACE_BACKTRACE_H
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index bb18aa2..df48dfe 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -19,7 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
-#ifdef USE_MINGW
+#ifdef _WIN32
// MINGW does not define these constants.
#define PROT_NONE 0
#define PROT_READ 0x1
@@ -31,6 +31,7 @@
#include <deque>
#include <string>
+#include <vector>
struct backtrace_map_t {
uintptr_t start = 0;
@@ -48,6 +49,8 @@
// is unsupported.
static BacktraceMap* Create(pid_t pid, bool uncached = false);
+ static BacktraceMap* Create(pid_t pid, const std::vector<backtrace_map_t>& maps);
+
virtual ~BacktraceMap();
// Fill in the map data structure for the given address.
@@ -68,6 +71,12 @@
bool IsWritable(uintptr_t pc) { return GetFlags(pc) & PROT_WRITE; }
bool IsExecutable(uintptr_t pc) { return GetFlags(pc) & PROT_EXEC; }
+ // In order to use the iterators on this object, a caller must
+ // call the LockIterator and UnlockIterator function to guarantee
+ // that the data does not change while it's being used.
+ virtual void LockIterator() {}
+ virtual void UnlockIterator() {}
+
typedef std::deque<backtrace_map_t>::iterator iterator;
iterator begin() { return maps_.begin(); }
iterator end() { return maps_.end(); }
@@ -99,4 +108,18 @@
pid_t pid_;
};
+class ScopedBacktraceMapIteratorLock {
+public:
+ explicit ScopedBacktraceMapIteratorLock(BacktraceMap* map) : map_(map) {
+ map->LockIterator();
+ }
+
+ ~ScopedBacktraceMapIteratorLock() {
+ map_->UnlockIterator();
+ }
+
+private:
+ BacktraceMap* map_;
+};
+
#endif // _BACKTRACE_BACKTRACE_MAP_H
diff --git a/include/cutils b/include/cutils
new file mode 120000
index 0000000..ac2ed40
--- /dev/null
+++ b/include/cutils
@@ -0,0 +1 @@
+../libcutils/include/cutils/
\ No newline at end of file
diff --git a/include/cutils/aref.h b/include/cutils/aref.h
deleted file mode 100644
index 3bd36ea..0000000
--- a/include/cutils/aref.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _CUTILS_AREF_H_
-#define _CUTILS_AREF_H_
-
-#include <stddef.h>
-#include <sys/cdefs.h>
-
-#include <cutils/atomic.h>
-
-__BEGIN_DECLS
-
-#define AREF_TO_ITEM(aref, container, member) \
- (container *) (((char*) (aref)) - offsetof(container, member))
-
-struct aref
-{
- volatile int32_t count;
-};
-
-static inline void aref_init(struct aref *r)
-{
- r->count = 1;
-}
-
-static inline int32_t aref_count(struct aref *r)
-{
- return r->count;
-}
-
-static inline void aref_get(struct aref *r)
-{
- android_atomic_inc(&r->count);
-}
-
-static inline void aref_put(struct aref *r, void (*release)(struct aref *))
-{
- if (android_atomic_dec(&r->count) == 1)
- release(r);
-}
-
-__END_DECLS
-
-#endif // _CUTILS_AREF_H_
diff --git a/include/cutils/ashmem.h b/include/cutils/ashmem.h
deleted file mode 100644
index 25b233e..0000000
--- a/include/cutils/ashmem.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* cutils/ashmem.h
- **
- ** Copyright 2008 The Android Open Source Project
- **
- ** This file is dual licensed. It may be redistributed and/or modified
- ** under the terms of the Apache 2.0 License OR version 2 of the GNU
- ** General Public License.
- */
-
-#ifndef _CUTILS_ASHMEM_H
-#define _CUTILS_ASHMEM_H
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int ashmem_create_region(const char *name, size_t size);
-int ashmem_set_prot_region(int fd, int prot);
-int ashmem_pin_region(int fd, size_t offset, size_t len);
-int ashmem_unpin_region(int fd, size_t offset, size_t len);
-int ashmem_get_size_region(int fd);
-
-#ifdef __cplusplus
-}
-#endif
-
-#ifndef __ASHMEMIOC /* in case someone included <linux/ashmem.h> too */
-
-#define ASHMEM_NAME_LEN 256
-
-#define ASHMEM_NAME_DEF "dev/ashmem"
-
-/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
-#define ASHMEM_NOT_PURGED 0
-#define ASHMEM_WAS_PURGED 1
-
-/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
-#define ASHMEM_IS_UNPINNED 0
-#define ASHMEM_IS_PINNED 1
-
-#endif /* ! __ASHMEMIOC */
-
-#endif /* _CUTILS_ASHMEM_H */
diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h
deleted file mode 100644
index ded972a..0000000
--- a/include/cutils/atomic.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_CUTILS_ATOMIC_H
-#define ANDROID_CUTILS_ATOMIC_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <stdatomic.h>
-
-#ifndef ANDROID_ATOMIC_INLINE
-#define ANDROID_ATOMIC_INLINE static inline
-#endif
-
-/*
- * A handful of basic atomic operations.
- * THESE ARE HERE FOR LEGACY REASONS ONLY. AVOID.
- *
- * PREFERRED ALTERNATIVES:
- * - Use C++/C/pthread locks/mutexes whenever there is not a
- * convincing reason to do otherwise. Note that very clever and
- * complicated, but correct, lock-free code is often slower than
- * using locks, especially where nontrivial data structures
- * are involved.
- * - C11 stdatomic.h.
- * - Where supported, C++11 std::atomic<T> .
- *
- * PLEASE STOP READING HERE UNLESS YOU ARE TRYING TO UNDERSTAND
- * OR UPDATE OLD CODE.
- *
- * The "acquire" and "release" terms can be defined intuitively in terms
- * of the placement of memory barriers in a simple lock implementation:
- * - wait until compare-and-swap(lock-is-free --> lock-is-held) succeeds
- * - barrier
- * - [do work]
- * - barrier
- * - store(lock-is-free)
- * In very crude terms, the initial (acquire) barrier prevents any of the
- * "work" from happening before the lock is held, and the later (release)
- * barrier ensures that all of the work happens before the lock is released.
- * (Think of cached writes, cache read-ahead, and instruction reordering
- * around the CAS and store instructions.)
- *
- * The barriers must apply to both the compiler and the CPU. Note it is
- * legal for instructions that occur before an "acquire" barrier to be
- * moved down below it, and for instructions that occur after a "release"
- * barrier to be moved up above it.
- *
- * The ARM-driven implementation we use here is short on subtlety,
- * and actually requests a full barrier from the compiler and the CPU.
- * The only difference between acquire and release is in whether they
- * are issued before or after the atomic operation with which they
- * are associated. To ease the transition to C/C++ atomic intrinsics,
- * you should not rely on this, and instead assume that only the minimal
- * acquire/release protection is provided.
- *
- * NOTE: all int32_t* values are expected to be aligned on 32-bit boundaries.
- * If they are not, atomicity is not guaranteed.
- */
-
-/*
- * Basic arithmetic and bitwise operations. These all provide a
- * barrier with "release" ordering, and return the previous value.
- *
- * These have the same characteristics (e.g. what happens on overflow)
- * as the equivalent non-atomic C operations.
- */
-ANDROID_ATOMIC_INLINE
-int32_t android_atomic_inc(volatile int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- /* Int32_t, if it exists, is the same as int_least32_t. */
- return atomic_fetch_add_explicit(a, 1, memory_order_release);
-}
-
-ANDROID_ATOMIC_INLINE
-int32_t android_atomic_dec(volatile int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- return atomic_fetch_sub_explicit(a, 1, memory_order_release);
-}
-
-ANDROID_ATOMIC_INLINE
-int32_t android_atomic_add(int32_t value, volatile int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- return atomic_fetch_add_explicit(a, value, memory_order_release);
-}
-
-ANDROID_ATOMIC_INLINE
-int32_t android_atomic_and(int32_t value, volatile int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- return atomic_fetch_and_explicit(a, value, memory_order_release);
-}
-
-ANDROID_ATOMIC_INLINE
-int32_t android_atomic_or(int32_t value, volatile int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- return atomic_fetch_or_explicit(a, value, memory_order_release);
-}
-
-/*
- * Perform an atomic load with "acquire" or "release" ordering.
- *
- * Note that the notion of a "release" ordering for a load does not
- * really fit into the C11 or C++11 memory model. The extra ordering
- * is normally observable only by code using memory_order_relaxed
- * atomics, or data races. In the rare cases in which such ordering
- * is called for, use memory_order_relaxed atomics and a leading
- * atomic_thread_fence (typically with memory_order_acquire,
- * not memory_order_release!) instead. If you do not understand
- * this comment, you are in the vast majority, and should not be
- * using release loads or replacing them with anything other than
- * locks or default sequentially consistent atomics.
- */
-ANDROID_ATOMIC_INLINE
-int32_t android_atomic_acquire_load(volatile const int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- return atomic_load_explicit(a, memory_order_acquire);
-}
-
-ANDROID_ATOMIC_INLINE
-int32_t android_atomic_release_load(volatile const int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- atomic_thread_fence(memory_order_seq_cst);
- /* Any reasonable clients of this interface would probably prefer */
- /* something weaker. But some remaining clients seem to be */
- /* abusing this API in strange ways, e.g. by using it as a fence. */
- /* Thus we are conservative until we can get rid of remaining */
- /* clients (and this function). */
- return atomic_load_explicit(a, memory_order_relaxed);
-}
-
-/*
- * Perform an atomic store with "acquire" or "release" ordering.
- *
- * Note that the notion of an "acquire" ordering for a store does not
- * really fit into the C11 or C++11 memory model. The extra ordering
- * is normally observable only by code using memory_order_relaxed
- * atomics, or data races. In the rare cases in which such ordering
- * is called for, use memory_order_relaxed atomics and a trailing
- * atomic_thread_fence (typically with memory_order_release,
- * not memory_order_acquire!) instead.
- */
-ANDROID_ATOMIC_INLINE
-void android_atomic_acquire_store(int32_t value, volatile int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- atomic_store_explicit(a, value, memory_order_relaxed);
- atomic_thread_fence(memory_order_seq_cst);
- /* Again overly conservative to accomodate weird clients. */
-}
-
-ANDROID_ATOMIC_INLINE
-void android_atomic_release_store(int32_t value, volatile int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- atomic_store_explicit(a, value, memory_order_release);
-}
-
-/*
- * Compare-and-set operation with "acquire" or "release" ordering.
- *
- * This returns zero if the new value was successfully stored, which will
- * only happen when *addr == oldvalue.
- *
- * (The return value is inverted from implementations on other platforms,
- * but matches the ARM ldrex/strex result.)
- *
- * Implementations that use the release CAS in a loop may be less efficient
- * than possible, because we re-issue the memory barrier on each iteration.
- */
-ANDROID_ATOMIC_INLINE
-int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue,
- volatile int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- return (int)(!atomic_compare_exchange_strong_explicit(
- a, &oldvalue, newvalue,
- memory_order_acquire,
- memory_order_acquire));
-}
-
-ANDROID_ATOMIC_INLINE
-int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue,
- volatile int32_t* addr)
-{
- volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
- return (int)(!atomic_compare_exchange_strong_explicit(
- a, &oldvalue, newvalue,
- memory_order_release,
- memory_order_relaxed));
-}
-
-/*
- * Fence primitives.
- */
-ANDROID_ATOMIC_INLINE
-void android_compiler_barrier(void)
-{
- __asm__ __volatile__ ("" : : : "memory");
- /* Could probably also be: */
- /* atomic_signal_fence(memory_order_seq_cst); */
-}
-
-ANDROID_ATOMIC_INLINE
-void android_memory_barrier(void)
-{
- atomic_thread_fence(memory_order_seq_cst);
-}
-
-/*
- * Aliases for code using an older version of this header. These are now
- * deprecated and should not be used. The definitions will be removed
- * in a future release.
- */
-#define android_atomic_write android_atomic_release_store
-#define android_atomic_cmpxchg android_atomic_release_cas
-
-#endif // ANDROID_CUTILS_ATOMIC_H
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
deleted file mode 100644
index 285e1af..0000000
--- a/include/cutils/debugger.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 __CUTILS_DEBUGGER_H
-#define __CUTILS_DEBUGGER_H
-
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-__BEGIN_DECLS
-
-#define DEBUGGER_SOCKET_NAME "android:debuggerd"
-#define DEBUGGER32_SOCKET_NAME "android:debuggerd32"
-#define DEBUGGER64_SOCKET_NAME DEBUGGER_SOCKET_NAME
-
-typedef enum {
- // dump a crash
- DEBUGGER_ACTION_CRASH,
- // dump a tombstone file
- DEBUGGER_ACTION_DUMP_TOMBSTONE,
- // dump a backtrace only back to the socket
- DEBUGGER_ACTION_DUMP_BACKTRACE,
-} debugger_action_t;
-
-// Make sure that all values have a fixed size so that this structure
-// is the same for 32 bit and 64 bit processes.
-// NOTE: Any changes to this structure must also be reflected in
-// bionic/linker/debugger.cpp.
-typedef struct __attribute__((packed)) {
- int32_t action;
- pid_t tid;
- uint64_t abort_msg_address;
- int32_t original_si_code;
-} debugger_msg_t;
-
-/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
- * Stores the tombstone path in the provided buffer.
- * Returns 0 on success, -1 on error.
- */
-int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen);
-
-/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
- * Stores the tombstone path in the provided buffer.
- * If reading debugger data from debuggerd ever takes longer than timeout_secs
- * seconds, then stop and return an error.
- * Returns 0 on success, -1 on error.
- */
-int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs);
-
-/* Dumps a process backtrace only to the specified file (requires root).
- * Returns 0 on success, -1 on error.
- */
-int dump_backtrace_to_file(pid_t tid, int fd);
-
-/* Dumps a process backtrace only to the specified file (requires root).
- * If reading debugger data from debuggerd ever takes longer than timeout_secs
- * seconds, then stop and return an error.
- * Returns 0 on success, -1 on error.
- */
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
-
-__END_DECLS
-
-#endif /* __CUTILS_DEBUGGER_H */
diff --git a/include/cutils/fs.h b/include/cutils/fs.h
deleted file mode 100644
index 70f0b92..0000000
--- a/include/cutils/fs.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 __CUTILS_FS_H
-#define __CUTILS_FS_H
-
-#include <sys/types.h>
-#include <unistd.h>
-
-/*
- * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
- * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
- * not already defined, then define it here.
- */
-#ifndef TEMP_FAILURE_RETRY
-/* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp) ({ \
- typeof (exp) _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; })
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Ensure that directory exists with given mode and owners.
- */
-extern int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
-
-/*
- * Read single plaintext integer from given file, correctly handling files
- * partially written with fs_write_atomic_int().
- */
-extern int fs_read_atomic_int(const char* path, int* value);
-
-/*
- * Write single plaintext integer to given file, creating backup while
- * in progress.
- */
-extern int fs_write_atomic_int(const char* path, int value);
-
-/*
- * Ensure that all directories along given path exist, creating parent
- * directories as needed. Validates that given path is absolute and that
- * it contains no relative "." or ".." paths or symlinks. Last path segment
- * is treated as filename and ignored, unless the path ends with "/".
- */
-extern int fs_mkdirs(const char* path, mode_t mode);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CUTILS_FS_H */
diff --git a/include/cutils/klog.h b/include/cutils/klog.h
deleted file mode 100644
index 295d62b..0000000
--- a/include/cutils/klog.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _CUTILS_KLOG_H_
-#define _CUTILS_KLOG_H_
-
-#include <sys/cdefs.h>
-#include <sys/uio.h>
-#include <stdarg.h>
-
-__BEGIN_DECLS
-
-void klog_init(void);
-int klog_get_level(void);
-void klog_set_level(int level);
-/* TODO: void klog_close(void); - and make klog_fd users thread safe. */
-
-void klog_write(int level, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-void klog_writev(int level, const struct iovec* iov, int iov_count);
-
-__END_DECLS
-
-#define KLOG_ERROR_LEVEL 3
-#define KLOG_WARNING_LEVEL 4
-#define KLOG_NOTICE_LEVEL 5
-#define KLOG_INFO_LEVEL 6
-#define KLOG_DEBUG_LEVEL 7
-
-#define KLOG_ERROR(tag,x...) klog_write(KLOG_ERROR_LEVEL, "<3>" tag ": " x)
-#define KLOG_WARNING(tag,x...) klog_write(KLOG_WARNING_LEVEL, "<4>" tag ": " x)
-#define KLOG_NOTICE(tag,x...) klog_write(KLOG_NOTICE_LEVEL, "<5>" tag ": " x)
-#define KLOG_INFO(tag,x...) klog_write(KLOG_INFO_LEVEL, "<6>" tag ": " x)
-#define KLOG_DEBUG(tag,x...) klog_write(KLOG_DEBUG_LEVEL, "<7>" tag ": " x)
-
-#define KLOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */
-
-#endif
diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h
deleted file mode 100644
index 635ddb1..0000000
--- a/include/cutils/multiuser.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 __CUTILS_MULTIUSER_H
-#define __CUTILS_MULTIUSER_H
-
-#include <sys/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// NOTE: keep in sync with android.os.UserId
-
-#define MULTIUSER_APP_PER_USER_RANGE 100000
-
-typedef uid_t userid_t;
-typedef uid_t appid_t;
-
-extern userid_t multiuser_get_user_id(uid_t uid);
-extern appid_t multiuser_get_app_id(uid_t uid);
-extern uid_t multiuser_get_uid(userid_t userId, appid_t appId);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CUTILS_MULTIUSER_H */
diff --git a/include/cutils/native_handle.h b/include/cutils/native_handle.h
deleted file mode 100644
index 268c5d3..0000000
--- a/include/cutils/native_handle.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 NATIVE_HANDLE_H_
-#define NATIVE_HANDLE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct native_handle
-{
- int version; /* sizeof(native_handle_t) */
- int numFds; /* number of file-descriptors at &data[0] */
- int numInts; /* number of ints at &data[numFds] */
- int data[0]; /* numFds + numInts ints */
-} native_handle_t;
-
-/*
- * native_handle_close
- *
- * closes the file descriptors contained in this native_handle_t
- *
- * return 0 on success, or a negative error code on failure
- *
- */
-int native_handle_close(const native_handle_t* h);
-
-
-/*
- * native_handle_create
- *
- * creates a native_handle_t and initializes it. must be destroyed with
- * native_handle_delete().
- *
- */
-native_handle_t* native_handle_create(int numFds, int numInts);
-
-/*
- * native_handle_delete
- *
- * frees a native_handle_t allocated with native_handle_create().
- * This ONLY frees the memory allocated for the native_handle_t, but doesn't
- * close the file descriptors; which can be achieved with native_handle_close().
- *
- * return 0 on success, or a negative error code on failure
- *
- */
-int native_handle_delete(native_handle_t* h);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* NATIVE_HANDLE_H_ */
diff --git a/include/cutils/process_name.h b/include/cutils/process_name.h
deleted file mode 100644
index 1e72e5c..0000000
--- a/include/cutils/process_name.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Gives the current process a name.
- */
-
-#ifndef __PROCESS_NAME_H
-#define __PROCESS_NAME_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Sets the current process name.
- *
- * Warning: This leaks a string every time you call it. Use judiciously!
- */
-void set_process_name(const char* process_name);
-
-/** Gets the current process name. */
-const char* get_process_name(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __PROCESS_NAME_H */
diff --git a/include/cutils/properties.h b/include/cutils/properties.h
deleted file mode 100644
index 24aa224..0000000
--- a/include/cutils/properties.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CUTILS_PROPERTIES_H
-#define __CUTILS_PROPERTIES_H
-
-#include <sys/cdefs.h>
-#include <stddef.h>
-#include <sys/system_properties.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* System properties are *small* name value pairs managed by the
-** property service. If your data doesn't fit in the provided
-** space it is not appropriate for a system property.
-**
-** WARNING: system/bionic/include/sys/system_properties.h also defines
-** these, but with different names. (TODO: fix that)
-*/
-#define PROPERTY_KEY_MAX PROP_NAME_MAX
-#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
-
-/* property_get: returns the length of the value which will never be
-** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
-** (the length does not include the terminating zero).
-**
-** If the property read fails or returns an empty value, the default
-** value is used (if nonnull).
-*/
-int property_get(const char *key, char *value, const char *default_value);
-
-/* property_get_bool: returns the value of key coerced into a
-** boolean. If the property is not set, then the default value is returned.
-**
-* The following is considered to be true (1):
-** "1", "true", "y", "yes", "on"
-**
-** The following is considered to be false (0):
-** "0", "false", "n", "no", "off"
-**
-** The conversion is whitespace-sensitive (e.g. " off" will not be false).
-**
-** If no property with this key is set (or the key is NULL) or the boolean
-** conversion fails, the default value is returned.
-**/
-int8_t property_get_bool(const char *key, int8_t default_value);
-
-/* property_get_int64: returns the value of key truncated and coerced into a
-** int64_t. If the property is not set, then the default value is used.
-**
-** The numeric conversion is identical to strtoimax with the base inferred:
-** - All digits up to the first non-digit characters are read
-** - The longest consecutive prefix of digits is converted to a long
-**
-** Valid strings of digits are:
-** - An optional sign character + or -
-** - An optional prefix indicating the base (otherwise base 10 is assumed)
-** -- 0 prefix is octal
-** -- 0x / 0X prefix is hex
-**
-** Leading/trailing whitespace is ignored. Overflow/underflow will cause
-** numeric conversion to fail.
-**
-** If no property with this key is set (or the key is NULL) or the numeric
-** conversion fails, the default value is returned.
-**/
-int64_t property_get_int64(const char *key, int64_t default_value);
-
-/* property_get_int32: returns the value of key truncated and coerced into an
-** int32_t. If the property is not set, then the default value is used.
-**
-** The numeric conversion is identical to strtoimax with the base inferred:
-** - All digits up to the first non-digit characters are read
-** - The longest consecutive prefix of digits is converted to a long
-**
-** Valid strings of digits are:
-** - An optional sign character + or -
-** - An optional prefix indicating the base (otherwise base 10 is assumed)
-** -- 0 prefix is octal
-** -- 0x / 0X prefix is hex
-**
-** Leading/trailing whitespace is ignored. Overflow/underflow will cause
-** numeric conversion to fail.
-**
-** If no property with this key is set (or the key is NULL) or the numeric
-** conversion fails, the default value is returned.
-**/
-int32_t property_get_int32(const char *key, int32_t default_value);
-
-/* property_set: returns 0 on success, < 0 on failure
-*/
-int property_set(const char *key, const char *value);
-
-int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
-
-#if defined(__BIONIC_FORTIFY)
-
-extern int __property_get_real(const char *, char *, const char *)
- __asm__(__USER_LABEL_PREFIX__ "property_get");
-__errordecl(__property_get_too_small_error, "property_get() called with too small of a buffer");
-
-__BIONIC_FORTIFY_INLINE
-int property_get(const char *key, char *value, const char *default_value) {
- size_t bos = __bos(value);
- if (bos < PROPERTY_VALUE_MAX) {
- __property_get_too_small_error();
- }
- return __property_get_real(key, value, default_value);
-}
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/cutils/sched_policy.h b/include/cutils/sched_policy.h
deleted file mode 100644
index ba84ce3..0000000
--- a/include/cutils/sched_policy.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CUTILS_SCHED_POLICY_H
-#define __CUTILS_SCHED_POLICY_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
-typedef enum {
- SP_DEFAULT = -1,
- SP_BACKGROUND = 0,
- SP_FOREGROUND = 1,
- SP_SYSTEM = 2, // can't be used with set_sched_policy()
- SP_AUDIO_APP = 3,
- SP_AUDIO_SYS = 4,
- SP_CNT,
- SP_MAX = SP_CNT - 1,
- SP_SYSTEM_DEFAULT = SP_FOREGROUND,
-} SchedPolicy;
-
-/* Assign thread tid to the cgroup associated with the specified policy.
- * If the thread is a thread group leader, that is it's gettid() == getpid(),
- * then the other threads in the same thread group are _not_ affected.
- * On platforms which support gettid(), zero tid means current thread.
- * Return value: 0 for success, or -errno for error.
- */
-extern int set_sched_policy(int tid, SchedPolicy policy);
-
-/* Return the policy associated with the cgroup of thread tid via policy pointer.
- * On platforms which support gettid(), zero tid means current thread.
- * Return value: 0 for success, or -1 for error and set errno.
- */
-extern int get_sched_policy(int tid, SchedPolicy *policy);
-
-/* Return a displayable string corresponding to policy.
- * Return value: non-NULL NUL-terminated name of unspecified length;
- * the caller is responsible for displaying the useful part of the string.
- */
-extern const char *get_sched_policy_name(SchedPolicy policy);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CUTILS_SCHED_POLICY_H */
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
deleted file mode 100644
index 2d3c743..0000000
--- a/include/cutils/sockets.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CUTILS_SOCKETS_H
-#define __CUTILS_SOCKETS_H
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-
-#if defined(_WIN32)
-#include <winsock2.h>
-typedef int socklen_t;
-#else
-#include <sys/socket.h>
-#endif
-
-#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
-#define ANDROID_SOCKET_DIR "/dev/socket"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * android_get_control_socket - simple helper function to get the file
- * descriptor of our init-managed Unix domain socket. `name' is the name of the
- * socket, as given in init.rc. Returns -1 on error.
- *
- * This is inline and not in libcutils proper because we want to use this in
- * third-party daemons with minimal modification.
- */
-static inline int android_get_control_socket(const char *name)
-{
- char key[64];
- snprintf(key, sizeof(key), ANDROID_SOCKET_ENV_PREFIX "%s", name);
-
- const char* val = getenv(key);
- if (!val) {
- return -1;
- }
-
- errno = 0;
- int fd = strtol(val, NULL, 10);
- if (errno) {
- return -1;
- }
-
- return fd;
-}
-
-/*
- * See also android.os.LocalSocketAddress.Namespace
- */
-// Linux "abstract" (non-filesystem) namespace
-#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
-// Android "reserved" (/dev/socket) namespace
-#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
-// Normal filesystem namespace
-#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
-
-extern int socket_loopback_client(int port, int type);
-extern int socket_network_client(const char *host, int port, int type);
-extern int socket_network_client_timeout(const char *host, int port, int type,
- int timeout, int* getaddrinfo_error);
-extern int socket_loopback_server(int port, int type);
-extern int socket_local_server(const char *name, int namespaceId, int type);
-extern int socket_local_server_bind(int s, const char *name, int namespaceId);
-extern int socket_local_client_connect(int fd,
- const char *name, int namespaceId, int type);
-extern int socket_local_client(const char *name, int namespaceId, int type);
-extern int socket_inaddr_any_server(int port, int type);
-
-/*
- * socket_peer_is_trusted - Takes a socket which is presumed to be a
- * connected local socket (e.g. AF_LOCAL) and returns whether the peer
- * (the userid that owns the process on the other end of that socket)
- * is one of the two trusted userids, root or shell.
- *
- * Note: This only works as advertised on the Android OS and always
- * just returns true when called on other operating systems.
- */
-extern bool socket_peer_is_trusted(int fd);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CUTILS_SOCKETS_H */
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
deleted file mode 100644
index 6d9b3bc..0000000
--- a/include/cutils/trace.h
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_CUTILS_TRACE_H
-#define _LIBS_CUTILS_TRACE_H
-
-#include <inttypes.h>
-#include <stdatomic.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/compiler.h>
-
-__BEGIN_DECLS
-
-/**
- * The ATRACE_TAG macro can be defined before including this header to trace
- * using one of the tags defined below. It must be defined to one of the
- * following ATRACE_TAG_* macros. The trace tag is used to filter tracing in
- * userland to avoid some of the runtime cost of tracing when it is not desired.
- *
- * Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always
- * being enabled - this should ONLY be done for debug code, as userland tracing
- * has a performance cost even when the trace is not being recorded. Defining
- * ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result
- * in the tracing always being disabled.
- *
- * ATRACE_TAG_HAL should be bitwise ORed with the relevant tags for tracing
- * within a hardware module. For example a camera hardware module would set:
- * #define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
- *
- * Keep these in sync with frameworks/base/core/java/android/os/Trace.java.
- */
-#define ATRACE_TAG_NEVER 0 // This tag is never enabled.
-#define ATRACE_TAG_ALWAYS (1<<0) // This tag is always enabled.
-#define ATRACE_TAG_GRAPHICS (1<<1)
-#define ATRACE_TAG_INPUT (1<<2)
-#define ATRACE_TAG_VIEW (1<<3)
-#define ATRACE_TAG_WEBVIEW (1<<4)
-#define ATRACE_TAG_WINDOW_MANAGER (1<<5)
-#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6)
-#define ATRACE_TAG_SYNC_MANAGER (1<<7)
-#define ATRACE_TAG_AUDIO (1<<8)
-#define ATRACE_TAG_VIDEO (1<<9)
-#define ATRACE_TAG_CAMERA (1<<10)
-#define ATRACE_TAG_HAL (1<<11)
-#define ATRACE_TAG_APP (1<<12)
-#define ATRACE_TAG_RESOURCES (1<<13)
-#define ATRACE_TAG_DALVIK (1<<14)
-#define ATRACE_TAG_RS (1<<15)
-#define ATRACE_TAG_BIONIC (1<<16)
-#define ATRACE_TAG_POWER (1<<17)
-#define ATRACE_TAG_PACKAGE_MANAGER (1<<18)
-#define ATRACE_TAG_LAST ATRACE_TAG_PACKAGE_MANAGER
-
-// Reserved for initialization.
-#define ATRACE_TAG_NOT_READY (1LL<<63)
-
-#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
-
-#ifndef ATRACE_TAG
-#define ATRACE_TAG ATRACE_TAG_NEVER
-#elif ATRACE_TAG > ATRACE_TAG_VALID_MASK
-#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
-#endif
-
-/**
- * Opens the trace file for writing and reads the property for initial tags.
- * The atrace.tags.enableflags property sets the tags to trace.
- * This function should not be explicitly called, the first call to any normal
- * trace function will cause it to be run safely.
- */
-void atrace_setup();
-
-/**
- * If tracing is ready, set atrace_enabled_tags to the system property
- * debug.atrace.tags.enableflags. Can be used as a sysprop change callback.
- */
-void atrace_update_tags();
-
-/**
- * Set whether the process is debuggable. By default the process is not
- * considered debuggable. If the process is not debuggable then application-
- * level tracing is not allowed unless the ro.debuggable system property is
- * set to '1'.
- */
-void atrace_set_debuggable(bool debuggable);
-
-/**
- * Set whether tracing is enabled for the current process. This is used to
- * prevent tracing within the Zygote process.
- */
-void atrace_set_tracing_enabled(bool enabled);
-
-/**
- * Flag indicating whether setup has been completed, initialized to 0.
- * Nonzero indicates setup has completed.
- * Note: This does NOT indicate whether or not setup was successful.
- */
-extern atomic_bool atrace_is_ready;
-
-/**
- * Set of ATRACE_TAG flags to trace for, initialized to ATRACE_TAG_NOT_READY.
- * A value of zero indicates setup has failed.
- * Any other nonzero value indicates setup has succeeded, and tracing is on.
- */
-extern uint64_t atrace_enabled_tags;
-
-/**
- * Handle to the kernel's trace buffer, initialized to -1.
- * Any other value indicates setup has succeeded, and is a valid fd for tracing.
- */
-extern int atrace_marker_fd;
-
-/**
- * atrace_init readies the process for tracing by opening the trace_marker file.
- * Calling any trace function causes this to be run, so calling it is optional.
- * This can be explicitly run to avoid setup delay on first trace function.
- */
-#define ATRACE_INIT() atrace_init()
-static inline void atrace_init()
-{
- if (CC_UNLIKELY(!atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
- atrace_setup();
- }
-}
-
-/**
- * Get the mask of all tags currently enabled.
- * It can be used as a guard condition around more expensive trace calculations.
- * Every trace function calls this, which ensures atrace_init is run.
- */
-#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
-static inline uint64_t atrace_get_enabled_tags()
-{
- atrace_init();
- return atrace_enabled_tags;
-}
-
-/**
- * Test if a given tag is currently enabled.
- * Returns nonzero if the tag is enabled, otherwise zero.
- * It can be used as a guard condition around more expensive trace calculations.
- */
-#define ATRACE_ENABLED() atrace_is_tag_enabled(ATRACE_TAG)
-static inline uint64_t atrace_is_tag_enabled(uint64_t tag)
-{
- return atrace_get_enabled_tags() & tag;
-}
-
-/**
- * Trace the beginning of a context. name is used to identify the context.
- * This is often used to time function execution.
- */
-#define ATRACE_BEGIN(name) atrace_begin(ATRACE_TAG, name)
-static inline void atrace_begin(uint64_t tag, const char* name)
-{
- if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_begin_body(const char*);
- atrace_begin_body(name);
- }
-}
-
-/**
- * Trace the end of a context.
- * This should match up (and occur after) a corresponding ATRACE_BEGIN.
- */
-#define ATRACE_END() atrace_end(ATRACE_TAG)
-static inline void atrace_end(uint64_t tag)
-{
- if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- char c = 'E';
- write(atrace_marker_fd, &c, 1);
- }
-}
-
-/**
- * Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END
- * contexts, asynchronous events do not need to be nested. The name describes
- * the event, and the cookie provides a unique identifier for distinguishing
- * simultaneous events. The name and cookie used to begin an event must be
- * used to end it.
- */
-#define ATRACE_ASYNC_BEGIN(name, cookie) \
- atrace_async_begin(ATRACE_TAG, name, cookie)
-static inline void atrace_async_begin(uint64_t tag, const char* name,
- int32_t cookie)
-{
- if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_async_begin_body(const char*, int32_t);
- atrace_async_begin_body(name, cookie);
- }
-}
-
-/**
- * Trace the end of an asynchronous event.
- * This should have a corresponding ATRACE_ASYNC_BEGIN.
- */
-#define ATRACE_ASYNC_END(name, cookie) atrace_async_end(ATRACE_TAG, name, cookie)
-static inline void atrace_async_end(uint64_t tag, const char* name, int32_t cookie)
-{
- if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_async_end_body(const char*, int32_t);
- atrace_async_end_body(name, cookie);
- }
-}
-
-/**
- * Traces an integer counter value. name is used to identify the counter.
- * This can be used to track how a value changes over time.
- */
-#define ATRACE_INT(name, value) atrace_int(ATRACE_TAG, name, value)
-static inline void atrace_int(uint64_t tag, const char* name, int32_t value)
-{
- if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_int_body(const char*, int32_t);
- atrace_int_body(name, value);
- }
-}
-
-/**
- * Traces a 64-bit integer counter value. name is used to identify the
- * counter. This can be used to track how a value changes over time.
- */
-#define ATRACE_INT64(name, value) atrace_int64(ATRACE_TAG, name, value)
-static inline void atrace_int64(uint64_t tag, const char* name, int64_t value)
-{
- if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
- void atrace_int64_body(const char*, int64_t);
- atrace_int64_body(name, value);
- }
-}
-
-__END_DECLS
-
-#endif // _LIBS_CUTILS_TRACE_H
diff --git a/include/log b/include/log
new file mode 120000
index 0000000..714065f
--- /dev/null
+++ b/include/log
@@ -0,0 +1 @@
+../liblog/include/log
\ No newline at end of file
diff --git a/include/log/event_tag_map.h b/include/log/event_tag_map.h
deleted file mode 100644
index 1653c61..0000000
--- a/include/log/event_tag_map.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
-#define _LIBS_CUTILS_EVENTTAGMAP_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
-
-struct EventTagMap;
-typedef struct EventTagMap EventTagMap;
-
-/*
- * Open the specified file as an event log tag map.
- *
- * Returns NULL on failure.
- */
-EventTagMap* android_openEventTagMap(const char* fileName);
-
-/*
- * Close the map.
- */
-void android_closeEventTagMap(EventTagMap* map);
-
-/*
- * Look up a tag by index. Returns the tag string, or NULL if not found.
- */
-const char* android_lookupEventTag(const EventTagMap* map, int tag);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
diff --git a/include/log/log.h b/include/log/log.h
deleted file mode 100644
index 0b17574..0000000
--- a/include/log/log.h
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * Copyright (C) 2005-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// C/C++ logging functions. See the logging documentation for API details.
-//
-// We'd like these to be available from C code (in case we import some from
-// somewhere), so this has a C interface.
-//
-// The output will be correct when the log file is shared between multiple
-// threads and/or multiple processes so long as the operating system
-// supports O_APPEND. These calls have mutex-protected data structures
-// and so are NOT reentrant. Do not use LOG in a signal handler.
-//
-#ifndef _LIBS_LOG_LOG_H
-#define _LIBS_LOG_LOG_H
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <log/logd.h>
-#include <log/uio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// ---------------------------------------------------------------------
-
-/*
- * Normally we strip ALOGV (VERBOSE messages) from release builds.
- * You can modify this (for example with "#define LOG_NDEBUG 0"
- * at the top of your source file) to change that behavior.
- */
-#ifndef LOG_NDEBUG
-#ifdef NDEBUG
-#define LOG_NDEBUG 1
-#else
-#define LOG_NDEBUG 0
-#endif
-#endif
-
-/*
- * This is the local tag used for the following simplified
- * logging macros. You can change this preprocessor definition
- * before using the other macros to change the tag.
- */
-#ifndef LOG_TAG
-#define LOG_TAG NULL
-#endif
-
-// ---------------------------------------------------------------------
-
-#ifndef __predict_false
-#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
-#endif
-
-/*
- * -DLINT_RLOG in sources that you want to enforce that all logging
- * goes to the radio log buffer. If any logging goes to any of the other
- * log buffers, there will be a compile or link error to highlight the
- * problem. This is not a replacement for a full audit of the code since
- * this only catches compiled code, not ifdef'd debug code. Options to
- * defining this, either temporarily to do a spot check, or permanently
- * to enforce, in all the communications trees; We have hopes to ensure
- * that by supplying just the radio log buffer that the communications
- * teams will have their one-stop shop for triaging issues.
- */
-#ifndef LINT_RLOG
-
-/*
- * Simplified macro to send a verbose log message using the current LOG_TAG.
- */
-#ifndef ALOGV
-#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#if LOG_NDEBUG
-#define ALOGV(...) do { if (0) { __ALOGV(__VA_ARGS__); } } while (0)
-#else
-#define ALOGV(...) __ALOGV(__VA_ARGS__)
-#endif
-#endif
-
-#ifndef ALOGV_IF
-#if LOG_NDEBUG
-#define ALOGV_IF(cond, ...) ((void)0)
-#else
-#define ALOGV_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-#endif
-
-/*
- * Simplified macro to send a debug log message using the current LOG_TAG.
- */
-#ifndef ALOGD
-#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef ALOGD_IF
-#define ALOGD_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an info log message using the current LOG_TAG.
- */
-#ifndef ALOGI
-#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef ALOGI_IF
-#define ALOGI_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-/*
- * Simplified macro to send a warning log message using the current LOG_TAG.
- */
-#ifndef ALOGW
-#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef ALOGW_IF
-#define ALOGW_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an error log message using the current LOG_TAG.
- */
-#ifndef ALOGE
-#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef ALOGE_IF
-#define ALOGE_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-// ---------------------------------------------------------------------
-
-/*
- * Conditional based on whether the current LOG_TAG is enabled at
- * verbose priority.
- */
-#ifndef IF_ALOGV
-#if LOG_NDEBUG
-#define IF_ALOGV() if (false)
-#else
-#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
-#endif
-#endif
-
-/*
- * Conditional based on whether the current LOG_TAG is enabled at
- * debug priority.
- */
-#ifndef IF_ALOGD
-#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
-#endif
-
-/*
- * Conditional based on whether the current LOG_TAG is enabled at
- * info priority.
- */
-#ifndef IF_ALOGI
-#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
-#endif
-
-/*
- * Conditional based on whether the current LOG_TAG is enabled at
- * warn priority.
- */
-#ifndef IF_ALOGW
-#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
-#endif
-
-/*
- * Conditional based on whether the current LOG_TAG is enabled at
- * error priority.
- */
-#ifndef IF_ALOGE
-#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
-#endif
-
-
-// ---------------------------------------------------------------------
-
-/*
- * Simplified macro to send a verbose system log message using the current LOG_TAG.
- */
-#ifndef SLOGV
-#define __SLOGV(...) \
- ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#if LOG_NDEBUG
-#define SLOGV(...) do { if (0) { __SLOGV(__VA_ARGS__); } } while (0)
-#else
-#define SLOGV(...) __SLOGV(__VA_ARGS__)
-#endif
-#endif
-
-#ifndef SLOGV_IF
-#if LOG_NDEBUG
-#define SLOGV_IF(cond, ...) ((void)0)
-#else
-#define SLOGV_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-#endif
-
-/*
- * Simplified macro to send a debug system log message using the current LOG_TAG.
- */
-#ifndef SLOGD
-#define SLOGD(...) \
- ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef SLOGD_IF
-#define SLOGD_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an info system log message using the current LOG_TAG.
- */
-#ifndef SLOGI
-#define SLOGI(...) \
- ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef SLOGI_IF
-#define SLOGI_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-/*
- * Simplified macro to send a warning system log message using the current LOG_TAG.
- */
-#ifndef SLOGW
-#define SLOGW(...) \
- ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef SLOGW_IF
-#define SLOGW_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an error system log message using the current LOG_TAG.
- */
-#ifndef SLOGE
-#define SLOGE(...) \
- ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef SLOGE_IF
-#define SLOGE_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-#endif /* !LINT_RLOG */
-
-// ---------------------------------------------------------------------
-
-/*
- * Simplified macro to send a verbose radio log message using the current LOG_TAG.
- */
-#ifndef RLOGV
-#define __RLOGV(...) \
- ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#if LOG_NDEBUG
-#define RLOGV(...) do { if (0) { __RLOGV(__VA_ARGS__); } } while (0)
-#else
-#define RLOGV(...) __RLOGV(__VA_ARGS__)
-#endif
-#endif
-
-#ifndef RLOGV_IF
-#if LOG_NDEBUG
-#define RLOGV_IF(cond, ...) ((void)0)
-#else
-#define RLOGV_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-#endif
-
-/*
- * Simplified macro to send a debug radio log message using the current LOG_TAG.
- */
-#ifndef RLOGD
-#define RLOGD(...) \
- ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef RLOGD_IF
-#define RLOGD_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an info radio log message using the current LOG_TAG.
- */
-#ifndef RLOGI
-#define RLOGI(...) \
- ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef RLOGI_IF
-#define RLOGI_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-/*
- * Simplified macro to send a warning radio log message using the current LOG_TAG.
- */
-#ifndef RLOGW
-#define RLOGW(...) \
- ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef RLOGW_IF
-#define RLOGW_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an error radio log message using the current LOG_TAG.
- */
-#ifndef RLOGE
-#define RLOGE(...) \
- ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef RLOGE_IF
-#define RLOGE_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-
-// ---------------------------------------------------------------------
-
-/*
- * Log a fatal error. If the given condition fails, this stops program
- * execution like a normal assertion, but also generating the given message.
- * It is NOT stripped from release builds. Note that the condition test
- * is -inverted- from the normal assert() semantics.
- */
-#ifndef LOG_ALWAYS_FATAL_IF
-#define LOG_ALWAYS_FATAL_IF(cond, ...) \
- ( (__predict_false(cond)) \
- ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-#ifndef LOG_ALWAYS_FATAL
-#define LOG_ALWAYS_FATAL(...) \
- ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) )
-#endif
-
-/*
- * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
- * are stripped out of release builds.
- */
-#if LOG_NDEBUG
-
-#ifndef LOG_FATAL_IF
-#define LOG_FATAL_IF(cond, ...) ((void)0)
-#endif
-#ifndef LOG_FATAL
-#define LOG_FATAL(...) ((void)0)
-#endif
-
-#else
-
-#ifndef LOG_FATAL_IF
-#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__)
-#endif
-#ifndef LOG_FATAL
-#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
-#endif
-
-#endif
-
-/*
- * Assertion that generates a log message when the assertion fails.
- * Stripped out of release builds. Uses the current LOG_TAG.
- */
-#ifndef ALOG_ASSERT
-#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
-//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
-#endif
-
-// ---------------------------------------------------------------------
-
-/*
- * Basic log message macro.
- *
- * Example:
- * ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
- *
- * The second argument may be NULL or "" to indicate the "global" tag.
- */
-#ifndef ALOG
-#define ALOG(priority, tag, ...) \
- LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
-#endif
-
-/*
- * Log macro that allows you to specify a number for the priority.
- */
-#ifndef LOG_PRI
-#define LOG_PRI(priority, tag, ...) \
- android_printLog(priority, tag, __VA_ARGS__)
-#endif
-
-/*
- * Log macro that allows you to pass in a varargs ("args" is a va_list).
- */
-#ifndef LOG_PRI_VA
-#define LOG_PRI_VA(priority, tag, fmt, args) \
- android_vprintLog(priority, NULL, tag, fmt, args)
-#endif
-
-/*
- * Conditional given a desired logging priority and tag.
- */
-#ifndef IF_ALOG
-#define IF_ALOG(priority, tag) \
- if (android_testLog(ANDROID_##priority, tag))
-#endif
-
-// ---------------------------------------------------------------------
-
-/*
- * Event logging.
- */
-
-/*
- * Event log entry types. These must match up with the declarations in
- * java/android/android/util/EventLog.java.
- */
-typedef enum {
- EVENT_TYPE_INT = 0,
- EVENT_TYPE_LONG = 1,
- EVENT_TYPE_STRING = 2,
- EVENT_TYPE_LIST = 3,
- EVENT_TYPE_FLOAT = 4,
-} AndroidEventLogType;
-#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
-#define typeof_AndroidEventLogType unsigned char
-
-#ifndef LOG_EVENT_INT
-#define LOG_EVENT_INT(_tag, _value) { \
- int intBuf = _value; \
- (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \
- sizeof(intBuf)); \
- }
-#endif
-#ifndef LOG_EVENT_LONG
-#define LOG_EVENT_LONG(_tag, _value) { \
- long long longBuf = _value; \
- (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \
- sizeof(longBuf)); \
- }
-#endif
-#ifndef LOG_EVENT_FLOAT
-#define LOG_EVENT_FLOAT(_tag, _value) { \
- float floatBuf = _value; \
- (void) android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \
- sizeof(floatBuf)); \
- }
-#endif
-#ifndef LOG_EVENT_STRING
-#define LOG_EVENT_STRING(_tag, _value) \
- (void) __android_log_bswrite(_tag, _value);
-#endif
-/* TODO: something for LIST */
-
-/*
- * ===========================================================================
- *
- * The stuff in the rest of this file should not be used directly.
- */
-
-#define android_printLog(prio, tag, fmt...) \
- __android_log_print(prio, tag, fmt)
-
-#define android_vprintLog(prio, cond, tag, fmt...) \
- __android_log_vprint(prio, tag, fmt)
-
-/* XXX Macros to work around syntax errors in places where format string
- * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
- * (happens only in debug builds).
- */
-
-/* Returns 2nd arg. Used to substitute default value if caller's vararg list
- * is empty.
- */
-#define __android_second(dummy, second, ...) second
-
-/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
- * returns nothing.
- */
-#define __android_rest(first, ...) , ## __VA_ARGS__
-
-#define android_printAssert(cond, tag, fmt...) \
- __android_log_assert(cond, tag, \
- __android_second(0, ## fmt, NULL) __android_rest(fmt))
-
-#define android_writeLog(prio, tag, text) \
- __android_log_write(prio, tag, text)
-
-#define android_bWriteLog(tag, payload, len) \
- __android_log_bwrite(tag, payload, len)
-#define android_btWriteLog(tag, type, payload, len) \
- __android_log_btwrite(tag, type, payload, len)
-
-/*
- * IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
- * android_testLog will remain constant in its purpose as a wrapper
- * for Android logging filter policy, and can be subject to
- * change. It can be reused by the developers that override
- * IF_ALOG as a convenient means to reimplement their policy
- * over Android.
- */
-#if LOG_NDEBUG /* Production */
-#define android_testLog(prio, tag) \
- (__android_log_is_loggable(prio, tag, ANDROID_LOG_DEBUG) != 0)
-#else
-#define android_testLog(prio, tag) \
- (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
-#endif
-
-// TODO: remove these prototypes and their users
-#define android_writevLog(vec,num) do{}while(0)
-#define android_write1Log(str,len) do{}while (0)
-#define android_setMinPriority(tag, prio) do{}while(0)
-//#define android_logToCallback(func) do{}while(0)
-#define android_logToFile(tag, file) (0)
-#define android_logToFd(tag, fd) (0)
-
-typedef enum log_id {
- LOG_ID_MIN = 0,
-
-#ifndef LINT_RLOG
- LOG_ID_MAIN = 0,
-#endif
- LOG_ID_RADIO = 1,
-#ifndef LINT_RLOG
- LOG_ID_EVENTS = 2,
- LOG_ID_SYSTEM = 3,
- LOG_ID_CRASH = 4,
- LOG_ID_KERNEL = 5,
-#endif
-
- LOG_ID_MAX
-} log_id_t;
-#define sizeof_log_id_t sizeof(typeof_log_id_t)
-#define typeof_log_id_t unsigned char
-
-/*
- * Use the per-tag properties "log.tag.<tagname>" to generate a runtime
- * result of non-zero to expose a log.
- */
-int __android_log_is_loggable(int prio, const char *tag, int def);
-
-/*
- * Send a simple string to the log.
- */
-int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text);
-int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
-#if defined(__GNUC__)
- __attribute__((__format__(printf, 4, 5)))
-#endif
- ;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBS_LOG_LOG_H */
diff --git a/include/log/log_read.h b/include/log/log_read.h
deleted file mode 100644
index 1b70aff..0000000
--- a/include/log/log_read.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2013-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_LOG_LOG_READ_H
-#define _LIBS_LOG_LOG_READ_H
-
-#include <stdint.h>
-#include <time.h>
-
-/* struct log_time is a wire-format variant of struct timespec */
-#define NS_PER_SEC 1000000000ULL
-
-#ifdef __cplusplus
-
-// NB: do NOT define a copy constructor. This will result in structure
-// no longer being compatible with pass-by-value which is desired
-// efficient behavior. Also, pass-by-reference breaks C/C++ ABI.
-struct log_time {
-public:
- uint32_t tv_sec; // good to Feb 5 2106
- uint32_t tv_nsec;
-
- static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
- static const uint32_t tv_nsec_max = 999999999UL;
-
- log_time(const timespec &T)
- {
- tv_sec = T.tv_sec;
- tv_nsec = T.tv_nsec;
- }
- log_time(uint32_t sec, uint32_t nsec)
- {
- tv_sec = sec;
- tv_nsec = nsec;
- }
- static const timespec EPOCH;
- log_time()
- {
- }
- log_time(clockid_t id)
- {
- timespec T;
- clock_gettime(id, &T);
- tv_sec = T.tv_sec;
- tv_nsec = T.tv_nsec;
- }
- log_time(const char *T)
- {
- const uint8_t *c = (const uint8_t *) T;
- tv_sec = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
- tv_nsec = c[4] | (c[5] << 8) | (c[6] << 16) | (c[7] << 24);
- }
-
- // timespec
- bool operator== (const timespec &T) const
- {
- return (tv_sec == static_cast<uint32_t>(T.tv_sec))
- && (tv_nsec == static_cast<uint32_t>(T.tv_nsec));
- }
- bool operator!= (const timespec &T) const
- {
- return !(*this == T);
- }
- bool operator< (const timespec &T) const
- {
- return (tv_sec < static_cast<uint32_t>(T.tv_sec))
- || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
- && (tv_nsec < static_cast<uint32_t>(T.tv_nsec)));
- }
- bool operator>= (const timespec &T) const
- {
- return !(*this < T);
- }
- bool operator> (const timespec &T) const
- {
- return (tv_sec > static_cast<uint32_t>(T.tv_sec))
- || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
- && (tv_nsec > static_cast<uint32_t>(T.tv_nsec)));
- }
- bool operator<= (const timespec &T) const
- {
- return !(*this > T);
- }
- log_time operator-= (const timespec &T);
- log_time operator- (const timespec &T) const
- {
- log_time local(*this);
- return local -= T;
- }
- log_time operator+= (const timespec &T);
- log_time operator+ (const timespec &T) const
- {
- log_time local(*this);
- return local += T;
- }
-
- // log_time
- bool operator== (const log_time &T) const
- {
- return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
- }
- bool operator!= (const log_time &T) const
- {
- return !(*this == T);
- }
- bool operator< (const log_time &T) const
- {
- return (tv_sec < T.tv_sec)
- || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
- }
- bool operator>= (const log_time &T) const
- {
- return !(*this < T);
- }
- bool operator> (const log_time &T) const
- {
- return (tv_sec > T.tv_sec)
- || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
- }
- bool operator<= (const log_time &T) const
- {
- return !(*this > T);
- }
- log_time operator-= (const log_time &T);
- log_time operator- (const log_time &T) const
- {
- log_time local(*this);
- return local -= T;
- }
- log_time operator+= (const log_time &T);
- log_time operator+ (const log_time &T) const
- {
- log_time local(*this);
- return local += T;
- }
-
- uint64_t nsec() const
- {
- return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
- }
-
- static const char default_format[];
-
- // Add %#q for the fraction of a second to the standard library functions
- char *strptime(const char *s, const char *format = default_format);
-} __attribute__((__packed__));
-
-#else
-
-typedef struct log_time {
- uint32_t tv_sec;
- uint32_t tv_nsec;
-} __attribute__((__packed__)) log_time;
-
-#endif
-
-#endif /* define _LIBS_LOG_LOG_READ_H */
diff --git a/include/log/logd.h b/include/log/logd.h
deleted file mode 100644
index 0fe515f..0000000
--- a/include/log/logd.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_CUTILS_LOGD_H
-#define _ANDROID_CUTILS_LOGD_H
-
-/* the stable/frozen log-related definitions have been
- * moved to this header, which is exposed by the NDK
- */
-#include <android/log.h>
-
-/* the rest is only used internally by the system */
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <log/uio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int __android_log_bwrite(int32_t tag, const void *payload, size_t len);
-int __android_log_btwrite(int32_t tag, char type, const void *payload,
- size_t len);
-int __android_log_bswrite(int32_t tag, const char *payload);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LOGD_H */
diff --git a/include/log/logger.h b/include/log/logger.h
deleted file mode 100644
index f030dab..0000000
--- a/include/log/logger.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
-**
-** Copyright 2007-2014, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
-
-#ifndef _LIBS_LOG_LOGGER_H
-#define _LIBS_LOG_LOGGER_H
-
-#include <stdint.h>
-#include <log/log.h>
-#include <log/log_read.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * The userspace structure for version 1 of the logger_entry ABI.
- * This structure is returned to userspace by the kernel logger
- * driver unless an upgrade to a newer ABI version is requested.
- */
-struct logger_entry {
- uint16_t len; /* length of the payload */
- uint16_t __pad; /* no matter what, we get 2 bytes of padding */
- int32_t pid; /* generating process's pid */
- int32_t tid; /* generating process's tid */
- int32_t sec; /* seconds since Epoch */
- int32_t nsec; /* nanoseconds */
- char msg[0]; /* the entry's payload */
-} __attribute__((__packed__));
-
-/*
- * The userspace structure for version 2 of the logger_entry ABI.
- * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)
- * is called with version==2; or used with the user space log daemon.
- */
-struct logger_entry_v2 {
- uint16_t len; /* length of the payload */
- uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */
- int32_t pid; /* generating process's pid */
- int32_t tid; /* generating process's tid */
- int32_t sec; /* seconds since Epoch */
- int32_t nsec; /* nanoseconds */
- uint32_t euid; /* effective UID of logger */
- char msg[0]; /* the entry's payload */
-} __attribute__((__packed__));
-
-struct logger_entry_v3 {
- uint16_t len; /* length of the payload */
- uint16_t hdr_size; /* sizeof(struct logger_entry_v3) */
- int32_t pid; /* generating process's pid */
- int32_t tid; /* generating process's tid */
- int32_t sec; /* seconds since Epoch */
- int32_t nsec; /* nanoseconds */
- uint32_t lid; /* log id of the payload */
- char msg[0]; /* the entry's payload */
-} __attribute__((__packed__));
-
-/*
- * The maximum size of the log entry payload that can be
- * written to the logger. An attempt to write more than
- * this amount will result in a truncated log entry.
- */
-#define LOGGER_ENTRY_MAX_PAYLOAD 4076
-
-/*
- * The maximum size of a log entry which can be read from the
- * kernel logger driver. An attempt to read less than this amount
- * may result in read() returning EINVAL.
- */
-#define LOGGER_ENTRY_MAX_LEN (5*1024)
-
-#define NS_PER_SEC 1000000000ULL
-
-struct log_msg {
- union {
- unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
- struct logger_entry_v3 entry;
- struct logger_entry_v3 entry_v3;
- struct logger_entry_v2 entry_v2;
- struct logger_entry entry_v1;
- } __attribute__((aligned(4)));
-#ifdef __cplusplus
- /* Matching log_time operators */
- bool operator== (const log_msg &T) const
- {
- return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec);
- }
- bool operator!= (const log_msg &T) const
- {
- return !(*this == T);
- }
- bool operator< (const log_msg &T) const
- {
- return (entry.sec < T.entry.sec)
- || ((entry.sec == T.entry.sec)
- && (entry.nsec < T.entry.nsec));
- }
- bool operator>= (const log_msg &T) const
- {
- return !(*this < T);
- }
- bool operator> (const log_msg &T) const
- {
- return (entry.sec > T.entry.sec)
- || ((entry.sec == T.entry.sec)
- && (entry.nsec > T.entry.nsec));
- }
- bool operator<= (const log_msg &T) const
- {
- return !(*this > T);
- }
- uint64_t nsec() const
- {
- return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
- }
-
- /* packet methods */
- log_id_t id()
- {
- return (log_id_t) entry.lid;
- }
- char *msg()
- {
- return entry.hdr_size ? (char *) buf + entry.hdr_size : entry_v1.msg;
- }
- unsigned int len()
- {
- return (entry.hdr_size ? entry.hdr_size : sizeof(entry_v1)) + entry.len;
- }
-#endif
-};
-
-struct logger;
-
-log_id_t android_logger_get_id(struct logger *logger);
-
-int android_logger_clear(struct logger *logger);
-long android_logger_get_log_size(struct logger *logger);
-int android_logger_set_log_size(struct logger *logger, unsigned long size);
-long android_logger_get_log_readable_size(struct logger *logger);
-int android_logger_get_log_version(struct logger *logger);
-
-struct logger_list;
-
-ssize_t android_logger_get_statistics(struct logger_list *logger_list,
- char *buf, size_t len);
-ssize_t android_logger_get_prune_list(struct logger_list *logger_list,
- char *buf, size_t len);
-int android_logger_set_prune_list(struct logger_list *logger_list,
- char *buf, size_t len);
-
-#define ANDROID_LOG_RDONLY O_RDONLY
-#define ANDROID_LOG_WRONLY O_WRONLY
-#define ANDROID_LOG_RDWR O_RDWR
-#define ANDROID_LOG_ACCMODE O_ACCMODE
-#define ANDROID_LOG_NONBLOCK O_NONBLOCK
-#define ANDROID_LOG_PSTORE 0x80000000
-
-struct logger_list *android_logger_list_alloc(int mode,
- unsigned int tail,
- pid_t pid);
-struct logger_list *android_logger_list_alloc_time(int mode,
- log_time start,
- pid_t pid);
-void android_logger_list_free(struct logger_list *logger_list);
-/* In the purest sense, the following two are orthogonal interfaces */
-int android_logger_list_read(struct logger_list *logger_list,
- struct log_msg *log_msg);
-
-/* Multiple log_id_t opens */
-struct logger *android_logger_open(struct logger_list *logger_list,
- log_id_t id);
-#define android_logger_close android_logger_free
-/* Single log_id_t open */
-struct logger_list *android_logger_list_open(log_id_t id,
- int mode,
- unsigned int tail,
- pid_t pid);
-#define android_logger_list_close android_logger_list_free
-
-/*
- * log_id_t helpers
- */
-log_id_t android_name_to_log_id(const char *logName);
-const char *android_log_id_to_name(log_id_t log_id);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBS_LOG_LOGGER_H */
diff --git a/include/log/logprint.h b/include/log/logprint.h
deleted file mode 100644
index 4b812cc..0000000
--- a/include/log/logprint.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LOGPRINT_H
-#define _LOGPRINT_H
-
-#include <log/log.h>
-#include <log/logger.h>
-#include <log/event_tag_map.h>
-#include <pthread.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- FORMAT_OFF = 0,
- FORMAT_BRIEF,
- FORMAT_PROCESS,
- FORMAT_TAG,
- FORMAT_THREAD,
- FORMAT_RAW,
- FORMAT_TIME,
- FORMAT_THREADTIME,
- FORMAT_LONG,
- /* The following three are modifiers to above formats */
- FORMAT_MODIFIER_COLOR, /* converts priority to color */
- FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
- FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
-} AndroidLogPrintFormat;
-
-typedef struct AndroidLogFormat_t AndroidLogFormat;
-
-typedef struct AndroidLogEntry_t {
- time_t tv_sec;
- long tv_nsec;
- android_LogPriority priority;
- int32_t pid;
- int32_t tid;
- const char * tag;
- size_t messageLen;
- const char * message;
-} AndroidLogEntry;
-
-AndroidLogFormat *android_log_format_new();
-
-void android_log_format_free(AndroidLogFormat *p_format);
-
-/* currently returns 0 if format is a modifier, 1 if not */
-int android_log_setPrintFormat(AndroidLogFormat *p_format,
- AndroidLogPrintFormat format);
-
-/**
- * Returns FORMAT_OFF on invalid string
- */
-AndroidLogPrintFormat android_log_formatFromString(const char *s);
-
-/**
- * filterExpression: a single filter expression
- * eg "AT:d"
- *
- * returns 0 on success and -1 on invalid expression
- *
- * Assumes single threaded execution
- *
- */
-
-int android_log_addFilterRule(AndroidLogFormat *p_format,
- const char *filterExpression);
-
-
-/**
- * filterString: a whitespace-separated set of filter expressions
- * eg "AT:d *:i"
- *
- * returns 0 on success and -1 on invalid expression
- *
- * Assumes single threaded execution
- *
- */
-
-int android_log_addFilterString(AndroidLogFormat *p_format,
- const char *filterString);
-
-
-/**
- * returns 1 if this log line should be printed based on its priority
- * and tag, and 0 if it should not
- */
-int android_log_shouldPrintLine (
- AndroidLogFormat *p_format, const char *tag, android_LogPriority pri);
-
-
-/**
- * Splits a wire-format buffer into an AndroidLogEntry
- * entry allocated by caller. Pointers will point directly into buf
- *
- * Returns 0 on success and -1 on invalid wire format (entry will be
- * in unspecified state)
- */
-int android_log_processLogBuffer(struct logger_entry *buf,
- AndroidLogEntry *entry);
-
-/**
- * Like android_log_processLogBuffer, but for binary logs.
- *
- * If "map" is non-NULL, it will be used to convert the log tag number
- * into a string.
- */
-int android_log_processBinaryLogBuffer(struct logger_entry *buf,
- AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
- int messageBufLen);
-
-
-/**
- * Formats a log message into a buffer
- *
- * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
- * If return value != defaultBuffer, caller must call free()
- * Returns NULL on malloc error
- */
-
-char *android_log_formatLogLine (
- AndroidLogFormat *p_format,
- char *defaultBuffer,
- size_t defaultBufferSize,
- const AndroidLogEntry *p_line,
- size_t *p_outLength);
-
-
-/**
- * Either print or do not print log line, based on filter
- *
- * Assumes single threaded execution
- *
- */
-int android_log_printLogLine(
- AndroidLogFormat *p_format,
- int fd,
- const AndroidLogEntry *entry);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /*_LOGPRINT_H*/
diff --git a/include/memtrack/memtrack.h b/include/memtrack/memtrack.h
deleted file mode 100644
index 3917300..0000000
--- a/include/memtrack/memtrack.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBMEMTRACK_MEMTRACK_H_
-#define _LIBMEMTRACK_MEMTRACK_H_
-
-#include <sys/types.h>
-#include <stddef.h>
-#include <cutils/compiler.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * struct memtrack_proc
- *
- * an opaque handle to the memory stats on a process.
- * Created with memtrack_proc_new, destroyed by
- * memtrack_proc_destroy. Can be reused multiple times with
- * memtrack_proc_get.
- */
-struct memtrack_proc;
-
-/**
- * memtrack_init
- *
- * Must be called once before calling any other functions. After this function
- * is called, everything else is thread-safe.
- *
- * Returns 0 on success, -errno on error.
- */
-int memtrack_init(void);
-
-/**
- * memtrack_proc_new
- *
- * Return a new handle to hold process memory stats.
- *
- * Returns NULL on error.
- */
-struct memtrack_proc *memtrack_proc_new(void);
-
-/**
- * memtrack_proc_destroy
- *
- * Free all memory associated with a process memory stats handle.
- */
-void memtrack_proc_destroy(struct memtrack_proc *p);
-
-/**
- * memtrack_proc_get
- *
- * Fill a process memory stats handle with data about the given pid. Can be
- * called on a handle that was just allocated with memtrack_proc_new,
- * or on a handle that has been previously passed to memtrack_proc_get
- * to replace the data with new data on the same or another process. It is
- * expected that the second call on the same handle should not require
- * allocating any new memory.
- *
- * Returns 0 on success, -errno on error.
- */
-int memtrack_proc_get(struct memtrack_proc *p, pid_t pid);
-
-/**
- * memtrack_proc_graphics_total
- *
- * Return total amount of memory that has been allocated for use as window
- * buffers. Does not differentiate between memory that has already been
- * accounted for by reading /proc/pid/smaps and memory that has not been
- * accounted for.
- *
- * Returns non-negative size in bytes on success, -errno on error.
- */
-ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p);
-
-/**
- * memtrack_proc_graphics_pss
- *
- * Return total amount of memory that has been allocated for use as window
- * buffers, but has not already been accounted for by reading /proc/pid/smaps.
- * Memory that is shared across processes may already be divided by the
- * number of processes that share it (preferred), or may be charged in full to
- * every process that shares it, depending on the capabilities of the driver.
- *
- * Returns non-negative size in bytes on success, -errno on error.
- */
-ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p);
-
-/**
- * memtrack_proc_gl_total
- *
- * Same as memtrack_proc_graphics_total, but counts GL memory (which
- * should not overlap with graphics memory) instead of graphics memory.
- *
- * Returns non-negative size in bytes on success, -errno on error.
- */
-ssize_t memtrack_proc_gl_total(struct memtrack_proc *p);
-
-/**
- * memtrack_proc_gl_pss
- *
- * Same as memtrack_proc_graphics_total, but counts GL memory (which
- * should not overlap with graphics memory) instead of graphics memory.
- *
- * Returns non-negative size in bytes on success, -errno on error.
- */
-ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p);
-
-/**
- * memtrack_proc_other_total
- *
- * Same as memtrack_proc_graphics_total, but counts miscellaneous memory
- * not tracked by gl or graphics calls above.
- *
- * Returns non-negative size in bytes on success, -errno on error.
- */
-ssize_t memtrack_proc_other_total(struct memtrack_proc *p);
-
-/**
- * memtrack_proc_other_pss
- *
- * Same as memtrack_proc_graphics_total, but counts miscellaneous memory
- * not tracked by gl or graphics calls above.
- *
- * Returns non-negative size in bytes on success, -errno on error.
- */
-ssize_t memtrack_proc_other_pss(struct memtrack_proc *p);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/mincrypt/dsa_sig.h b/include/mincrypt/dsa_sig.h
deleted file mode 100644
index b0d91cd..0000000
--- a/include/mincrypt/dsa_sig.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_
-#define SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_
-
-#include "mincrypt/p256.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Returns 0 if input sig is not a valid ASN.1 sequence
-int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ */
diff --git a/include/mincrypt/hash-internal.h b/include/mincrypt/hash-internal.h
deleted file mode 100644
index c813b44..0000000
--- a/include/mincrypt/hash-internal.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2007 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
-#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
-
-struct HASH_CTX; // forward decl
-
-typedef struct HASH_VTAB {
- void (* const init)(struct HASH_CTX*);
- void (* const update)(struct HASH_CTX*, const void*, int);
- const uint8_t* (* const final)(struct HASH_CTX*);
- const uint8_t* (* const hash)(const void*, int, uint8_t*);
- int size;
-} HASH_VTAB;
-
-typedef struct HASH_CTX {
- const HASH_VTAB * f;
- uint64_t count;
- uint8_t buf[64];
- uint32_t state[8]; // upto SHA2
-} HASH_CTX;
-
-#define HASH_init(ctx) (ctx)->f->init(ctx)
-#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len)
-#define HASH_final(ctx) (ctx)->f->final(ctx)
-#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest)
-#define HASH_size(ctx) (ctx)->f->size
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
-
-#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
diff --git a/include/mincrypt/p256.h b/include/mincrypt/p256.h
deleted file mode 100644
index 465a1b9..0000000
--- a/include/mincrypt/p256.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
-#define SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
-
-// Collection of routines manipulating 256 bit unsigned integers.
-// Just enough to implement ecdsa-p256 and related algorithms.
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define P256_BITSPERDIGIT 32
-#define P256_NDIGITS 8
-#define P256_NBYTES 32
-
-typedef int p256_err;
-typedef uint32_t p256_digit;
-typedef int32_t p256_sdigit;
-typedef uint64_t p256_ddigit;
-typedef int64_t p256_sddigit;
-
-// Defining p256_int as struct to leverage struct assigment.
-typedef struct {
- p256_digit a[P256_NDIGITS];
-} p256_int;
-
-extern const p256_int SECP256r1_n; // Curve order
-extern const p256_int SECP256r1_p; // Curve prime
-extern const p256_int SECP256r1_b; // Curve param
-
-// Initialize a p256_int to zero.
-void p256_init(p256_int* a);
-
-// Clear a p256_int to zero.
-void p256_clear(p256_int* a);
-
-// Return bit. Index 0 is least significant.
-int p256_get_bit(const p256_int* a, int index);
-
-// b := a % MOD
-void p256_mod(
- const p256_int* MOD,
- const p256_int* a,
- p256_int* b);
-
-// c := a * (top_b | b) % MOD
-void p256_modmul(
- const p256_int* MOD,
- const p256_int* a,
- const p256_digit top_b,
- const p256_int* b,
- p256_int* c);
-
-// b := 1 / a % MOD
-// MOD best be SECP256r1_n
-void p256_modinv(
- const p256_int* MOD,
- const p256_int* a,
- p256_int* b);
-
-// b := 1 / a % MOD
-// MOD best be SECP256r1_n
-// Faster than p256_modinv()
-void p256_modinv_vartime(
- const p256_int* MOD,
- const p256_int* a,
- p256_int* b);
-
-// b := a << (n % P256_BITSPERDIGIT)
-// Returns the bits shifted out of most significant digit.
-p256_digit p256_shl(const p256_int* a, int n, p256_int* b);
-
-// b := a >> (n % P256_BITSPERDIGIT)
-void p256_shr(const p256_int* a, int n, p256_int* b);
-
-int p256_is_zero(const p256_int* a);
-int p256_is_odd(const p256_int* a);
-int p256_is_even(const p256_int* a);
-
-// Returns -1, 0 or 1.
-int p256_cmp(const p256_int* a, const p256_int *b);
-
-// c: = a - b
-// Returns -1 on borrow.
-int p256_sub(const p256_int* a, const p256_int* b, p256_int* c);
-
-// c := a + b
-// Returns 1 on carry.
-int p256_add(const p256_int* a, const p256_int* b, p256_int* c);
-
-// c := a + (single digit)b
-// Returns carry 1 on carry.
-int p256_add_d(const p256_int* a, p256_digit b, p256_int* c);
-
-// ec routines.
-
-// {out_x,out_y} := nG
-void p256_base_point_mul(const p256_int *n,
- p256_int *out_x,
- p256_int *out_y);
-
-// {out_x,out_y} := n{in_x,in_y}
-void p256_point_mul(const p256_int *n,
- const p256_int *in_x,
- const p256_int *in_y,
- p256_int *out_x,
- p256_int *out_y);
-
-// {out_x,out_y} := n1G + n2{in_x,in_y}
-void p256_points_mul_vartime(
- const p256_int *n1, const p256_int *n2,
- const p256_int *in_x, const p256_int *in_y,
- p256_int *out_x, p256_int *out_y);
-
-// Return whether point {x,y} is on curve.
-int p256_is_valid_point(const p256_int* x, const p256_int* y);
-
-// Outputs big-endian binary form. No leading zero skips.
-void p256_to_bin(const p256_int* src, uint8_t dst[P256_NBYTES]);
-
-// Reads from big-endian binary form,
-// thus pre-pad with leading zeros if short.
-void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst);
-
-#define P256_DIGITS(x) ((x)->a)
-#define P256_DIGIT(x,y) ((x)->a[y])
-
-#define P256_ZERO {{0}}
-#define P256_ONE {{1}}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
diff --git a/include/mincrypt/p256_ecdsa.h b/include/mincrypt/p256_ecdsa.h
deleted file mode 100644
index da339fa..0000000
--- a/include/mincrypt/p256_ecdsa.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
-#define SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
-
-// Using current directory as relative include path here since
-// this code typically gets lifted into a variety of build systems
-// and directory structures.
-#include "p256.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Returns 0 if {r,s} is not a signature on message for
-// public key {key_x,key_y}.
-//
-// Note: message is a p256_int.
-// Convert from a binary string using p256_from_bin().
-int p256_ecdsa_verify(const p256_int* key_x,
- const p256_int* key_y,
- const p256_int* message,
- const p256_int* r, const p256_int* s);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h
deleted file mode 100644
index 3d0556b..0000000
--- a/include/mincrypt/rsa.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* rsa.h
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in the
-** documentation and/or other materials provided with the distribution.
-** * Neither the name of Google Inc. nor the names of its contributors may
-** be used to endorse or promote products derived from this software
-** without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_
-#define SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_
-
-#include <inttypes.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define RSANUMBYTES 256 /* 2048 bit key length */
-#define RSANUMWORDS (RSANUMBYTES / sizeof(uint32_t))
-
-typedef struct RSAPublicKey {
- int len; /* Length of n[] in number of uint32_t */
- uint32_t n0inv; /* -1 / n[0] mod 2^32 */
- uint32_t n[RSANUMWORDS]; /* modulus as little endian array */
- uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */
- int exponent; /* 3 or 65537 */
-} RSAPublicKey;
-
-int RSA_verify(const RSAPublicKey *key,
- const uint8_t* signature,
- const int len,
- const uint8_t* hash,
- const int hash_len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_
diff --git a/include/mincrypt/sha.h b/include/mincrypt/sha.h
deleted file mode 100644
index ef60aab..0000000
--- a/include/mincrypt/sha.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
-#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
-
-#include <stdint.h>
-#include "hash-internal.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
-
-typedef HASH_CTX SHA_CTX;
-
-void SHA_init(SHA_CTX* ctx);
-void SHA_update(SHA_CTX* ctx, const void* data, int len);
-const uint8_t* SHA_final(SHA_CTX* ctx);
-
-// Convenience method. Returns digest address.
-// NOTE: *digest needs to hold SHA_DIGEST_SIZE bytes.
-const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest);
-
-#define SHA_DIGEST_SIZE 20
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
-
-#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
diff --git a/include/mincrypt/sha256.h b/include/mincrypt/sha256.h
deleted file mode 100644
index 3a87c31..0000000
--- a/include/mincrypt/sha256.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2011 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
-#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
-
-#include <stdint.h>
-#include "hash-internal.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
-
-typedef HASH_CTX SHA256_CTX;
-
-void SHA256_init(SHA256_CTX* ctx);
-void SHA256_update(SHA256_CTX* ctx, const void* data, int len);
-const uint8_t* SHA256_final(SHA256_CTX* ctx);
-
-// Convenience method. Returns digest address.
-const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest);
-
-#define SHA256_DIGEST_SIZE 32
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
-
-#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
diff --git a/include/nativebridge/native_bridge.h b/include/nativebridge/native_bridge.h
index 18300bc..45266de 100644
--- a/include/nativebridge/native_bridge.h
+++ b/include/nativebridge/native_bridge.h
@@ -62,12 +62,19 @@
bool NativeBridgeInitialized();
// Load a shared library that is supported by the native bridge.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeLoadLibraryExt() instead in namespace scenario.
void* NativeBridgeLoadLibrary(const char* libpath, int flag);
// Get a native bridge trampoline for specified native method.
void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len);
-// True if native library is valid and is for an ABI that is supported by native bridge.
+// True if native library paths are valid and is for an ABI that is supported by native bridge.
+// The *libpath* must point to a library.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeIsPathSupported() instead in namespace scenario.
bool NativeBridgeIsSupported(const char* libpath);
// Returns the version number of the native bridge. This information is available after a
@@ -91,6 +98,48 @@
// This functionality is exposed mainly for testing.
bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename);
+// Decrements the reference count on the dynamic library handler. If the reference count drops
+// to zero then the dynamic library is unloaded.
+int NativeBridgeUnloadLibrary(void* handle);
+
+// Get last error message of native bridge when fail to load library or search symbol.
+// This is reflection of dlerror() for native bridge.
+const char* NativeBridgeGetError();
+
+struct native_bridge_namespace_t;
+
+// True if native library paths are valid and is for an ABI that is supported by native bridge.
+// Different from NativeBridgeIsSupported(), the *path* here must be a directory containing
+// libraries of an ABI.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeIsSupported() instead in non-namespace scenario.
+bool NativeBridgeIsPathSupported(const char* path);
+
+// Initializes public and anonymous namespace at native bridge side.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+bool NativeBridgeInitNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path);
+
+// Create a namespace and pass the key of related namespaces to native bridge.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
+ const char* ld_library_path,
+ const char* default_library_path,
+ uint64_t type,
+ const char* permitted_when_isolated_path,
+ native_bridge_namespace_t* parent_ns);
+
+// Load a shared library with namespace key that is supported by the native bridge.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
+
// Native bridge interfaces to runtime.
struct NativeBridgeCallbacks {
// Version number of the interface.
@@ -114,6 +163,9 @@
// flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
// Returns:
// The opaque handle of the shared library if sucessful, otherwise NULL
+ //
+ // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+ // Use loadLibraryExt instead in namespace scenario.
void* (*loadLibrary)(const char* libpath, int flag);
// Get a native bridge trampoline for specified native method. The trampoline has same
@@ -133,6 +185,9 @@
// libpath [IN] path to the shared library
// Returns:
// TRUE if library is supported by native bridge, FALSE otherwise
+ //
+ // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+ // Use isPathSupported instead in namespace scenario.
bool (*isSupported)(const char* libpath);
// Provide environment values required by the app running with native bridge according to the
@@ -169,6 +224,88 @@
// runtime.
// Otherwise, a pointer to the signal handler.
NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal);
+
+ // Added callbacks in version 3.
+
+ // Decrements the reference count on the dynamic library handler. If the reference count drops
+ // to zero then the dynamic library is unloaded.
+ //
+ // Parameters:
+ // handle [IN] the handler of a dynamic library.
+ //
+ // Returns:
+ // 0 on success, and nonzero on error.
+ int (*unloadLibrary)(void* handle);
+
+ // Dump the last failure message of native bridge when fail to load library or search symbol.
+ //
+ // Parameters:
+ //
+ // Returns:
+ // A string describing the most recent error that occurred when load library
+ // or lookup symbol via native bridge.
+ const char* (*getError)();
+
+ // Check whether library paths are supported by native bridge.
+ //
+ // Parameters:
+ // library_path [IN] search paths for native libraries (directories separated by ':')
+ // Returns:
+ // TRUE if libraries within search paths are supported by native bridge, FALSE otherwise
+ //
+ // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+ // Use isSupported instead in non-namespace scenario.
+ bool (*isPathSupported)(const char* library_path);
+
+ // Initializes anonymous namespace at native bridge side and pass the key of
+ // two namespaces(default and anonymous) owned by dynamic linker to native bridge.
+ //
+ // Parameters:
+ // public_ns_sonames [IN] the name of "public" libraries.
+ // anon_ns_library_path [IN] the library search path of (anonymous) namespace.
+ // Returns:
+ // true if the pass is ok.
+ // Otherwise, false.
+ //
+ // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+ // Should not use in non-namespace scenario.
+ bool (*initNamespace)(const char* public_ns_sonames,
+ const char* anon_ns_library_path);
+
+
+ // Create a namespace and pass the key of releated namespaces to native bridge.
+ //
+ // Parameters:
+ // name [IN] the name of the namespace.
+ // ld_library_path [IN] the first set of library search paths of the namespace.
+ // default_library_path [IN] the second set of library search path of the namespace.
+ // type [IN] the attribute of the namespace.
+ // permitted_when_isolated_path [IN] the permitted path for isolated namespace(if it is).
+ // parent_ns [IN] the pointer of the parent namespace to be inherited from.
+ // Returns:
+ // native_bridge_namespace_t* for created namespace or nullptr in the case of error.
+ //
+ // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+ // Should not use in non-namespace scenario.
+ native_bridge_namespace_t* (*createNamespace)(const char* name,
+ const char* ld_library_path,
+ const char* default_library_path,
+ uint64_t type,
+ const char* permitted_when_isolated_path,
+ native_bridge_namespace_t* parent_ns);
+
+ // Load a shared library within a namespace.
+ //
+ // Parameters:
+ // libpath [IN] path to the shared library
+ // flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
+ // ns [IN] the pointer of the namespace in which the library should be loaded.
+ // Returns:
+ // The opaque handle of the shared library if sucessful, otherwise NULL
+ //
+ // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+ // Use loadLibrary instead in non-namespace scenario.
+ void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
};
// Runtime interfaces to native bridge.
diff --git a/include/netutils/dhcp.h b/include/netutils/dhcp.h
deleted file mode 100644
index de6bc82..0000000
--- a/include/netutils/dhcp.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NETUTILS_DHCP_H_
-#define _NETUTILS_DHCP_H_
-
-#include <sys/cdefs.h>
-#include <arpa/inet.h>
-
-__BEGIN_DECLS
-
-extern int do_dhcp(char *iname);
-extern int dhcp_do_request(const char *ifname,
- char *ipaddr,
- char *gateway,
- uint32_t *prefixLength,
- char *dns[],
- char *server,
- uint32_t *lease,
- char *vendorInfo,
- char *domain,
- char *mtu);
-extern int dhcp_do_request_renew(const char *ifname,
- char *ipaddr,
- char *gateway,
- uint32_t *prefixLength,
- char *dns[],
- char *server,
- uint32_t *lease,
- char *vendorInfo,
- char *domain,
- char *mtu);
-extern int dhcp_stop(const char *ifname);
-extern int dhcp_release_lease(const char *ifname);
-extern char *dhcp_get_errmsg();
-
-__END_DECLS
-
-#endif /* _NETUTILS_DHCP_H_ */
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 7047e0f..8e2bc1c 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -19,6 +19,33 @@
** by the device side of adb.
*/
+/*
+ * This file is consumed by build/tools/fs_config and is used
+ * for generating various files. Anything #define AID_<name>
+ * becomes the mapping for getpwnam/getpwuid, etc. The <name>
+ * field is lowercased.
+ * For example:
+ * #define AID_FOO_BAR 6666 becomes a friendly name of "foo_bar"
+ *
+ * The above holds true with the exception of:
+ * mediacodec
+ * mediaex
+ * mediadrm
+ * Whose friendly names do not match the #define statements.
+ *
+ * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END
+ * can be used to define reserved OEM ranges used for sanity checks
+ * during the build process. The rules are, they must end with START/END
+ * The proper convention is incrementing a number like so:
+ * AID_OEM_RESERVED_START
+ * AID_OEM_RESERVED_1_START
+ * AID_OEM_RESERVED_2_START
+ * ...
+ * The same applies to the END.
+ * They are not required to be in order, but must not overlap each other and
+ * must define a START and END'ing range. START must be smaller than END.
+ */
+
#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
#define _ANDROID_FILESYSTEM_CONFIG_H_
@@ -32,6 +59,8 @@
#include "android_filesystem_capability.h"
#endif
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
+
/* This is the master Users and Groups config for the platform.
* DO NOT EVER RENUMBER
*/
@@ -78,6 +107,28 @@
#define AID_LOGD 1036 /* log daemon */
#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */
#define AID_DBUS 1038 /* dbus-daemon IPC broker process */
+#define AID_TLSDATE 1039 /* tlsdate unprivileged user */
+#define AID_MEDIA_EX 1040 /* mediaextractor process */
+#define AID_AUDIOSERVER 1041 /* audioserver process */
+#define AID_METRICS_COLL 1042 /* metrics_collector process */
+#define AID_METRICSD 1043 /* metricsd process */
+#define AID_WEBSERV 1044 /* webservd process */
+#define AID_DEBUGGERD 1045 /* debuggerd unprivileged user */
+#define AID_MEDIA_CODEC 1046 /* mediacodec process */
+#define AID_CAMERASERVER 1047 /* cameraserver process */
+#define AID_FIREWALL 1048 /* firewalld process */
+#define AID_TRUNKS 1049 /* trunksd process (TPM daemon) */
+#define AID_NVRAM 1050 /* Access-controlled NVRAM */
+#define AID_DNS 1051 /* DNS resolution daemon (system: netd) */
+#define AID_DNS_TETHER 1052 /* DNS resolution daemon (tether: dnsmasq) */
+#define AID_WEBVIEW_ZYGOTE 1053 /* WebView zygote process */
+#define AID_VEHICLE_NETWORK 1054 /* Vehicle network service */
+#define AID_MEDIA_AUDIO 1055 /* GID for audio files on internal media storage */
+#define AID_MEDIA_VIDEO 1056 /* GID for video files on internal media storage */
+#define AID_MEDIA_IMAGE 1057 /* GID for image files on internal media storage */
+#define AID_TOMBSTONED 1058 /* tombstoned user */
+#define AID_MEDIA_OBB 1059 /* GID for OBB files on internal media storage */
+/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
@@ -97,7 +148,8 @@
#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */
#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
-#define AID_NET_BT_STACK 3008 /* bluetooth: access config files */
+#define AID_READPROC 3009 /* Allow /proc read access */
+#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */
/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
#define AID_OEM_RESERVED_2_START 5000
@@ -107,94 +159,34 @@
#define AID_MISC 9998 /* access to misc storage */
#define AID_NOBODY 9999
-#define AID_APP 10000 /* first app user */
+#define AID_APP 10000 /* TODO: switch users over to AID_APP_START */
+#define AID_APP_START 10000 /* first app user */
+#define AID_APP_END 19999 /* last app user */
-#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
-#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
+#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
+#define AID_CACHE_GID_END 29999 /* end of gids for apps to mark cached data */
-#define AID_USER 100000 /* offset for uid ranges for each user */
+#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
+#define AID_EXT_GID_END 39999 /* end of gids for apps to mark external data */
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
-#define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */
+#define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */
+
+#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
+#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
+
+#define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */
+#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
+
+/*
+ * android_ids has moved to pwd/grp functionality.
+ * If you need to add one, the structure is now
+ * auto-generated based on the AID_ constraints
+ * documented at the top of this header file.
+ * Also see build/tools/fs_config for more details.
+ */
#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
-/*
- * Used in:
- * bionic/libc/bionic/stubs.cpp
- * external/libselinux/src/android.c
- * system/core/logd/LogStatistics.cpp
- * system/core/init/ueventd.cpp
- * system/core/init/util.cpp
- */
-struct android_id_info {
- const char *name;
- unsigned aid;
-};
-
-static const struct android_id_info android_ids[] = {
- { "root", AID_ROOT, },
-
- { "system", AID_SYSTEM, },
-
- { "radio", AID_RADIO, },
- { "bluetooth", AID_BLUETOOTH, },
- { "graphics", AID_GRAPHICS, },
- { "input", AID_INPUT, },
- { "audio", AID_AUDIO, },
- { "camera", AID_CAMERA, },
- { "log", AID_LOG, },
- { "compass", AID_COMPASS, },
- { "mount", AID_MOUNT, },
- { "wifi", AID_WIFI, },
- { "adb", AID_ADB, },
- { "install", AID_INSTALL, },
- { "media", AID_MEDIA, },
- { "dhcp", AID_DHCP, },
- { "sdcard_rw", AID_SDCARD_RW, },
- { "vpn", AID_VPN, },
- { "keystore", AID_KEYSTORE, },
- { "usb", AID_USB, },
- { "drm", AID_DRM, },
- { "mdnsr", AID_MDNSR, },
- { "gps", AID_GPS, },
- // AID_UNUSED1
- { "media_rw", AID_MEDIA_RW, },
- { "mtp", AID_MTP, },
- // AID_UNUSED2
- { "drmrpc", AID_DRMRPC, },
- { "nfc", AID_NFC, },
- { "sdcard_r", AID_SDCARD_R, },
- { "clat", AID_CLAT, },
- { "loop_radio", AID_LOOP_RADIO, },
- { "mediadrm", AID_MEDIA_DRM, },
- { "package_info", AID_PACKAGE_INFO, },
- { "sdcard_pics", AID_SDCARD_PICS, },
- { "sdcard_av", AID_SDCARD_AV, },
- { "sdcard_all", AID_SDCARD_ALL, },
- { "logd", AID_LOGD, },
- { "shared_relro", AID_SHARED_RELRO, },
- { "dbus", AID_DBUS, },
-
- { "shell", AID_SHELL, },
- { "cache", AID_CACHE, },
- { "diag", AID_DIAG, },
-
- { "net_bt_admin", AID_NET_BT_ADMIN, },
- { "net_bt", AID_NET_BT, },
- { "inet", AID_INET, },
- { "net_raw", AID_NET_RAW, },
- { "net_admin", AID_NET_ADMIN, },
- { "net_bw_stats", AID_NET_BW_STATS, },
- { "net_bw_acct", AID_NET_BW_ACCT, },
- { "net_bt_stack", AID_NET_BT_STACK, },
-
- { "everybody", AID_EVERYBODY, },
- { "misc", AID_MISC, },
- { "nobody", AID_NOBODY, },
-};
-
-#define android_id_count \
- (sizeof(android_ids) / sizeof(android_ids[0]))
struct fs_path_config {
unsigned mode;
@@ -212,13 +204,13 @@
* Used in:
* build/tools/fs_config/fs_config.c
* build/tools/fs_get_stats/fs_get_stats.c
- * external/genext2fs/genext2fs.c
+ * system/extras/ext4_utils/make_ext4fs_main.c
* external/squashfs-tools/squashfs-tools/android.c
* system/core/cpio/mkbootfs.c
* system/core/adb/file_sync_service.cpp
* system/extras/ext4_utils/canned_fs_config.c
*/
-void fs_config(const char *path, int dir,
+void fs_config(const char *path, int dir, const char *target_out_path,
unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities);
ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc);
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
deleted file mode 100644
index 04238a6..0000000
--- a/include/private/android_logger.h
+++ /dev/null
@@ -1,98 +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.
- */
-
-/* This file is used to define the internal protocol for the Android Logger */
-
-#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-
-#include <stdint.h>
-
-#include <log/log.h>
-#include <log/log_read.h>
-
-#define LOGGER_MAGIC 'l'
-
-/* Header Structure to pstore */
-typedef struct __attribute__((__packed__)) {
- uint8_t magic;
- uint16_t len;
- uint16_t uid;
- uint16_t pid;
-} android_pmsg_log_header_t;
-
-/* Header Structure to logd, and second header for pstore */
-typedef struct __attribute__((__packed__)) {
- typeof_log_id_t id;
- uint16_t tid;
- log_time realtime;
-} android_log_header_t;
-
-/* Event Header Structure to logd */
-typedef struct __attribute__((__packed__)) {
- int32_t tag; // Little Endian Order
-} android_event_header_t;
-
-/* Event payload EVENT_TYPE_INT */
-typedef struct __attribute__((__packed__)) {
- int8_t type; // EVENT_TYPE_INT
- int32_t data; // Little Endian Order
-} android_event_int_t;
-
-/* Event with single EVENT_TYPE_INT */
-typedef struct __attribute__((__packed__)) {
- android_event_header_t header;
- android_event_int_t payload;
-} android_log_event_int_t;
-
-/* Event payload EVENT_TYPE_LONG */
-typedef struct __attribute__((__packed__)) {
- int8_t type; // EVENT_TYPE_LONG
- int64_t data; // Little Endian Order
-} android_event_long_t;
-
-/* Event with single EVENT_TYPE_LONG */
-typedef struct __attribute__((__packed__)) {
- android_event_header_t header;
- android_event_long_t payload;
-} android_log_event_long_t;
-
-/*
- * Event payload EVENT_TYPE_STRING
- *
- * Danger: do not embed this structure into another structure.
- * This structure uses a flexible array member, and when
- * compiled using g++, __builtin_object_size(data, 1) returns
- * a bad value. This is possibly a g++ bug, or a bug due to
- * the fact that flexible array members are not supported
- * in C++.
- * http://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c
- */
-typedef struct __attribute__((__packed__)) {
- int8_t type; // EVENT_TYPE_STRING;
- int32_t length; // Little Endian Order
- char data[];
-} android_event_string_t;
-
-/* Event with single EVENT_TYPE_STRING */
-typedef struct __attribute__((__packed__)) {
- android_event_header_t header;
- int8_t type; // EVENT_TYPE_STRING;
- int32_t length; // Little Endian Order
- char data[];
-} android_log_event_string_t;
-
-#endif
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
new file mode 120000
index 0000000..f187a6d
--- /dev/null
+++ b/include/private/android_logger.h
@@ -0,0 +1 @@
+../../liblog/include/private/android_logger.h
\ No newline at end of file
diff --git a/include/private/canned_fs_config.h b/include/private/canned_fs_config.h
new file mode 100644
index 0000000..d9f51ca
--- /dev/null
+++ b/include/private/canned_fs_config.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CANNED_FS_CONFIG_H
+#define _CANNED_FS_CONFIG_H
+
+#include <inttypes.h>
+
+int load_canned_fs_config(const char* fn);
+void canned_fs_config(const char* path, int dir, const char* target_out_path,
+ unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities);
+
+#endif
diff --git a/include/system/audio.h b/include/system/audio.h
deleted file mode 100644
index ced2005..0000000
--- a/include/system/audio.h
+++ /dev/null
@@ -1,1375 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_AUDIO_CORE_H
-#define ANDROID_AUDIO_CORE_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-#include <cutils/bitops.h>
-
-__BEGIN_DECLS
-
-/* The enums were moved here mostly from
- * frameworks/base/include/media/AudioSystem.h
- */
-
-/* device address used to refer to the standard remote submix */
-#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0"
-
-/* AudioFlinger and AudioPolicy services use I/O handles to identify audio sources and sinks */
-typedef int audio_io_handle_t;
-#define AUDIO_IO_HANDLE_NONE 0
-
-/* Audio stream types */
-typedef enum {
- /* These values must kept in sync with
- * frameworks/base/media/java/android/media/AudioSystem.java
- */
- AUDIO_STREAM_DEFAULT = -1,
- AUDIO_STREAM_MIN = 0,
- AUDIO_STREAM_VOICE_CALL = 0,
- AUDIO_STREAM_SYSTEM = 1,
- AUDIO_STREAM_RING = 2,
- AUDIO_STREAM_MUSIC = 3,
- AUDIO_STREAM_ALARM = 4,
- AUDIO_STREAM_NOTIFICATION = 5,
- AUDIO_STREAM_BLUETOOTH_SCO = 6,
- AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user
- * and must be routed to speaker
- */
- AUDIO_STREAM_DTMF = 8,
- AUDIO_STREAM_TTS = 9, /* Transmitted Through Speaker.
- * Plays over speaker only, silent on other devices.
- */
- AUDIO_STREAM_ACCESSIBILITY = 10, /* For accessibility talk back prompts */
- AUDIO_STREAM_REROUTING = 11, /* For dynamic policy output mixes */
- AUDIO_STREAM_PATCH = 12, /* For internal audio flinger tracks. Fixed volume */
- AUDIO_STREAM_PUBLIC_CNT = AUDIO_STREAM_TTS + 1,
- AUDIO_STREAM_CNT = AUDIO_STREAM_PATCH + 1,
-} audio_stream_type_t;
-
-/* Do not change these values without updating their counterparts
- * in frameworks/base/media/java/android/media/AudioAttributes.java
- */
-typedef enum {
- AUDIO_CONTENT_TYPE_UNKNOWN = 0,
- AUDIO_CONTENT_TYPE_SPEECH = 1,
- AUDIO_CONTENT_TYPE_MUSIC = 2,
- AUDIO_CONTENT_TYPE_MOVIE = 3,
- AUDIO_CONTENT_TYPE_SONIFICATION = 4,
-
- AUDIO_CONTENT_TYPE_CNT,
- AUDIO_CONTENT_TYPE_MAX = AUDIO_CONTENT_TYPE_CNT - 1,
-} audio_content_type_t;
-
-/* Do not change these values without updating their counterparts
- * in frameworks/base/media/java/android/media/AudioAttributes.java
- */
-typedef enum {
- AUDIO_USAGE_UNKNOWN = 0,
- AUDIO_USAGE_MEDIA = 1,
- AUDIO_USAGE_VOICE_COMMUNICATION = 2,
- AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING = 3,
- AUDIO_USAGE_ALARM = 4,
- AUDIO_USAGE_NOTIFICATION = 5,
- AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6,
- AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7,
- AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8,
- AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9,
- AUDIO_USAGE_NOTIFICATION_EVENT = 10,
- AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY = 11,
- AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12,
- AUDIO_USAGE_ASSISTANCE_SONIFICATION = 13,
- AUDIO_USAGE_GAME = 14,
- AUDIO_USAGE_VIRTUAL_SOURCE = 15,
-
- AUDIO_USAGE_CNT,
- AUDIO_USAGE_MAX = AUDIO_USAGE_CNT - 1,
-} audio_usage_t;
-
-typedef uint32_t audio_flags_mask_t;
-
-/* Do not change these values without updating their counterparts
- * in frameworks/base/media/java/android/media/AudioAttributes.java
- */
-enum {
- AUDIO_FLAG_AUDIBILITY_ENFORCED = 0x1,
- AUDIO_FLAG_SECURE = 0x2,
- AUDIO_FLAG_SCO = 0x4,
- AUDIO_FLAG_BEACON = 0x8,
- AUDIO_FLAG_HW_AV_SYNC = 0x10,
- AUDIO_FLAG_HW_HOTWORD = 0x20,
-};
-
-/* Do not change these values without updating their counterparts
- * in frameworks/base/media/java/android/media/MediaRecorder.java,
- * frameworks/av/services/audiopolicy/AudioPolicyService.cpp,
- * and system/media/audio_effects/include/audio_effects/audio_effects_conf.h!
- */
-typedef enum {
- AUDIO_SOURCE_DEFAULT = 0,
- AUDIO_SOURCE_MIC = 1,
- AUDIO_SOURCE_VOICE_UPLINK = 2,
- AUDIO_SOURCE_VOICE_DOWNLINK = 3,
- AUDIO_SOURCE_VOICE_CALL = 4,
- AUDIO_SOURCE_CAMCORDER = 5,
- AUDIO_SOURCE_VOICE_RECOGNITION = 6,
- AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
- AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */
- /* An example of remote presentation is Wifi Display */
- /* where a dongle attached to a TV can be used to */
- /* play the mix captured by this audio source. */
- AUDIO_SOURCE_CNT,
- AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1,
- AUDIO_SOURCE_FM_TUNER = 1998,
- AUDIO_SOURCE_HOTWORD = 1999, /* A low-priority, preemptible audio source for
- for background software hotword detection.
- Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION.
- Used only internally to the framework. Not exposed
- at the audio HAL. */
-} audio_source_t;
-
-/* Audio attributes */
-#define AUDIO_ATTRIBUTES_TAGS_MAX_SIZE 256
-typedef struct {
- audio_content_type_t content_type;
- audio_usage_t usage;
- audio_source_t source;
- audio_flags_mask_t flags;
- char tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE]; /* UTF8 */
-} audio_attributes_t;
-
-/* special audio session values
- * (XXX: should this be living in the audio effects land?)
- */
-typedef enum {
- /* session for effects attached to a particular output stream
- * (value must be less than 0)
- */
- AUDIO_SESSION_OUTPUT_STAGE = -1,
-
- /* session for effects applied to output mix. These effects can
- * be moved by audio policy manager to another output stream
- * (value must be 0)
- */
- AUDIO_SESSION_OUTPUT_MIX = 0,
-
- /* application does not specify an explicit session ID to be used,
- * and requests a new session ID to be allocated
- * TODO use unique values for AUDIO_SESSION_OUTPUT_MIX and AUDIO_SESSION_ALLOCATE,
- * after all uses have been updated from 0 to the appropriate symbol, and have been tested.
- */
- AUDIO_SESSION_ALLOCATE = 0,
-} audio_session_t;
-
-/* a unique ID allocated by AudioFlinger for use as a audio_io_handle_t or audio_session_t */
-typedef int audio_unique_id_t;
-
-#define AUDIO_UNIQUE_ID_ALLOCATE AUDIO_SESSION_ALLOCATE
-
-/* Audio sub formats (see enum audio_format). */
-
-/* PCM sub formats */
-typedef enum {
- /* All of these are in native byte order */
- AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */
- AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */
- AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */
- AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */
- AUDIO_FORMAT_PCM_SUB_FLOAT = 0x5, /* PCM single-precision floating point */
- AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED = 0x6, /* PCM signed .23 fixed point packed in 3 bytes */
-} audio_format_pcm_sub_fmt_t;
-
-/* The audio_format_*_sub_fmt_t declarations are not currently used */
-
-/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3
- * frame header to specify bit rate, stereo mode, version...
- */
-typedef enum {
- AUDIO_FORMAT_MP3_SUB_NONE = 0x0,
-} audio_format_mp3_sub_fmt_t;
-
-/* AMR NB/WB sub format field definition: specify frame block interleaving,
- * bandwidth efficient or octet aligned, encoding mode for recording...
- */
-typedef enum {
- AUDIO_FORMAT_AMR_SUB_NONE = 0x0,
-} audio_format_amr_sub_fmt_t;
-
-/* AAC sub format field definition: specify profile or bitrate for recording... */
-typedef enum {
- AUDIO_FORMAT_AAC_SUB_MAIN = 0x1,
- AUDIO_FORMAT_AAC_SUB_LC = 0x2,
- AUDIO_FORMAT_AAC_SUB_SSR = 0x4,
- AUDIO_FORMAT_AAC_SUB_LTP = 0x8,
- AUDIO_FORMAT_AAC_SUB_HE_V1 = 0x10,
- AUDIO_FORMAT_AAC_SUB_SCALABLE = 0x20,
- AUDIO_FORMAT_AAC_SUB_ERLC = 0x40,
- AUDIO_FORMAT_AAC_SUB_LD = 0x80,
- AUDIO_FORMAT_AAC_SUB_HE_V2 = 0x100,
- AUDIO_FORMAT_AAC_SUB_ELD = 0x200,
-} audio_format_aac_sub_fmt_t;
-
-/* VORBIS sub format field definition: specify quality for recording... */
-typedef enum {
- AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0,
-} audio_format_vorbis_sub_fmt_t;
-
-/* Audio format consists of a main format field (upper 8 bits) and a sub format
- * field (lower 24 bits).
- *
- * The main format indicates the main codec type. The sub format field
- * indicates options and parameters for each format. The sub format is mainly
- * used for record to indicate for instance the requested bitrate or profile.
- * It can also be used for certain formats to give informations not present in
- * the encoded audio stream (e.g. octet alignement for AMR).
- */
-typedef enum {
- AUDIO_FORMAT_INVALID = 0xFFFFFFFFUL,
- AUDIO_FORMAT_DEFAULT = 0,
- AUDIO_FORMAT_PCM = 0x00000000UL, /* DO NOT CHANGE */
- AUDIO_FORMAT_MP3 = 0x01000000UL,
- AUDIO_FORMAT_AMR_NB = 0x02000000UL,
- AUDIO_FORMAT_AMR_WB = 0x03000000UL,
- AUDIO_FORMAT_AAC = 0x04000000UL,
- AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V1*/
- AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V2*/
- AUDIO_FORMAT_VORBIS = 0x07000000UL,
- AUDIO_FORMAT_OPUS = 0x08000000UL,
- AUDIO_FORMAT_AC3 = 0x09000000UL,
- AUDIO_FORMAT_E_AC3 = 0x0A000000UL,
- AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL,
- AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL,
-
- /* Aliases */
- /* note != AudioFormat.ENCODING_PCM_16BIT */
- AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM |
- AUDIO_FORMAT_PCM_SUB_16_BIT),
- /* note != AudioFormat.ENCODING_PCM_8BIT */
- AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM |
- AUDIO_FORMAT_PCM_SUB_8_BIT),
- AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM |
- AUDIO_FORMAT_PCM_SUB_32_BIT),
- AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM |
- AUDIO_FORMAT_PCM_SUB_8_24_BIT),
- AUDIO_FORMAT_PCM_FLOAT = (AUDIO_FORMAT_PCM |
- AUDIO_FORMAT_PCM_SUB_FLOAT),
- AUDIO_FORMAT_PCM_24_BIT_PACKED = (AUDIO_FORMAT_PCM |
- AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED),
- AUDIO_FORMAT_AAC_MAIN = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_MAIN),
- AUDIO_FORMAT_AAC_LC = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_LC),
- AUDIO_FORMAT_AAC_SSR = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_SSR),
- AUDIO_FORMAT_AAC_LTP = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_LTP),
- AUDIO_FORMAT_AAC_HE_V1 = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_HE_V1),
- AUDIO_FORMAT_AAC_SCALABLE = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_SCALABLE),
- AUDIO_FORMAT_AAC_ERLC = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_ERLC),
- AUDIO_FORMAT_AAC_LD = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_LD),
- AUDIO_FORMAT_AAC_HE_V2 = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_HE_V2),
- AUDIO_FORMAT_AAC_ELD = (AUDIO_FORMAT_AAC |
- AUDIO_FORMAT_AAC_SUB_ELD),
-} audio_format_t;
-
-/* For the channel mask for position assignment representation */
-enum {
-
-/* These can be a complete audio_channel_mask_t. */
-
- AUDIO_CHANNEL_NONE = 0x0,
- AUDIO_CHANNEL_INVALID = 0xC0000000,
-
-/* These can be the bits portion of an audio_channel_mask_t
- * with representation AUDIO_CHANNEL_REPRESENTATION_POSITION.
- * Using these bits as a complete audio_channel_mask_t is deprecated.
- */
-
- /* output channels */
- AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1,
- AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2,
- AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4,
- AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8,
- AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10,
- AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20,
- AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40,
- AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80,
- AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100,
- AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200,
- AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400,
- AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800,
- AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000,
- AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000,
- AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000,
- AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000,
- AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000,
- AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000,
-
-/* TODO: should these be considered complete channel masks, or only bits? */
-
- AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT,
- AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
- AUDIO_CHANNEL_OUT_FRONT_RIGHT),
- AUDIO_CHANNEL_OUT_QUAD = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
- AUDIO_CHANNEL_OUT_FRONT_RIGHT |
- AUDIO_CHANNEL_OUT_BACK_LEFT |
- AUDIO_CHANNEL_OUT_BACK_RIGHT),
- AUDIO_CHANNEL_OUT_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD,
- /* like AUDIO_CHANNEL_OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_* */
- AUDIO_CHANNEL_OUT_QUAD_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
- AUDIO_CHANNEL_OUT_FRONT_RIGHT |
- AUDIO_CHANNEL_OUT_SIDE_LEFT |
- AUDIO_CHANNEL_OUT_SIDE_RIGHT),
- AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
- AUDIO_CHANNEL_OUT_FRONT_RIGHT |
- AUDIO_CHANNEL_OUT_FRONT_CENTER |
- AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
- AUDIO_CHANNEL_OUT_BACK_LEFT |
- AUDIO_CHANNEL_OUT_BACK_RIGHT),
- AUDIO_CHANNEL_OUT_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1,
- /* like AUDIO_CHANNEL_OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_* */
- AUDIO_CHANNEL_OUT_5POINT1_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
- AUDIO_CHANNEL_OUT_FRONT_RIGHT |
- AUDIO_CHANNEL_OUT_FRONT_CENTER |
- AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
- AUDIO_CHANNEL_OUT_SIDE_LEFT |
- AUDIO_CHANNEL_OUT_SIDE_RIGHT),
- // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1
- AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
- AUDIO_CHANNEL_OUT_FRONT_RIGHT |
- AUDIO_CHANNEL_OUT_FRONT_CENTER |
- AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
- AUDIO_CHANNEL_OUT_BACK_LEFT |
- AUDIO_CHANNEL_OUT_BACK_RIGHT |
- AUDIO_CHANNEL_OUT_SIDE_LEFT |
- AUDIO_CHANNEL_OUT_SIDE_RIGHT),
- AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
- AUDIO_CHANNEL_OUT_FRONT_RIGHT |
- AUDIO_CHANNEL_OUT_FRONT_CENTER |
- AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
- AUDIO_CHANNEL_OUT_BACK_LEFT |
- AUDIO_CHANNEL_OUT_BACK_RIGHT |
- AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER |
- AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER |
- AUDIO_CHANNEL_OUT_BACK_CENTER|
- AUDIO_CHANNEL_OUT_SIDE_LEFT|
- AUDIO_CHANNEL_OUT_SIDE_RIGHT|
- AUDIO_CHANNEL_OUT_TOP_CENTER|
- AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT|
- AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER|
- AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT|
- AUDIO_CHANNEL_OUT_TOP_BACK_LEFT|
- AUDIO_CHANNEL_OUT_TOP_BACK_CENTER|
- AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
-
-/* These are bits only, not complete values */
-
- /* input channels */
- AUDIO_CHANNEL_IN_LEFT = 0x4,
- AUDIO_CHANNEL_IN_RIGHT = 0x8,
- AUDIO_CHANNEL_IN_FRONT = 0x10,
- AUDIO_CHANNEL_IN_BACK = 0x20,
- AUDIO_CHANNEL_IN_LEFT_PROCESSED = 0x40,
- AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80,
- AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100,
- AUDIO_CHANNEL_IN_BACK_PROCESSED = 0x200,
- AUDIO_CHANNEL_IN_PRESSURE = 0x400,
- AUDIO_CHANNEL_IN_X_AXIS = 0x800,
- AUDIO_CHANNEL_IN_Y_AXIS = 0x1000,
- AUDIO_CHANNEL_IN_Z_AXIS = 0x2000,
- AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000,
- AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000,
-
-/* TODO: should these be considered complete channel masks, or only bits, or deprecated? */
-
- AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT,
- AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT),
- AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK),
- AUDIO_CHANNEL_IN_ALL = (AUDIO_CHANNEL_IN_LEFT |
- AUDIO_CHANNEL_IN_RIGHT |
- AUDIO_CHANNEL_IN_FRONT |
- AUDIO_CHANNEL_IN_BACK|
- AUDIO_CHANNEL_IN_LEFT_PROCESSED |
- AUDIO_CHANNEL_IN_RIGHT_PROCESSED |
- AUDIO_CHANNEL_IN_FRONT_PROCESSED |
- AUDIO_CHANNEL_IN_BACK_PROCESSED|
- AUDIO_CHANNEL_IN_PRESSURE |
- AUDIO_CHANNEL_IN_X_AXIS |
- AUDIO_CHANNEL_IN_Y_AXIS |
- AUDIO_CHANNEL_IN_Z_AXIS |
- AUDIO_CHANNEL_IN_VOICE_UPLINK |
- AUDIO_CHANNEL_IN_VOICE_DNLINK),
-};
-
-/* A channel mask per se only defines the presence or absence of a channel, not the order.
- * But see AUDIO_INTERLEAVE_* below for the platform convention of order.
- *
- * audio_channel_mask_t is an opaque type and its internal layout should not
- * be assumed as it may change in the future.
- * Instead, always use the functions declared in this header to examine.
- *
- * These are the current representations:
- *
- * AUDIO_CHANNEL_REPRESENTATION_POSITION
- * is a channel mask representation for position assignment.
- * Each low-order bit corresponds to the spatial position of a transducer (output),
- * or interpretation of channel (input).
- * The user of a channel mask needs to know the context of whether it is for output or input.
- * The constants AUDIO_CHANNEL_OUT_* or AUDIO_CHANNEL_IN_* apply to the bits portion.
- * It is not permitted for no bits to be set.
- *
- * AUDIO_CHANNEL_REPRESENTATION_INDEX
- * is a channel mask representation for index assignment.
- * Each low-order bit corresponds to a selected channel.
- * There is no platform interpretation of the various bits.
- * There is no concept of output or input.
- * It is not permitted for no bits to be set.
- *
- * All other representations are reserved for future use.
- *
- * Warning: current representation distinguishes between input and output, but this will not the be
- * case in future revisions of the platform. Wherever there is an ambiguity between input and output
- * that is currently resolved by checking the channel mask, the implementer should look for ways to
- * fix it with additional information outside of the mask.
- */
-typedef uint32_t audio_channel_mask_t;
-
-/* Maximum number of channels for all representations */
-#define AUDIO_CHANNEL_COUNT_MAX 30
-
-/* log(2) of maximum number of representations, not part of public API */
-#define AUDIO_CHANNEL_REPRESENTATION_LOG2 2
-
-/* Representations */
-typedef enum {
- AUDIO_CHANNEL_REPRESENTATION_POSITION = 0, // must be zero for compatibility
- // 1 is reserved for future use
- AUDIO_CHANNEL_REPRESENTATION_INDEX = 2,
- // 3 is reserved for future use
-} audio_channel_representation_t;
-
-/* The return value is undefined if the channel mask is invalid. */
-static inline uint32_t audio_channel_mask_get_bits(audio_channel_mask_t channel)
-{
- return channel & ((1 << AUDIO_CHANNEL_COUNT_MAX) - 1);
-}
-
-/* The return value is undefined if the channel mask is invalid. */
-static inline audio_channel_representation_t audio_channel_mask_get_representation(
- audio_channel_mask_t channel)
-{
- // The right shift should be sufficient, but also "and" for safety in case mask is not 32 bits
- return (audio_channel_representation_t)
- ((channel >> AUDIO_CHANNEL_COUNT_MAX) & ((1 << AUDIO_CHANNEL_REPRESENTATION_LOG2) - 1));
-}
-
-/* Returns true if the channel mask is valid,
- * or returns false for AUDIO_CHANNEL_NONE, AUDIO_CHANNEL_INVALID, and other invalid values.
- * This function is unable to determine whether a channel mask for position assignment
- * is invalid because an output mask has an invalid output bit set,
- * or because an input mask has an invalid input bit set.
- * All other APIs that take a channel mask assume that it is valid.
- */
-static inline bool audio_channel_mask_is_valid(audio_channel_mask_t channel)
-{
- uint32_t bits = audio_channel_mask_get_bits(channel);
- audio_channel_representation_t representation = audio_channel_mask_get_representation(channel);
- switch (representation) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- break;
- default:
- bits = 0;
- break;
- }
- return bits != 0;
-}
-
-/* Not part of public API */
-static inline audio_channel_mask_t audio_channel_mask_from_representation_and_bits(
- audio_channel_representation_t representation, uint32_t bits)
-{
- return (audio_channel_mask_t) ((representation << AUDIO_CHANNEL_COUNT_MAX) | bits);
-}
-
-/* Expresses the convention when stereo audio samples are stored interleaved
- * in an array. This should improve readability by allowing code to use
- * symbolic indices instead of hard-coded [0] and [1].
- *
- * For multi-channel beyond stereo, the platform convention is that channels
- * are interleaved in order from least significant channel mask bit
- * to most significant channel mask bit, with unused bits skipped.
- * Any exceptions to this convention will be noted at the appropriate API.
- */
-enum {
- AUDIO_INTERLEAVE_LEFT = 0,
- AUDIO_INTERLEAVE_RIGHT = 1,
-};
-
-typedef enum {
- AUDIO_MODE_INVALID = -2,
- AUDIO_MODE_CURRENT = -1,
- AUDIO_MODE_NORMAL = 0,
- AUDIO_MODE_RINGTONE = 1,
- AUDIO_MODE_IN_CALL = 2,
- AUDIO_MODE_IN_COMMUNICATION = 3,
-
- AUDIO_MODE_CNT,
- AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1,
-} audio_mode_t;
-
-/* This enum is deprecated */
-typedef enum {
- AUDIO_IN_ACOUSTICS_NONE = 0,
- AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001,
- AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0,
- AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002,
- AUDIO_IN_ACOUSTICS_NS_DISABLE = 0,
- AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004,
- AUDIO_IN_ACOUSTICS_TX_DISABLE = 0,
-} audio_in_acoustics_t;
-
-enum {
- AUDIO_DEVICE_NONE = 0x0,
- /* reserved bits */
- AUDIO_DEVICE_BIT_IN = 0x80000000,
- AUDIO_DEVICE_BIT_DEFAULT = 0x40000000,
- /* output devices */
- AUDIO_DEVICE_OUT_EARPIECE = 0x1,
- AUDIO_DEVICE_OUT_SPEAKER = 0x2,
- AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4,
- AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8,
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10,
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
- AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400,
- AUDIO_DEVICE_OUT_HDMI = AUDIO_DEVICE_OUT_AUX_DIGITAL,
- /* uses an analog connection (multiplexed over the USB connector pins for instance) */
- AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800,
- AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000,
- /* USB accessory mode: your Android device is a USB device and the dock is a USB host */
- AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000,
- /* USB host mode: your Android device is a USB host and the dock is a USB device */
- AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000,
- AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000,
- /* Telephony voice TX path */
- AUDIO_DEVICE_OUT_TELEPHONY_TX = 0x10000,
- /* Analog jack with line impedance detected */
- AUDIO_DEVICE_OUT_LINE = 0x20000,
- /* HDMI Audio Return Channel */
- AUDIO_DEVICE_OUT_HDMI_ARC = 0x40000,
- /* S/PDIF out */
- AUDIO_DEVICE_OUT_SPDIF = 0x80000,
- /* FM transmitter out */
- AUDIO_DEVICE_OUT_FM = 0x100000,
- /* Line out for av devices */
- AUDIO_DEVICE_OUT_AUX_LINE = 0x200000,
- /* limited-output speaker device for acoustic safety */
- AUDIO_DEVICE_OUT_SPEAKER_SAFE = 0x400000,
- AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT,
- AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE |
- AUDIO_DEVICE_OUT_SPEAKER |
- AUDIO_DEVICE_OUT_WIRED_HEADSET |
- AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
- AUDIO_DEVICE_OUT_HDMI |
- AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
- AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET |
- AUDIO_DEVICE_OUT_USB_ACCESSORY |
- AUDIO_DEVICE_OUT_USB_DEVICE |
- AUDIO_DEVICE_OUT_REMOTE_SUBMIX |
- AUDIO_DEVICE_OUT_TELEPHONY_TX |
- AUDIO_DEVICE_OUT_LINE |
- AUDIO_DEVICE_OUT_HDMI_ARC |
- AUDIO_DEVICE_OUT_SPDIF |
- AUDIO_DEVICE_OUT_FM |
- AUDIO_DEVICE_OUT_AUX_LINE |
- AUDIO_DEVICE_OUT_SPEAKER_SAFE |
- AUDIO_DEVICE_OUT_DEFAULT),
- AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
- AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
- AUDIO_DEVICE_OUT_ALL_USB = (AUDIO_DEVICE_OUT_USB_ACCESSORY |
- AUDIO_DEVICE_OUT_USB_DEVICE),
-
- /* input devices */
- AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1,
- AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2,
- AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4,
- AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8,
- AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10,
- AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20,
- AUDIO_DEVICE_IN_HDMI = AUDIO_DEVICE_IN_AUX_DIGITAL,
- /* Telephony voice RX path */
- AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40,
- AUDIO_DEVICE_IN_TELEPHONY_RX = AUDIO_DEVICE_IN_VOICE_CALL,
- AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80,
- AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100,
- AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200,
- AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400,
- AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800,
- AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000,
- /* FM tuner input */
- AUDIO_DEVICE_IN_FM_TUNER = AUDIO_DEVICE_BIT_IN | 0x2000,
- /* TV tuner input */
- AUDIO_DEVICE_IN_TV_TUNER = AUDIO_DEVICE_BIT_IN | 0x4000,
- /* Analog jack with line impedance detected */
- AUDIO_DEVICE_IN_LINE = AUDIO_DEVICE_BIT_IN | 0x8000,
- /* S/PDIF in */
- AUDIO_DEVICE_IN_SPDIF = AUDIO_DEVICE_BIT_IN | 0x10000,
- AUDIO_DEVICE_IN_BLUETOOTH_A2DP = AUDIO_DEVICE_BIT_IN | 0x20000,
- AUDIO_DEVICE_IN_LOOPBACK = AUDIO_DEVICE_BIT_IN | 0x40000,
- AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT,
-
- AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION |
- AUDIO_DEVICE_IN_AMBIENT |
- AUDIO_DEVICE_IN_BUILTIN_MIC |
- AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET |
- AUDIO_DEVICE_IN_WIRED_HEADSET |
- AUDIO_DEVICE_IN_HDMI |
- AUDIO_DEVICE_IN_TELEPHONY_RX |
- AUDIO_DEVICE_IN_BACK_MIC |
- AUDIO_DEVICE_IN_REMOTE_SUBMIX |
- AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET |
- AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET |
- AUDIO_DEVICE_IN_USB_ACCESSORY |
- AUDIO_DEVICE_IN_USB_DEVICE |
- AUDIO_DEVICE_IN_FM_TUNER |
- AUDIO_DEVICE_IN_TV_TUNER |
- AUDIO_DEVICE_IN_LINE |
- AUDIO_DEVICE_IN_SPDIF |
- AUDIO_DEVICE_IN_BLUETOOTH_A2DP |
- AUDIO_DEVICE_IN_LOOPBACK |
- AUDIO_DEVICE_IN_DEFAULT),
- AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
- AUDIO_DEVICE_IN_ALL_USB = (AUDIO_DEVICE_IN_USB_ACCESSORY |
- AUDIO_DEVICE_IN_USB_DEVICE),
-};
-
-typedef uint32_t audio_devices_t;
-
-/* the audio output flags serve two purposes:
- * - when an AudioTrack is created they indicate a "wish" to be connected to an
- * output stream with attributes corresponding to the specified flags
- * - when present in an output profile descriptor listed for a particular audio
- * hardware module, they indicate that an output stream can be opened that
- * supports the attributes indicated by the flags.
- * the audio policy manager will try to match the flags in the request
- * (when getOuput() is called) to an available output stream.
- */
-typedef enum {
- AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes
- AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track
- // to one output stream: no software mixer
- AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of
- // the device. It is unique and must be
- // present. It is opened by default and
- // receives routing, audio mode and volume
- // controls related to voice calls.
- AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks",
- // defined elsewhere
- AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers
- AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10, // offload playback of compressed
- // streams to hardware codec
- AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20, // use non-blocking write
- AUDIO_OUTPUT_FLAG_HW_AV_SYNC = 0x40 // output uses a hardware A/V synchronization source
-} audio_output_flags_t;
-
-/* The audio input flags are analogous to audio output flags.
- * Currently they are used only when an AudioRecord is created,
- * to indicate a preference to be connected to an input stream with
- * attributes corresponding to the specified flags.
- */
-typedef enum {
- AUDIO_INPUT_FLAG_NONE = 0x0, // no attributes
- AUDIO_INPUT_FLAG_FAST = 0x1, // prefer an input that supports "fast tracks"
- AUDIO_INPUT_FLAG_HW_HOTWORD = 0x2, // prefer an input that captures from hw hotword source
-} audio_input_flags_t;
-
-/* Additional information about compressed streams offloaded to
- * hardware playback
- * The version and size fields must be initialized by the caller by using
- * one of the constants defined here.
- */
-typedef struct {
- uint16_t version; // version of the info structure
- uint16_t size; // total size of the structure including version and size
- uint32_t sample_rate; // sample rate in Hz
- audio_channel_mask_t channel_mask; // channel mask
- audio_format_t format; // audio format
- audio_stream_type_t stream_type; // stream type
- uint32_t bit_rate; // bit rate in bits per second
- int64_t duration_us; // duration in microseconds, -1 if unknown
- bool has_video; // true if stream is tied to a video stream
- bool is_streaming; // true if streaming, false if local playback
-} audio_offload_info_t;
-
-#define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \
- ((((maj) & 0xff) << 8) | ((min) & 0xff))
-
-#define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1)
-#define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1
-
-static const audio_offload_info_t AUDIO_INFO_INITIALIZER = {
- version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT,
- size: sizeof(audio_offload_info_t),
- sample_rate: 0,
- channel_mask: 0,
- format: AUDIO_FORMAT_DEFAULT,
- stream_type: AUDIO_STREAM_VOICE_CALL,
- bit_rate: 0,
- duration_us: 0,
- has_video: false,
- is_streaming: false
-};
-
-/* common audio stream configuration parameters
- * You should memset() the entire structure to zero before use to
- * ensure forward compatibility
- */
-struct audio_config {
- uint32_t sample_rate;
- audio_channel_mask_t channel_mask;
- audio_format_t format;
- audio_offload_info_t offload_info;
- size_t frame_count;
-};
-typedef struct audio_config audio_config_t;
-
-static const audio_config_t AUDIO_CONFIG_INITIALIZER = {
- sample_rate: 0,
- channel_mask: AUDIO_CHANNEL_NONE,
- format: AUDIO_FORMAT_DEFAULT,
- offload_info: {
- version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT,
- size: sizeof(audio_offload_info_t),
- sample_rate: 0,
- channel_mask: 0,
- format: AUDIO_FORMAT_DEFAULT,
- stream_type: AUDIO_STREAM_VOICE_CALL,
- bit_rate: 0,
- duration_us: 0,
- has_video: false,
- is_streaming: false
- },
- frame_count: 0,
-};
-
-
-/* audio hw module handle functions or structures referencing a module */
-typedef int audio_module_handle_t;
-
-/******************************
- * Volume control
- *****************************/
-
-/* If the audio hardware supports gain control on some audio paths,
- * the platform can expose them in the audio_policy.conf file. The audio HAL
- * will then implement gain control functions that will use the following data
- * structures. */
-
-/* Type of gain control exposed by an audio port */
-#define AUDIO_GAIN_MODE_JOINT 0x1 /* supports joint channel gain control */
-#define AUDIO_GAIN_MODE_CHANNELS 0x2 /* supports separate channel gain control */
-#define AUDIO_GAIN_MODE_RAMP 0x4 /* supports gain ramps */
-
-typedef uint32_t audio_gain_mode_t;
-
-
-/* An audio_gain struct is a representation of a gain stage.
- * A gain stage is always attached to an audio port. */
-struct audio_gain {
- audio_gain_mode_t mode; /* e.g. AUDIO_GAIN_MODE_JOINT */
- audio_channel_mask_t channel_mask; /* channels which gain an be controlled.
- N/A if AUDIO_GAIN_MODE_CHANNELS is not supported */
- int min_value; /* minimum gain value in millibels */
- int max_value; /* maximum gain value in millibels */
- int default_value; /* default gain value in millibels */
- unsigned int step_value; /* gain step in millibels */
- unsigned int min_ramp_ms; /* minimum ramp duration in ms */
- unsigned int max_ramp_ms; /* maximum ramp duration in ms */
-};
-
-/* The gain configuration structure is used to get or set the gain values of a
- * given port */
-struct audio_gain_config {
- int index; /* index of the corresponding audio_gain in the
- audio_port gains[] table */
- audio_gain_mode_t mode; /* mode requested for this command */
- audio_channel_mask_t channel_mask; /* channels which gain value follows.
- N/A in joint mode */
- int values[sizeof(audio_channel_mask_t) * 8]; /* gain values in millibels
- for each channel ordered from LSb to MSb in
- channel mask. The number of values is 1 in joint
- mode or popcount(channel_mask) */
- unsigned int ramp_duration_ms; /* ramp duration in ms */
-};
-
-/******************************
- * Routing control
- *****************************/
-
-/* Types defined here are used to describe an audio source or sink at internal
- * framework interfaces (audio policy, patch panel) or at the audio HAL.
- * Sink and sources are grouped in a concept of “audio port” representing an
- * audio end point at the edge of the system managed by the module exposing
- * the interface. */
-
-/* Audio port role: either source or sink */
-typedef enum {
- AUDIO_PORT_ROLE_NONE,
- AUDIO_PORT_ROLE_SOURCE,
- AUDIO_PORT_ROLE_SINK,
-} audio_port_role_t;
-
-/* Audio port type indicates if it is a session (e.g AudioTrack),
- * a mix (e.g PlaybackThread output) or a physical device
- * (e.g AUDIO_DEVICE_OUT_SPEAKER) */
-typedef enum {
- AUDIO_PORT_TYPE_NONE,
- AUDIO_PORT_TYPE_DEVICE,
- AUDIO_PORT_TYPE_MIX,
- AUDIO_PORT_TYPE_SESSION,
-} audio_port_type_t;
-
-/* Each port has a unique ID or handle allocated by policy manager */
-typedef int audio_port_handle_t;
-#define AUDIO_PORT_HANDLE_NONE 0
-
-
-/* maximum audio device address length */
-#define AUDIO_DEVICE_MAX_ADDRESS_LEN 32
-
-/* extension for audio port configuration structure when the audio port is a
- * hardware device */
-struct audio_port_config_device_ext {
- audio_module_handle_t hw_module; /* module the device is attached to */
- audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */
- char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; /* device address. "" if N/A */
-};
-
-/* extension for audio port configuration structure when the audio port is a
- * sub mix */
-struct audio_port_config_mix_ext {
- audio_module_handle_t hw_module; /* module the stream is attached to */
- audio_io_handle_t handle; /* I/O handle of the input/output stream */
- union {
- //TODO: change use case for output streams: use strategy and mixer attributes
- audio_stream_type_t stream;
- audio_source_t source;
- } usecase;
-};
-
-/* extension for audio port configuration structure when the audio port is an
- * audio session */
-struct audio_port_config_session_ext {
- audio_session_t session; /* audio session */
-};
-
-/* Flags indicating which fields are to be considered in struct audio_port_config */
-#define AUDIO_PORT_CONFIG_SAMPLE_RATE 0x1
-#define AUDIO_PORT_CONFIG_CHANNEL_MASK 0x2
-#define AUDIO_PORT_CONFIG_FORMAT 0x4
-#define AUDIO_PORT_CONFIG_GAIN 0x8
-#define AUDIO_PORT_CONFIG_ALL (AUDIO_PORT_CONFIG_SAMPLE_RATE | \
- AUDIO_PORT_CONFIG_CHANNEL_MASK | \
- AUDIO_PORT_CONFIG_FORMAT | \
- AUDIO_PORT_CONFIG_GAIN)
-
-/* audio port configuration structure used to specify a particular configuration of
- * an audio port */
-struct audio_port_config {
- audio_port_handle_t id; /* port unique ID */
- audio_port_role_t role; /* sink or source */
- audio_port_type_t type; /* device, mix ... */
- unsigned int config_mask; /* e.g AUDIO_PORT_CONFIG_ALL */
- unsigned int sample_rate; /* sampling rate in Hz */
- audio_channel_mask_t channel_mask; /* channel mask if applicable */
- audio_format_t format; /* format if applicable */
- struct audio_gain_config gain; /* gain to apply if applicable */
- union {
- struct audio_port_config_device_ext device; /* device specific info */
- struct audio_port_config_mix_ext mix; /* mix specific info */
- struct audio_port_config_session_ext session; /* session specific info */
- } ext;
-};
-
-
-/* max number of sampling rates in audio port */
-#define AUDIO_PORT_MAX_SAMPLING_RATES 16
-/* max number of channel masks in audio port */
-#define AUDIO_PORT_MAX_CHANNEL_MASKS 16
-/* max number of audio formats in audio port */
-#define AUDIO_PORT_MAX_FORMATS 16
-/* max number of gain controls in audio port */
-#define AUDIO_PORT_MAX_GAINS 16
-
-/* extension for audio port structure when the audio port is a hardware device */
-struct audio_port_device_ext {
- audio_module_handle_t hw_module; /* module the device is attached to */
- audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */
- char address[AUDIO_DEVICE_MAX_ADDRESS_LEN];
-};
-
-/* Latency class of the audio mix */
-typedef enum {
- AUDIO_LATENCY_LOW,
- AUDIO_LATENCY_NORMAL,
-} audio_mix_latency_class_t;
-
-/* extension for audio port structure when the audio port is a sub mix */
-struct audio_port_mix_ext {
- audio_module_handle_t hw_module; /* module the stream is attached to */
- audio_io_handle_t handle; /* I/O handle of the input.output stream */
- audio_mix_latency_class_t latency_class; /* latency class */
- // other attributes: routing strategies
-};
-
-/* extension for audio port structure when the audio port is an audio session */
-struct audio_port_session_ext {
- audio_session_t session; /* audio session */
-};
-
-
-struct audio_port {
- audio_port_handle_t id; /* port unique ID */
- audio_port_role_t role; /* sink or source */
- audio_port_type_t type; /* device, mix ... */
- unsigned int num_sample_rates; /* number of sampling rates in following array */
- unsigned int sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES];
- unsigned int num_channel_masks; /* number of channel masks in following array */
- audio_channel_mask_t channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS];
- unsigned int num_formats; /* number of formats in following array */
- audio_format_t formats[AUDIO_PORT_MAX_FORMATS];
- unsigned int num_gains; /* number of gains in following array */
- struct audio_gain gains[AUDIO_PORT_MAX_GAINS];
- struct audio_port_config active_config; /* current audio port configuration */
- union {
- struct audio_port_device_ext device;
- struct audio_port_mix_ext mix;
- struct audio_port_session_ext session;
- } ext;
-};
-
-/* An audio patch represents a connection between one or more source ports and
- * one or more sink ports. Patches are connected and disconnected by audio policy manager or by
- * applications via framework APIs.
- * Each patch is identified by a handle at the interface used to create that patch. For instance,
- * when a patch is created by the audio HAL, the HAL allocates and returns a handle.
- * This handle is unique to a given audio HAL hardware module.
- * But the same patch receives another system wide unique handle allocated by the framework.
- * This unique handle is used for all transactions inside the framework.
- */
-typedef int audio_patch_handle_t;
-#define AUDIO_PATCH_HANDLE_NONE 0
-
-#define AUDIO_PATCH_PORTS_MAX 16
-
-struct audio_patch {
- audio_patch_handle_t id; /* patch unique ID */
- unsigned int num_sources; /* number of sources in following array */
- struct audio_port_config sources[AUDIO_PATCH_PORTS_MAX];
- unsigned int num_sinks; /* number of sinks in following array */
- struct audio_port_config sinks[AUDIO_PATCH_PORTS_MAX];
-};
-
-
-
-/* a HW synchronization source returned by the audio HAL */
-typedef uint32_t audio_hw_sync_t;
-
-/* an invalid HW synchronization source indicating an error */
-#define AUDIO_HW_SYNC_INVALID 0
-
-static inline bool audio_is_output_device(audio_devices_t device)
-{
- if (((device & AUDIO_DEVICE_BIT_IN) == 0) &&
- (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0))
- return true;
- else
- return false;
-}
-
-static inline bool audio_is_input_device(audio_devices_t device)
-{
- if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
- device &= ~AUDIO_DEVICE_BIT_IN;
- if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0))
- return true;
- }
- return false;
-}
-
-static inline bool audio_is_output_devices(audio_devices_t device)
-{
- return (device & AUDIO_DEVICE_BIT_IN) == 0;
-}
-
-static inline bool audio_is_a2dp_in_device(audio_devices_t device)
-{
- if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
- device &= ~AUDIO_DEVICE_BIT_IN;
- if ((popcount(device) == 1) && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP))
- return true;
- }
- return false;
-}
-
-static inline bool audio_is_a2dp_out_device(audio_devices_t device)
-{
- if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP))
- return true;
- else
- return false;
-}
-
-// Deprecated - use audio_is_a2dp_out_device() instead
-static inline bool audio_is_a2dp_device(audio_devices_t device)
-{
- return audio_is_a2dp_out_device(device);
-}
-
-static inline bool audio_is_bluetooth_sco_device(audio_devices_t device)
-{
- if ((device & AUDIO_DEVICE_BIT_IN) == 0) {
- if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL_SCO) == 0))
- return true;
- } else {
- device &= ~AUDIO_DEVICE_BIT_IN;
- if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) == 0))
- return true;
- }
-
- return false;
-}
-
-static inline bool audio_is_usb_out_device(audio_devices_t device)
-{
- return ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB));
-}
-
-static inline bool audio_is_usb_in_device(audio_devices_t device)
-{
- if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
- device &= ~AUDIO_DEVICE_BIT_IN;
- if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_ALL_USB) != 0)
- return true;
- }
- return false;
-}
-
-/* OBSOLETE - use audio_is_usb_out_device() instead. */
-static inline bool audio_is_usb_device(audio_devices_t device)
-{
- return audio_is_usb_out_device(device);
-}
-
-static inline bool audio_is_remote_submix_device(audio_devices_t device)
-{
- if ((audio_is_output_devices(device) &&
- (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
- || (!audio_is_output_devices(device) &&
- (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX))
- return true;
- else
- return false;
-}
-
-/* Returns true if:
- * representation is valid, and
- * there is at least one channel bit set which _could_ correspond to an input channel, and
- * there are no channel bits set which could _not_ correspond to an input channel.
- * Otherwise returns false.
- */
-static inline bool audio_is_input_channel(audio_channel_mask_t channel)
-{
- uint32_t bits = audio_channel_mask_get_bits(channel);
- switch (audio_channel_mask_get_representation(channel)) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- if (bits & ~AUDIO_CHANNEL_IN_ALL) {
- bits = 0;
- }
- // fall through
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- return bits != 0;
- default:
- return false;
- }
-}
-
-/* Returns true if:
- * representation is valid, and
- * there is at least one channel bit set which _could_ correspond to an output channel, and
- * there are no channel bits set which could _not_ correspond to an output channel.
- * Otherwise returns false.
- */
-static inline bool audio_is_output_channel(audio_channel_mask_t channel)
-{
- uint32_t bits = audio_channel_mask_get_bits(channel);
- switch (audio_channel_mask_get_representation(channel)) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- if (bits & ~AUDIO_CHANNEL_OUT_ALL) {
- bits = 0;
- }
- // fall through
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- return bits != 0;
- default:
- return false;
- }
-}
-
-/* Returns the number of channels from an input channel mask,
- * used in the context of audio input or recording.
- * If a channel bit is set which could _not_ correspond to an input channel,
- * it is excluded from the count.
- * Returns zero if the representation is invalid.
- */
-static inline uint32_t audio_channel_count_from_in_mask(audio_channel_mask_t channel)
-{
- uint32_t bits = audio_channel_mask_get_bits(channel);
- switch (audio_channel_mask_get_representation(channel)) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- // TODO: We can now merge with from_out_mask and remove anding
- bits &= AUDIO_CHANNEL_IN_ALL;
- // fall through
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- return popcount(bits);
- default:
- return 0;
- }
-}
-
-/* Returns the number of channels from an output channel mask,
- * used in the context of audio output or playback.
- * If a channel bit is set which could _not_ correspond to an output channel,
- * it is excluded from the count.
- * Returns zero if the representation is invalid.
- */
-static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel)
-{
- uint32_t bits = audio_channel_mask_get_bits(channel);
- switch (audio_channel_mask_get_representation(channel)) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- // TODO: We can now merge with from_in_mask and remove anding
- bits &= AUDIO_CHANNEL_OUT_ALL;
- // fall through
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- return popcount(bits);
- default:
- return 0;
- }
-}
-
-/* Derive an output channel mask for position assignment from a channel count.
- * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel
- * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad,
- * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC
- * for continuity with stereo.
- * Returns the matching channel mask,
- * or AUDIO_CHANNEL_NONE if the channel count is zero,
- * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the
- * configurations for which a default output channel mask is defined.
- */
-static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count)
-{
- uint32_t bits;
- switch (channel_count) {
- case 0:
- return AUDIO_CHANNEL_NONE;
- case 1:
- bits = AUDIO_CHANNEL_OUT_MONO;
- break;
- case 2:
- bits = AUDIO_CHANNEL_OUT_STEREO;
- break;
- case 3:
- bits = AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER;
- break;
- case 4: // 4.0
- bits = AUDIO_CHANNEL_OUT_QUAD;
- break;
- case 5: // 5.0
- bits = AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER;
- break;
- case 6: // 5.1
- bits = AUDIO_CHANNEL_OUT_5POINT1;
- break;
- case 7: // 6.1
- bits = AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER;
- break;
- case 8:
- bits = AUDIO_CHANNEL_OUT_7POINT1;
- break;
- default:
- return AUDIO_CHANNEL_INVALID;
- }
- return audio_channel_mask_from_representation_and_bits(
- AUDIO_CHANNEL_REPRESENTATION_POSITION, bits);
-}
-
-/* Derive an input channel mask for position assignment from a channel count.
- * Currently handles only mono and stereo.
- * Returns the matching channel mask,
- * or AUDIO_CHANNEL_NONE if the channel count is zero,
- * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the
- * configurations for which a default input channel mask is defined.
- */
-static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count)
-{
- uint32_t bits;
- switch (channel_count) {
- case 0:
- return AUDIO_CHANNEL_NONE;
- case 1:
- bits = AUDIO_CHANNEL_IN_MONO;
- break;
- case 2:
- bits = AUDIO_CHANNEL_IN_STEREO;
- break;
- default:
- return AUDIO_CHANNEL_INVALID;
- }
- return audio_channel_mask_from_representation_and_bits(
- AUDIO_CHANNEL_REPRESENTATION_POSITION, bits);
-}
-
-/* Derive a channel mask for index assignment from a channel count.
- * Returns the matching channel mask,
- * or AUDIO_CHANNEL_NONE if the channel count is zero,
- * or AUDIO_CHANNEL_INVALID if the channel count exceeds AUDIO_CHANNEL_COUNT_MAX.
- */
-static inline audio_channel_mask_t audio_channel_mask_for_index_assignment_from_count(
- uint32_t channel_count)
-{
- if (channel_count == 0) {
- return AUDIO_CHANNEL_NONE;
- }
- if (channel_count > AUDIO_CHANNEL_COUNT_MAX) {
- return AUDIO_CHANNEL_INVALID;
- }
- uint32_t bits = (1 << channel_count) - 1;
- return audio_channel_mask_from_representation_and_bits(
- AUDIO_CHANNEL_REPRESENTATION_INDEX, bits);
-}
-
-static inline bool audio_is_valid_format(audio_format_t format)
-{
- switch (format & AUDIO_FORMAT_MAIN_MASK) {
- case AUDIO_FORMAT_PCM:
- switch (format) {
- case AUDIO_FORMAT_PCM_16_BIT:
- case AUDIO_FORMAT_PCM_8_BIT:
- case AUDIO_FORMAT_PCM_32_BIT:
- case AUDIO_FORMAT_PCM_8_24_BIT:
- case AUDIO_FORMAT_PCM_FLOAT:
- case AUDIO_FORMAT_PCM_24_BIT_PACKED:
- return true;
- default:
- return false;
- }
- /* not reached */
- case AUDIO_FORMAT_MP3:
- case AUDIO_FORMAT_AMR_NB:
- case AUDIO_FORMAT_AMR_WB:
- case AUDIO_FORMAT_AAC:
- case AUDIO_FORMAT_HE_AAC_V1:
- case AUDIO_FORMAT_HE_AAC_V2:
- case AUDIO_FORMAT_VORBIS:
- case AUDIO_FORMAT_OPUS:
- case AUDIO_FORMAT_AC3:
- case AUDIO_FORMAT_E_AC3:
- return true;
- default:
- return false;
- }
-}
-
-static inline bool audio_is_linear_pcm(audio_format_t format)
-{
- return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM);
-}
-
-static inline size_t audio_bytes_per_sample(audio_format_t format)
-{
- size_t size = 0;
-
- switch (format) {
- case AUDIO_FORMAT_PCM_32_BIT:
- case AUDIO_FORMAT_PCM_8_24_BIT:
- size = sizeof(int32_t);
- break;
- case AUDIO_FORMAT_PCM_24_BIT_PACKED:
- size = sizeof(uint8_t) * 3;
- break;
- case AUDIO_FORMAT_PCM_16_BIT:
- size = sizeof(int16_t);
- break;
- case AUDIO_FORMAT_PCM_8_BIT:
- size = sizeof(uint8_t);
- break;
- case AUDIO_FORMAT_PCM_FLOAT:
- size = sizeof(float);
- break;
- default:
- break;
- }
- return size;
-}
-
-/* converts device address to string sent to audio HAL via set_parameters */
-static inline char *audio_device_address_to_parameter(audio_devices_t device, const char *address)
-{
- const size_t kSize = AUDIO_DEVICE_MAX_ADDRESS_LEN + sizeof("a2dp_sink_address=");
- char param[kSize];
-
- if (device & AUDIO_DEVICE_OUT_ALL_A2DP)
- snprintf(param, kSize, "%s=%s", "a2dp_sink_address", address);
- else if (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
- snprintf(param, kSize, "%s=%s", "mix", address);
- else
- snprintf(param, kSize, "%s", address);
-
- return strdup(param);
-}
-
-
-__END_DECLS
-
-#endif // ANDROID_AUDIO_CORE_H
diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h
deleted file mode 100644
index 2881104..0000000
--- a/include/system/audio_policy.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_AUDIO_POLICY_CORE_H
-#define ANDROID_AUDIO_POLICY_CORE_H
-
-#include <stdint.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-#include <cutils/bitops.h>
-
-__BEGIN_DECLS
-
-/* The enums were moved here mostly from
- * frameworks/base/include/media/AudioSystem.h
- */
-
-/* device categories used for audio_policy->set_force_use() */
-typedef enum {
- AUDIO_POLICY_FORCE_NONE,
- AUDIO_POLICY_FORCE_SPEAKER,
- AUDIO_POLICY_FORCE_HEADPHONES,
- AUDIO_POLICY_FORCE_BT_SCO,
- AUDIO_POLICY_FORCE_BT_A2DP,
- AUDIO_POLICY_FORCE_WIRED_ACCESSORY,
- AUDIO_POLICY_FORCE_BT_CAR_DOCK,
- AUDIO_POLICY_FORCE_BT_DESK_DOCK,
- AUDIO_POLICY_FORCE_ANALOG_DOCK,
- AUDIO_POLICY_FORCE_DIGITAL_DOCK,
- AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */
- AUDIO_POLICY_FORCE_SYSTEM_ENFORCED,
- AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED,
-
- AUDIO_POLICY_FORCE_CFG_CNT,
- AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1,
-
- AUDIO_POLICY_FORCE_DEFAULT = AUDIO_POLICY_FORCE_NONE,
-} audio_policy_forced_cfg_t;
-
-/* usages used for audio_policy->set_force_use() */
-typedef enum {
- AUDIO_POLICY_FORCE_FOR_COMMUNICATION,
- AUDIO_POLICY_FORCE_FOR_MEDIA,
- AUDIO_POLICY_FORCE_FOR_RECORD,
- AUDIO_POLICY_FORCE_FOR_DOCK,
- AUDIO_POLICY_FORCE_FOR_SYSTEM,
- AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO,
-
- AUDIO_POLICY_FORCE_USE_CNT,
- AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1,
-} audio_policy_force_use_t;
-
-/* device connection states used for audio_policy->set_device_connection_state()
- */
-typedef enum {
- AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
- AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
-
- AUDIO_POLICY_DEVICE_STATE_CNT,
- AUDIO_POLICY_DEVICE_STATE_MAX = AUDIO_POLICY_DEVICE_STATE_CNT - 1,
-} audio_policy_dev_state_t;
-
-typedef enum {
- /* Used to generate a tone to notify the user of a
- * notification/alarm/ringtone while they are in a call. */
- AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION = 0,
-
- AUDIO_POLICY_TONE_CNT,
- AUDIO_POLICY_TONE_MAX = AUDIO_POLICY_TONE_CNT - 1,
-} audio_policy_tone_t;
-
-
-static inline bool audio_is_low_visibility(audio_stream_type_t stream)
-{
- switch (stream) {
- case AUDIO_STREAM_SYSTEM:
- case AUDIO_STREAM_NOTIFICATION:
- case AUDIO_STREAM_RING:
- return true;
- default:
- return false;
- }
-}
-
-
-__END_DECLS
-
-#endif // ANDROID_AUDIO_POLICY_CORE_H
diff --git a/include/system/camera.h b/include/system/camera.h
index 7a4dd53..5d0873a 100644
--- a/include/system/camera.h
+++ b/include/system/camera.h
@@ -174,6 +174,22 @@
* count is non-positive or too big to be realized.
*/
CAMERA_CMD_SET_VIDEO_BUFFER_COUNT = 10,
+
+ /**
+ * Configure an explicit format to use for video recording metadata mode.
+ * This can be used to switch the format from the
+ * default IMPLEMENTATION_DEFINED gralloc format to some other
+ * device-supported format, and the default dataspace from the BT_709 color
+ * space to some other device-supported dataspace. arg1 is the HAL pixel
+ * format, and arg2 is the HAL dataSpace. This command returns
+ * INVALID_OPERATION error if it is sent after video recording is started,
+ * or the command is not supported at all.
+ *
+ * If the gralloc format is set to a format other than
+ * IMPLEMENTATION_DEFINED, then HALv3 devices will use gralloc usage flags
+ * of SW_READ_OFTEN.
+ */
+ CAMERA_CMD_SET_VIDEO_FORMAT = 11
};
/** camera fatal errors */
@@ -194,7 +210,12 @@
/** The facing of the camera is opposite to that of the screen. */
CAMERA_FACING_BACK = 0,
/** The facing of the camera is the same as that of the screen. */
- CAMERA_FACING_FRONT = 1
+ CAMERA_FACING_FRONT = 1,
+ /**
+ * The facing of the camera is not fixed relative to the screen.
+ * The cameras with this facing are external cameras, e.g. USB cameras.
+ */
+ CAMERA_FACING_EXTERNAL = 2
};
enum {
diff --git a/include/system/graphics.h b/include/system/graphics.h
index efd48cb..ae10fa0 100644
--- a/include/system/graphics.h
+++ b/include/system/graphics.h
@@ -17,6 +17,7 @@
#ifndef SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H
#define SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H
+#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
@@ -41,7 +42,7 @@
* pixel format definitions
*/
-enum {
+typedef enum android_pixel_format {
/*
* "linear" color pixel formats:
*
@@ -58,11 +59,6 @@
HAL_PIXEL_FORMAT_RGB_565 = 4,
HAL_PIXEL_FORMAT_BGRA_8888 = 5,
- // Deprecated sRGB formats for source code compatibility
- // Not for use in new code
- HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC,
- HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD,
-
/*
* 0x100 - 0x1FF
*
@@ -152,7 +148,8 @@
* When used with ANativeWindow, the dataSpace field describes the color
* space of the buffer, except that dataSpace field
* HAL_DATASPACE_DEPTH indicates that this buffer contains a depth
- * image where each sample is a distance value measured by a depth camera.
+ * image where each sample is a distance value measured by a depth camera,
+ * plus an associated confidence value.
*/
HAL_PIXEL_FORMAT_Y16 = 0x20363159,
@@ -194,9 +191,6 @@
*/
HAL_PIXEL_FORMAT_RAW16 = 0x20,
- // Temporary alias for source code compatibility; do not use in new code
- HAL_PIXEL_FORMAT_RAW_SENSOR = HAL_PIXEL_FORMAT_RAW16,
-
/*
* Android RAW10 format:
*
@@ -252,6 +246,56 @@
HAL_PIXEL_FORMAT_RAW10 = 0x25,
/*
+ * Android RAW12 format:
+ *
+ * This format is exposed outside of camera HAL to applications.
+ *
+ * RAW12 is a single-channel, 12-bit per pixel, densely packed in each row,
+ * unprocessed format, usually representing raw Bayer-pattern images coming from
+ * an image sensor.
+ *
+ * In an image buffer with this format, starting from the first pixel of each
+ * row, each two consecutive pixels are packed into 3 bytes (24 bits). The first
+ * and second byte contains the top 8 bits of first and second pixel. The third
+ * byte contains the 4 least significant bits of the two pixels, the exact layout
+ * data for each two consecutive pixels is illustrated below (Pi[j] stands for
+ * the jth bit of the ith pixel):
+ *
+ * bit 7 bit 0
+ * ======|======|======|======|======|======|======|======|
+ * Byte 0: |P0[11]|P0[10]|P0[ 9]|P0[ 8]|P0[ 7]|P0[ 6]|P0[ 5]|P0[ 4]|
+ * |------|------|------|------|------|------|------|------|
+ * Byte 1: |P1[11]|P1[10]|P1[ 9]|P1[ 8]|P1[ 7]|P1[ 6]|P1[ 5]|P1[ 4]|
+ * |------|------|------|------|------|------|------|------|
+ * Byte 2: |P1[ 3]|P1[ 2]|P1[ 1]|P1[ 0]|P0[ 3]|P0[ 2]|P0[ 1]|P0[ 0]|
+ * =======================================================
+ *
+ * This format assumes:
+ * - a width multiple of 4 pixels
+ * - an even height
+ * - a vertical stride equal to the height
+ * - strides are specified in bytes, not in pixels
+ *
+ * size = stride * height
+ *
+ * When stride is equal to width * (12 / 8), there will be no padding bytes at
+ * the end of each row, the entire image data is densely packed. When stride is
+ * larger than width * (12 / 8), padding bytes will be present at the end of
+ * each row (including the last row).
+ *
+ * This format must be accepted by the gralloc module when used with the
+ * following usage flags:
+ * - GRALLOC_USAGE_HW_CAMERA_*
+ * - GRALLOC_USAGE_SW_*
+ * - GRALLOC_USAGE_RENDERSCRIPT
+ *
+ * When used with ANativeWindow, the dataSpace field should be
+ * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+ * extra metadata to define.
+ */
+ HAL_PIXEL_FORMAT_RAW12 = 0x26,
+
+ /*
* Android opaque RAW format:
*
* This format is exposed outside of the camera HAL to applications.
@@ -316,18 +360,18 @@
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
/*
- * Android flexible YCbCr formats
+ * Android flexible YCbCr 4:2:0 formats
*
- * This format allows platforms to use an efficient YCbCr/YCrCb buffer
- * layout, while still describing the buffer layout in a way accessible to
- * the CPU in a device-independent manner. While called YCbCr, it can be
+ * This format allows platforms to use an efficient YCbCr/YCrCb 4:2:0
+ * buffer layout, while still describing the general format in a
+ * layout-independent manner. While called YCbCr, it can be
* used to describe formats with either chromatic ordering, as well as
* whole planar or semiplanar layouts.
*
* struct android_ycbcr (below) is the the struct used to describe it.
*
* This format must be accepted by the gralloc module when
- * USAGE_HW_CAMERA_WRITE and USAGE_SW_READ_* are set.
+ * USAGE_SW_WRITE_* or USAGE_SW_READ_* are set.
*
* This format is locked for use by gralloc's (*lock_ycbcr) method, and
* locking with the (*lock) method will return an error.
@@ -337,11 +381,67 @@
*/
HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23,
+ /*
+ * Android flexible YCbCr 4:2:2 formats
+ *
+ * This format allows platforms to use an efficient YCbCr/YCrCb 4:2:2
+ * buffer layout, while still describing the general format in a
+ * layout-independent manner. While called YCbCr, it can be
+ * used to describe formats with either chromatic ordering, as well as
+ * whole planar or semiplanar layouts.
+ *
+ * This format is currently only used by SW readable buffers
+ * produced by MediaCodecs, so the gralloc module can ignore this format.
+ */
+ HAL_PIXEL_FORMAT_YCbCr_422_888 = 0x27,
+
+ /*
+ * Android flexible YCbCr 4:4:4 formats
+ *
+ * This format allows platforms to use an efficient YCbCr/YCrCb 4:4:4
+ * buffer layout, while still describing the general format in a
+ * layout-independent manner. While called YCbCr, it can be
+ * used to describe formats with either chromatic ordering, as well as
+ * whole planar or semiplanar layouts.
+ *
+ * This format is currently only used by SW readable buffers
+ * produced by MediaCodecs, so the gralloc module can ignore this format.
+ */
+ HAL_PIXEL_FORMAT_YCbCr_444_888 = 0x28,
+
+ /*
+ * Android flexible RGB 888 formats
+ *
+ * This format allows platforms to use an efficient RGB/BGR/RGBX/BGRX
+ * buffer layout, while still describing the general format in a
+ * layout-independent manner. While called RGB, it can be
+ * used to describe formats with either color ordering and optional
+ * padding, as well as whole planar layout.
+ *
+ * This format is currently only used by SW readable buffers
+ * produced by MediaCodecs, so the gralloc module can ignore this format.
+ */
+ HAL_PIXEL_FORMAT_FLEX_RGB_888 = 0x29,
+
+ /*
+ * Android flexible RGBA 8888 formats
+ *
+ * This format allows platforms to use an efficient RGBA/BGRA/ARGB/ABGR
+ * buffer layout, while still describing the general format in a
+ * layout-independent manner. While called RGBA, it can be
+ * used to describe formats with any of the component orderings, as
+ * well as whole planar layout.
+ *
+ * This format is currently only used by SW readable buffers
+ * produced by MediaCodecs, so the gralloc module can ignore this format.
+ */
+ HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 0x2A,
+
/* Legacy formats (deprecated), used by ImageFormat.java */
HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16
HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21
HAL_PIXEL_FORMAT_YCbCr_422_I = 0x14, // YUY2
-};
+} android_pixel_format_t;
/*
* Structure for describing YCbCr formats for consumption by applications.
@@ -352,15 +452,15 @@
*
* Buffers must have a 8 bit depth.
*
- * @y, @cb, and @cr point to the first byte of their respective planes.
+ * y, cb, and cr point to the first byte of their respective planes.
*
* Stride describes the distance in bytes from the first value of one row of
* the image to the first value of the next row. It includes the width of the
* image plus padding.
- * @ystride is the stride of the luma plane.
- * @cstride is the stride of the chroma planes.
+ * ystride is the stride of the luma plane.
+ * cstride is the stride of the chroma planes.
*
- * @chroma_step is the distance in bytes from one chroma pixel value to the
+ * chroma_step is the distance in bytes from one chroma pixel value to the
* next. This is 2 bytes for semiplanar (because chroma values are interleaved
* and each chroma value is one byte) and 1 for planar.
*/
@@ -377,31 +477,133 @@
uint32_t reserved[8];
};
+/*
+ * Structures for describing flexible YUVA/RGBA formats for consumption by
+ * applications. Such flexible formats contain a plane for each component (e.g.
+ * red, green, blue), where each plane is laid out in a grid-like pattern
+ * occupying unique byte addresses and with consistent byte offsets between
+ * neighboring pixels.
+ *
+ * The android_flex_layout structure is used with any pixel format that can be
+ * represented by it, such as:
+ * - HAL_PIXEL_FORMAT_YCbCr_*_888
+ * - HAL_PIXEL_FORMAT_FLEX_RGB*_888
+ * - HAL_PIXEL_FORMAT_RGB[AX]_888[8],BGRA_8888,RGB_888
+ * - HAL_PIXEL_FORMAT_YV12,Y8,Y16,YCbCr_422_SP/I,YCrCb_420_SP
+ * - even implementation defined formats that can be represented by
+ * the structures
+ *
+ * Vertical increment (aka. row increment or stride) describes the distance in
+ * bytes from the first pixel of one row to the first pixel of the next row
+ * (below) for the component plane. This can be negative.
+ *
+ * Horizontal increment (aka. column or pixel increment) describes the distance
+ * in bytes from one pixel to the next pixel (to the right) on the same row for
+ * the component plane. This can be negative.
+ *
+ * Each plane can be subsampled either vertically or horizontally by
+ * a power-of-two factor.
+ *
+ * The bit-depth of each component can be arbitrary, as long as the pixels are
+ * laid out on whole bytes, in native byte-order, using the most significant
+ * bits of each unit.
+ */
+
+typedef enum android_flex_component {
+ /* luma */
+ FLEX_COMPONENT_Y = 1 << 0,
+ /* chroma blue */
+ FLEX_COMPONENT_Cb = 1 << 1,
+ /* chroma red */
+ FLEX_COMPONENT_Cr = 1 << 2,
+
+ /* red */
+ FLEX_COMPONENT_R = 1 << 10,
+ /* green */
+ FLEX_COMPONENT_G = 1 << 11,
+ /* blue */
+ FLEX_COMPONENT_B = 1 << 12,
+
+ /* alpha */
+ FLEX_COMPONENT_A = 1 << 30,
+} android_flex_component_t;
+
+typedef struct android_flex_plane {
+ /* pointer to the first byte of the top-left pixel of the plane. */
+ uint8_t *top_left;
+
+ android_flex_component_t component;
+
+ /* bits allocated for the component in each pixel. Must be a positive
+ multiple of 8. */
+ int32_t bits_per_component;
+ /* number of the most significant bits used in the format for this
+ component. Must be between 1 and bits_per_component, inclusive. */
+ int32_t bits_used;
+
+ /* horizontal increment */
+ int32_t h_increment;
+ /* vertical increment */
+ int32_t v_increment;
+ /* horizontal subsampling. Must be a positive power of 2. */
+ int32_t h_subsampling;
+ /* vertical subsampling. Must be a positive power of 2. */
+ int32_t v_subsampling;
+} android_flex_plane_t;
+
+typedef enum android_flex_format {
+ /* not a flexible format */
+ FLEX_FORMAT_INVALID = 0x0,
+ FLEX_FORMAT_Y = FLEX_COMPONENT_Y,
+ FLEX_FORMAT_YCbCr = FLEX_COMPONENT_Y | FLEX_COMPONENT_Cb | FLEX_COMPONENT_Cr,
+ FLEX_FORMAT_YCbCrA = FLEX_FORMAT_YCbCr | FLEX_COMPONENT_A,
+ FLEX_FORMAT_RGB = FLEX_COMPONENT_R | FLEX_COMPONENT_G | FLEX_COMPONENT_B,
+ FLEX_FORMAT_RGBA = FLEX_FORMAT_RGB | FLEX_COMPONENT_A,
+} android_flex_format_t;
+
+typedef struct android_flex_layout {
+ /* the kind of flexible format */
+ android_flex_format_t format;
+
+ /* number of planes; 0 for FLEX_FORMAT_INVALID */
+ uint32_t num_planes;
+ /* a plane for each component; ordered in increasing component value order.
+ E.g. FLEX_FORMAT_RGBA maps 0 -> R, 1 -> G, etc.
+ Can be NULL for FLEX_FORMAT_INVALID */
+ android_flex_plane_t *planes;
+} android_flex_layout_t;
+
/**
* Structure used to define depth point clouds for format HAL_PIXEL_FORMAT_BLOB
* with dataSpace value of HAL_DATASPACE_DEPTH.
* When locking a native buffer of the above format and dataSpace value,
* the vaddr pointer can be cast to this structure.
*
- * A variable-length list of (x,y,z) 3D points, as floats.
+ * A variable-length list of (x,y,z, confidence) 3D points, as floats. (x, y,
+ * z) represents a measured point's position, with the coordinate system defined
+ * by the data source. Confidence represents the estimated likelihood that this
+ * measurement is correct. It is between 0.f and 1.f, inclusive, with 1.f ==
+ * 100% confidence.
*
- * @num_points is the number of points in the list
+ * num_points is the number of points in the list
*
- * @xyz_points is the flexible array of floating-point values.
- * It contains (num_points) * 3 floats.
+ * xyz_points is the flexible array of floating-point values.
+ * It contains (num_points) * 4 floats.
*
* For example:
* android_depth_points d = get_depth_buffer();
* struct {
- * float x; float y; float z;
+ * float x; float y; float z; float confidence;
* } firstPoint, lastPoint;
*
- * firstPoint.x = d.xyz_points[0];
- * firstPoint.y = d.xyz_points[1];
- * firstPoint.z = d.xyz_points[2];
- * lastPoint.x = d.xyz_points[(d.num_points - 1) * 3 + 0];
- * lastPoint.y = d.xyz_points[(d.num_points - 1) * 3 + 1];
- * lastPoint.z = d.xyz_points[(d.num_points - 1) * 3 + 2];
+ * firstPoint.x = d.xyzc_points[0];
+ * firstPoint.y = d.xyzc_points[1];
+ * firstPoint.z = d.xyzc_points[2];
+ * firstPoint.confidence = d.xyzc_points[3];
+ * lastPoint.x = d.xyzc_points[(d.num_points - 1) * 4 + 0];
+ * lastPoint.y = d.xyzc_points[(d.num_points - 1) * 4 + 1];
+ * lastPoint.z = d.xyzc_points[(d.num_points - 1) * 4 + 2];
+ * lastPoint.confidence = d.xyzc_points[(d.num_points - 1) * 4 + 3];
*/
struct android_depth_points {
@@ -410,7 +612,14 @@
/** reserved for future use, set to 0 by gralloc's (*lock)() */
uint32_t reserved[8];
- float xyz_points[];
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc99-extensions"
+#endif
+ float xyzc_points[];
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
};
/**
@@ -421,7 +630,7 @@
*
*/
-enum {
+typedef enum android_transform {
/* flip source image horizontally (around the vertical axis) */
HAL_TRANSFORM_FLIP_H = 0x01,
/* flip source image vertically (around the horizontal axis)*/
@@ -434,7 +643,7 @@
HAL_TRANSFORM_ROT_270 = 0x07,
/* don't use. see system/window.h */
HAL_TRANSFORM_RESERVED = 0x08,
-};
+} android_transform_t;
/**
* Dataspace Definitions
@@ -447,6 +656,37 @@
* which describes both gamma curve and numeric range (within the bit depth).
*
* Other dataspaces include depth measurement data from a depth camera.
+ *
+ * A dataspace is comprised of a number of fields.
+ *
+ * Version
+ * --------
+ * The top 2 bits represent the revision of the field specification. This is
+ * currently always 0.
+ *
+ *
+ * bits 31-30 29 - 0
+ * +-----+----------------------------------------------------+
+ * fields | Rev | Revision specific fields |
+ * +-----+----------------------------------------------------+
+ *
+ * Field layout for version = 0:
+ * ----------------------------
+ *
+ * A dataspace is comprised of the following fields:
+ * Standard
+ * Transfer function
+ * Range
+ *
+ * bits 31-30 29-27 26 - 22 21 - 16 15 - 0
+ * +-----+-----+--------+--------+----------------------------+
+ * fields | 0 |Range|Transfer|Standard| Legacy and custom |
+ * +-----+-----+--------+--------+----------------------------+
+ * VV RRR TTTTT SSSSSS LLLLLLLL LLLLLLLL
+ *
+ * If range, transfer and standard fields are all 0 (e.g. top 16 bits are
+ * all zeroes), the bottom 16 bits contain either a legacy dataspace value,
+ * or a custom value.
*/
typedef enum android_dataspace {
@@ -475,14 +715,309 @@
HAL_DATASPACE_ARBITRARY = 0x1,
/*
- * RGB Colorspaces
- * -----------------
+ * Color-description aspects
*
- * Primaries are given using (x,y) coordinates in the CIE 1931 definition
- * of x and y specified by ISO 11664-1.
+ * The following aspects define various characteristics of the color
+ * specification. These represent bitfields, so that a data space value
+ * can specify each of them independently.
+ */
+
+ HAL_DATASPACE_STANDARD_SHIFT = 16,
+
+ /*
+ * Standard aspect
+ *
+ * Defines the chromaticity coordinates of the source primaries in terms of
+ * the CIE 1931 definition of x and y specified in ISO 11664-1.
+ */
+ HAL_DATASPACE_STANDARD_MASK = 63 << HAL_DATASPACE_STANDARD_SHIFT, // 0x3F
+
+ /*
+ * Chromacity coordinates are unknown or are determined by the application.
+ * Implementations shall use the following suggested standards:
+ *
+ * All YCbCr formats: BT709 if size is 720p or larger (since most video
+ * content is letterboxed this corresponds to width is
+ * 1280 or greater, or height is 720 or greater).
+ * BT601_625 if size is smaller than 720p or is JPEG.
+ * All RGB formats: BT709.
+ *
+ * For all other formats standard is undefined, and implementations should use
+ * an appropriate standard for the data represented.
+ */
+ HAL_DATASPACE_STANDARD_UNSPECIFIED = 0 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.300 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_BT709 = 1 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ */
+ HAL_DATASPACE_STANDARD_BT601_625 = 2 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ */
+ HAL_DATASPACE_STANDARD_BT601_525 = 4 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
+ * for RGB conversion (as in SMPTE 240M).
+ */
+ HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_BT2020 = 6 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+ * for RGB conversion using the linear domain.
+ */
+ HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.21 0.71
+ * blue 0.14 0.08
+ * red 0.67 0.33
+ * white (C) 0.310 0.316
+ *
+ * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_BT470M = 8 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.243 0.692
+ * blue 0.145 0.049
+ * red 0.681 0.319
+ * white (C) 0.310 0.316
+ *
+ * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_FILM = 9 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ HAL_DATASPACE_TRANSFER_SHIFT = 22,
+
+ /*
+ * Transfer aspect
*
* Transfer characteristics are the opto-electronic transfer characteristic
* at the source as a function of linear optical intensity (luminance).
+ *
+ * For digital signals, E corresponds to the recorded value. Normally, the
+ * transfer function is applied in RGB space to each of the R, G and B
+ * components independently. This may result in color shift that can be
+ * minized by applying the transfer function in Lab space only for the L
+ * component. Implementation may apply the transfer function in RGB space
+ * for all pixel formats if desired.
+ */
+
+ HAL_DATASPACE_TRANSFER_MASK = 31 << HAL_DATASPACE_TRANSFER_SHIFT, // 0x1F
+
+ /*
+ * Transfer characteristics are unknown or are determined by the
+ * application.
+ *
+ * Implementations should use the following transfer functions:
+ *
+ * For YCbCr formats: use HAL_DATASPACE_TRANSFER_SMPTE_170M
+ * For RGB formats: use HAL_DATASPACE_TRANSFER_SRGB
+ *
+ * For all other formats transfer function is undefined, and implementations
+ * should use an appropriate standard for the data represented.
+ */
+ HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * Transfer characteristic curve:
+ * E = L
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_LINEAR = 1 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * Transfer characteristic curve:
+ *
+ * E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1
+ * = 12.92 * L for 0 <= L < 0.0031308
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_SRGB = 2 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * BT.601 525, BT.601 625, BT.709, BT.2020
+ *
+ * Transfer characteristic curve:
+ * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1
+ * = 4.500 * L for 0 <= L < 0.018
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_SMPTE_170M = 3 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * Assumed display gamma 2.2.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.2)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_GAMMA2_2 = 4 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * display gamma 2.8.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.8)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_GAMMA2_8 = 5 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * SMPTE ST 2084
+ *
+ * Transfer characteristic curve:
+ * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
+ * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
+ * c2 = 32 * 2413 / 4096 = 18.8515625
+ * c3 = 32 * 2392 / 4096 = 18.6875
+ * m = 128 * 2523 / 4096 = 78.84375
+ * n = 0.25 * 2610 / 4096 = 0.1593017578125
+ * L - luminance of image 0 <= L <= 1 for HDR colorimetry.
+ * L = 1 corresponds to 10000 cd/m2
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_ST2084 = 6 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * ARIB STD-B67 Hybrid Log Gamma
+ *
+ * Transfer characteristic curve:
+ * E = r * L^0.5 for 0 <= L <= 1
+ * = a * ln(L - b) + c for 1 < L
+ * a = 0.17883277
+ * b = 0.28466892
+ * c = 0.55991073
+ * r = 0.5
+ * L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
+ * to reference white level of 100 cd/m2
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_HLG = 7 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ HAL_DATASPACE_RANGE_SHIFT = 27,
+
+ /*
+ * Range aspect
+ *
+ * Defines the range of values corresponding to the unit range of 0-1.
+ * This is defined for YCbCr only, but can be expanded to RGB space.
+ */
+ HAL_DATASPACE_RANGE_MASK = 7 << HAL_DATASPACE_RANGE_SHIFT, // 0x7
+
+ /*
+ * Range is unknown or are determined by the application. Implementations
+ * shall use the following suggested ranges:
+ *
+ * All YCbCr formats: limited range.
+ * All RGB or RGBA formats (including RAW and Bayer): full range.
+ * All Y formats: full range
+ *
+ * For all other formats range is undefined, and implementations should use
+ * an appropriate range for the data represented.
+ */
+ HAL_DATASPACE_RANGE_UNSPECIFIED = 0 << HAL_DATASPACE_RANGE_SHIFT,
+
+ /*
+ * Full range uses all values for Y, Cb and Cr from
+ * 0 to 2^b-1, where b is the bit depth of the color format.
+ */
+ HAL_DATASPACE_RANGE_FULL = 1 << HAL_DATASPACE_RANGE_SHIFT,
+
+ /*
+ * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
+ * 1/16*2^b to 15/16*2^b for Cb, Cr, R, G and B, where b is the bit depth of
+ * the color format.
+ *
+ * E.g. For 8-bit-depth formats:
+ * Luma (Y) samples should range from 16 to 235, inclusive
+ * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
+ *
+ * For 10-bit-depth formats:
+ * Luma (Y) samples should range from 64 to 940, inclusive
+ * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
+ */
+ HAL_DATASPACE_RANGE_LIMITED = 2 << HAL_DATASPACE_RANGE_SHIFT,
+
+ /*
+ * Legacy dataspaces
*/
/*
@@ -495,34 +1030,30 @@
* The values are encoded using the full range ([0,255] for 8-bit) for all
* components.
*/
- HAL_DATASPACE_SRGB_LINEAR = 0x200,
+ HAL_DATASPACE_SRGB_LINEAR = 0x200, // deprecated, use HAL_DATASPACE_V0_SRGB_LINEAR
+
+ HAL_DATASPACE_V0_SRGB_LINEAR = HAL_DATASPACE_STANDARD_BT709 |
+ HAL_DATASPACE_TRANSFER_LINEAR | HAL_DATASPACE_RANGE_FULL,
+
/*
* sRGB gamma encoding:
*
* The red, green and blue components are stored in sRGB space, and
- * converted to linear space when read, using the standard sRGB to linear
- * equation:
- *
- * Clinear = Csrgb / 12.92 for Csrgb <= 0.04045
- * = (Csrgb + 0.055 / 1.055)^2.4 for Csrgb > 0.04045
- *
- * When written the inverse transformation is performed:
- *
- * Csrgb = 12.92 * Clinear for Clinear <= 0.0031308
- * = 1.055 * Clinear^(1/2.4) - 0.055 for Clinear > 0.0031308
- *
+ * converted to linear space when read, using the SRGB transfer function
+ * for each of the R, G and B components. When written, the inverse
+ * transformation is performed.
*
* The alpha component, if present, is always stored in linear space and
* is left unmodified when read or written.
*
- * The RGB primaries and the white point are the same as BT.709.
- *
- * The values are encoded using the full range ([0,255] for 8-bit) for all
- * components.
- *
+ * Use full range and BT.709 standard.
*/
- HAL_DATASPACE_SRGB = 0x201,
+ HAL_DATASPACE_SRGB = 0x201, // deprecated, use HAL_DATASPACE_V0_SRGB
+
+ HAL_DATASPACE_V0_SRGB = HAL_DATASPACE_STANDARD_BT709 |
+ HAL_DATASPACE_TRANSFER_SRGB | HAL_DATASPACE_RANGE_FULL,
+
/*
* YCbCr Colorspaces
@@ -540,101 +1071,69 @@
*
* Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
*
- * Transfer characteristic curve:
- * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
- * E = 4.500 L, 0.018 > L >= 0
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- *
- * Primaries: x y
- * green 0.290 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * Use full range, BT.601 transfer and BT.601_625 standard.
*/
- HAL_DATASPACE_JFIF = 0x101,
+ HAL_DATASPACE_JFIF = 0x101, // deprecated, use HAL_DATASPACE_V0_JFIF
+
+ HAL_DATASPACE_V0_JFIF = HAL_DATASPACE_STANDARD_BT601_625 |
+ HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_FULL,
/*
* ITU-R Recommendation 601 (BT.601) - 625-line
*
* Standard-definition television, 625 Lines (PAL)
*
- * For 8-bit-depth formats:
- * Luma (Y) samples should range from 16 to 235, inclusive
- * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
- *
- * For 10-bit-depth formats:
- * Luma (Y) samples should range from 64 to 940, inclusive
- * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
- *
- * Transfer characteristic curve:
- * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
- * E = 4.500 L, 0.018 > L >= 0
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- *
- * Primaries: x y
- * green 0.290 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * Use limited range, BT.601 transfer and BT.601_625 standard.
*/
- HAL_DATASPACE_BT601_625 = 0x102,
+ HAL_DATASPACE_BT601_625 = 0x102, // deprecated, use HAL_DATASPACE_V0_BT601_625
+
+ HAL_DATASPACE_V0_BT601_625 = HAL_DATASPACE_STANDARD_BT601_625 |
+ HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
+
/*
* ITU-R Recommendation 601 (BT.601) - 525-line
*
* Standard-definition television, 525 Lines (NTSC)
*
- * For 8-bit-depth formats:
- * Luma (Y) samples should range from 16 to 235, inclusive
- * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
- *
- * For 10-bit-depth formats:
- * Luma (Y) samples should range from 64 to 940, inclusive
- * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
- *
- * Transfer characteristic curve:
- * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
- * E = 4.500 L, 0.018 > L >= 0
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- *
- * Primaries: x y
- * green 0.310 0.595
- * blue 0.155 0.070
- * red 0.630 0.340
- * white (D65) 0.3127 0.3290
+ * Use limited range, BT.601 transfer and BT.601_525 standard.
*/
- HAL_DATASPACE_BT601_525 = 0x103,
+ HAL_DATASPACE_BT601_525 = 0x103, // deprecated, use HAL_DATASPACE_V0_BT601_525
+
+ HAL_DATASPACE_V0_BT601_525 = HAL_DATASPACE_STANDARD_BT601_525 |
+ HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
/*
* ITU-R Recommendation 709 (BT.709)
*
* High-definition television
*
- * For 8-bit-depth formats:
- * Luma (Y) samples should range from 16 to 235, inclusive
- * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
- *
- * For 10-bit-depth formats:
- * Luma (Y) samples should range from 64 to 940, inclusive
- * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
- *
- * Primaries: x y
- * green 0.300 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * Use limited range, BT.709 transfer and BT.709 standard.
*/
- HAL_DATASPACE_BT709 = 0x104,
+ HAL_DATASPACE_BT709 = 0x104, // deprecated, use HAL_DATASPACE_V0_BT709
+
+ HAL_DATASPACE_V0_BT709 = HAL_DATASPACE_STANDARD_BT709 |
+ HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
+
+ /*
+ * Data spaces for non-color formats
+ */
/*
* The buffer contains depth ranging measurements from a depth camera.
* This value is valid with formats:
- * HAL_PIXEL_FORMAT_Y16: 16-bit single channel depth image.
+ * HAL_PIXEL_FORMAT_Y16: 16-bit samples, consisting of a depth measurement
+ * and an associated confidence value. The 3 MSBs of the sample make
+ * up the confidence value, and the low 13 LSBs of the sample make up
+ * the depth measurement.
+ * For the confidence section, 0 means 100% confidence, 1 means 0%
+ * confidence. The mapping to a linear float confidence value between
+ * 0.f and 1.f can be obtained with
+ * float confidence = (((depthSample >> 13) - 1) & 0x7) / 7.0f;
+ * The depth measurement can be extracted simply with
+ * uint16_t range = (depthSample & 0x1FFF);
* HAL_PIXEL_FORMAT_BLOB: A depth point cloud, as
- * a variable-length float (x,y,z) coordinate point list.
+ * a variable-length float (x,y,z, confidence) coordinate point list.
* The point cloud will be represented with the android_depth_points
* structure.
*/
@@ -642,6 +1141,278 @@
} android_dataspace_t;
+/*
+ * Color modes that may be supported by a display.
+ *
+ * Definitions:
+ * Rendering intent generally defines the goal in mapping a source (input)
+ * color to a destination device color for a given color mode.
+ *
+ * It is important to keep in mind three cases where mapping may be applied:
+ * 1. The source gamut is much smaller than the destination (display) gamut
+ * 2. The source gamut is much larger than the destination gamut (this will
+ * ordinarily be handled using colorimetric rendering, below)
+ * 3. The source and destination gamuts are roughly equal, although not
+ * completely overlapping
+ * Also, a common requirement for mappings is that skin tones should be
+ * preserved, or at least remain natural in appearance.
+ *
+ * Colorimetric Rendering Intent (All cases):
+ * Colorimetric indicates that colors should be preserved. In the case
+ * that the source gamut lies wholly within the destination gamut or is
+ * about the same (#1, #3), this will simply mean that no manipulations
+ * (no saturation boost, for example) are applied. In the case where some
+ * source colors lie outside the destination gamut (#2, #3), those will
+ * need to be mapped to colors that are within the destination gamut,
+ * while the already in-gamut colors remain unchanged.
+ *
+ * Non-colorimetric transforms can take many forms. There are no hard
+ * rules and it's left to the implementation to define.
+ * Two common intents are described below.
+ *
+ * Stretched-Gamut Enhancement Intent (Source < Destination):
+ * When the destination gamut is much larger than the source gamut (#1), the
+ * source primaries may be redefined to reflect the full extent of the
+ * destination space, or to reflect an intermediate gamut.
+ * Skin-tone preservation would likely be applied. An example might be sRGB
+ * input displayed on a DCI-P3 capable device, with skin-tone preservation.
+ *
+ * Within-Gamut Enhancement Intent (Source >= Destination):
+ * When the device (destination) gamut is not larger than the source gamut
+ * (#2 or #3), but the appearance of a larger gamut is desired, techniques
+ * such as saturation boost may be applied to the source colors. Skin-tone
+ * preservation may be applied. There is no unique method for within-gamut
+ * enhancement; it would be defined within a flexible color mode.
+ *
+ */
+typedef enum android_color_mode {
+
+ /*
+ * HAL_COLOR_MODE_DEFAULT is the "native" gamut of the display.
+ * White Point: Vendor/OEM defined
+ * Panel Gamma: Vendor/OEM defined (typically 2.2)
+ * Rendering Intent: Vendor/OEM defined (typically 'enhanced')
+ */
+ HAL_COLOR_MODE_NATIVE = 0,
+
+ /*
+ * HAL_COLOR_MODE_STANDARD_BT601_625 corresponds with display
+ * settings that implement the ITU-R Recommendation BT.601
+ * or Rec 601. Using 625 line version
+ * Rendering Intent: Colorimetric
+ * Primaries:
+ * x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ *
+ * Gamma Correction (GC):
+ *
+ * if Vlinear < 0.018
+ * Vnonlinear = 4.500 * Vlinear
+ * else
+ * Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
+ */
+ HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
+
+ /*
+ * Primaries:
+ * x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
+ * for RGB conversion.
+ *
+ * Gamma Correction (GC):
+ *
+ * if Vlinear < 0.018
+ * Vnonlinear = 4.500 * Vlinear
+ * else
+ * Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
+ */
+ HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
+
+ /*
+ * Primaries:
+ * x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ *
+ * Gamma Correction (GC):
+ *
+ * if Vlinear < 0.018
+ * Vnonlinear = 4.500 * Vlinear
+ * else
+ * Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
+ */
+ HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
+
+ /*
+ * Primaries:
+ * x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
+ * for RGB conversion (as in SMPTE 240M).
+ *
+ * Gamma Correction (GC):
+ *
+ * if Vlinear < 0.018
+ * Vnonlinear = 4.500 * Vlinear
+ * else
+ * Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
+ */
+ HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
+
+ /*
+ * HAL_COLOR_MODE_REC709 corresponds with display settings that implement
+ * the ITU-R Recommendation BT.709 / Rec. 709 for high-definition television.
+ * Rendering Intent: Colorimetric
+ * Primaries:
+ * x y
+ * green 0.300 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * HDTV REC709 Inverse Gamma Correction (IGC): V represents normalized
+ * (with [0 to 1] range) value of R, G, or B.
+ *
+ * if Vnonlinear < 0.081
+ * Vlinear = Vnonlinear / 4.5
+ * else
+ * Vlinear = ((Vnonlinear + 0.099) / 1.099) ^ (1/0.45)
+ *
+ * HDTV REC709 Gamma Correction (GC):
+ *
+ * if Vlinear < 0.018
+ * Vnonlinear = 4.5 * Vlinear
+ * else
+ * Vnonlinear = 1.099 * (Vlinear) ^ 0.45 – 0.099
+ */
+ HAL_COLOR_MODE_STANDARD_BT709 = 5,
+
+ /*
+ * HAL_COLOR_MODE_DCI_P3 corresponds with display settings that implement
+ * SMPTE EG 432-1 and SMPTE RP 431-2
+ * Rendering Intent: Colorimetric
+ * Primaries:
+ * x y
+ * green 0.265 0.690
+ * blue 0.150 0.060
+ * red 0.680 0.320
+ * white (D65) 0.3127 0.3290
+ *
+ * Gamma: 2.2
+ */
+ HAL_COLOR_MODE_DCI_P3 = 6,
+
+ /*
+ * HAL_COLOR_MODE_SRGB corresponds with display settings that implement
+ * the sRGB color space. Uses the same primaries as ITU-R Recommendation
+ * BT.709
+ * Rendering Intent: Colorimetric
+ * Primaries:
+ * x y
+ * green 0.300 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * PC/Internet (sRGB) Inverse Gamma Correction (IGC):
+ *
+ * if Vnonlinear ≤ 0.03928
+ * Vlinear = Vnonlinear / 12.92
+ * else
+ * Vlinear = ((Vnonlinear + 0.055)/1.055) ^ 2.4
+ *
+ * PC/Internet (sRGB) Gamma Correction (GC):
+ *
+ * if Vlinear ≤ 0.0031308
+ * Vnonlinear = 12.92 * Vlinear
+ * else
+ * Vnonlinear = 1.055 * (Vlinear)^(1/2.4) – 0.055
+ */
+ HAL_COLOR_MODE_SRGB = 7,
+
+ /*
+ * HAL_COLOR_MODE_ADOBE_RGB corresponds with the RGB color space developed
+ * by Adobe Systems, Inc. in 1998.
+ * Rendering Intent: Colorimetric
+ * Primaries:
+ * x y
+ * green 0.210 0.710
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Gamma: 2.2
+ */
+ HAL_COLOR_MODE_ADOBE_RGB = 8
+
+} android_color_mode_t;
+
+/*
+ * Color transforms that may be applied by hardware composer to the whole
+ * display.
+ */
+typedef enum android_color_transform {
+ /* Applies no transform to the output color */
+ HAL_COLOR_TRANSFORM_IDENTITY = 0,
+
+ /* Applies an arbitrary transform defined by a 4x4 affine matrix */
+ HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
+
+ /* Applies a transform that inverts the value or luminance of the color, but
+ * does not modify hue or saturation */
+ HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
+
+ /* Applies a transform that maps all colors to shades of gray */
+ HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
+
+ /* Applies a transform which corrects for protanopic color blindness */
+ HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
+
+ /* Applies a transform which corrects for deuteranopic color blindness */
+ HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
+
+ /* Applies a transform which corrects for tritanopic color blindness */
+ HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6
+} android_color_transform_t;
+
+/*
+ * Supported HDR formats. Must be kept in sync with equivalents in Display.java.
+ */
+typedef enum android_hdr {
+ /* Device supports Dolby Vision HDR */
+ HAL_HDR_DOLBY_VISION = 1,
+
+ /* Device supports HDR10 */
+ HAL_HDR_HDR10 = 2,
+
+ /* Device supports hybrid log-gamma HDR */
+ HAL_HDR_HLG = 3
+} android_hdr_t;
+
#ifdef __cplusplus
}
#endif
diff --git a/include/system/qemu_pipe.h b/include/system/qemu_pipe.h
new file mode 100644
index 0000000..af25079
--- /dev/null
+++ b/include/system/qemu_pipe.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_INCLUDE_SYSTEM_QEMU_PIPE_H
+#define ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+# define QEMU_PIPE_DEBUG(...) (void)0
+#endif
+
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and must begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+//
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL -> unknown/unsupported pipeName
+// ENOSYS -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+static __inline__ int qemu_pipe_open(const char* pipeName) {
+ // Sanity check.
+ if (!pipeName || memcmp(pipeName, "pipe:", 5) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+ if (fd < 0) {
+ QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
+ strerror(errno));
+ return -1;
+ }
+
+ // Write the pipe name, *including* the trailing zero which is necessary.
+ size_t pipeNameLen = strlen(pipeName);
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd, pipeName, pipeNameLen + 1U));
+ if (ret != (ssize_t)pipeNameLen + 1) {
+ QEMU_PIPE_DEBUG("%s: Could not connect to %s pipe service: %s",
+ __FUNCTION__, pipeName, strerror(errno));
+ if (ret == 0) {
+ errno = ECONNRESET;
+ } else if (ret > 0) {
+ errno = EINVAL;
+ }
+ return -1;
+ }
+ return fd;
+}
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+static int __inline__ qemu_pipe_frame_send(int fd,
+ const void* buff,
+ size_t len) {
+ char header[5];
+ snprintf(header, sizeof(header), "%04zx", len);
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd, header, 4));
+ if (ret != 4) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ ret = TEMP_FAILURE_RETRY(write(fd, buff, len));
+ if (ret != (ssize_t)len) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+static int __inline__ qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+ char header[5];
+ ssize_t ret = TEMP_FAILURE_RETRY(read(fd, header, 4));
+ if (ret != 4) {
+ QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ header[4] = '\0';
+ size_t size;
+ if (sscanf(header, "%04zx", &size) != 1) {
+ QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+ return -1;
+ }
+ if (size > len) {
+ QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
+ len);
+ return -1;
+ }
+ ret = TEMP_FAILURE_RETRY(read(fd, buff, size));
+ if (ret != (ssize_t)size) {
+ QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
+ strerror(errno));
+ return -1;
+ }
+ return size;
+}
+
+#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/include/system/radio.h b/include/system/radio.h
new file mode 100644
index 0000000..03b252e
--- /dev/null
+++ b/include/system/radio.h
@@ -0,0 +1,253 @@
+/*
+ * 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 ANDROID_RADIO_H
+#define ANDROID_RADIO_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+
+#define RADIO_NUM_BANDS_MAX 16
+#define RADIO_NUM_SPACINGS_MAX 16
+#define RADIO_STRING_LEN_MAX 128
+
+/*
+ * Radio hardware module class. A given radio hardware module HAL is of one class
+ * only. The platform can not have more than one hardware module of each class.
+ * Current version of the framework only supports RADIO_CLASS_AM_FM.
+ */
+typedef enum {
+ RADIO_CLASS_AM_FM = 0, /* FM (including HD radio) and AM */
+ RADIO_CLASS_SAT = 1, /* Satellite Radio */
+ RADIO_CLASS_DT = 2, /* Digital Radio (DAB) */
+} radio_class_t;
+
+/* value for field "type" of radio band described in struct radio_hal_band_config */
+typedef enum {
+ RADIO_BAND_AM = 0, /* Amplitude Modulation band: LW, MW, SW */
+ RADIO_BAND_FM = 1, /* Frequency Modulation band: FM */
+ RADIO_BAND_FM_HD = 2, /* FM HD Radio / DRM (IBOC) */
+ RADIO_BAND_AM_HD = 3, /* AM HD Radio / DRM (IBOC) */
+} radio_band_t;
+
+/* RDS variant implemented. A struct radio_hal_fm_band_config can list none or several. */
+enum {
+ RADIO_RDS_NONE = 0x0,
+ RADIO_RDS_WORLD = 0x01,
+ RADIO_RDS_US = 0x02,
+};
+typedef unsigned int radio_rds_t;
+
+/* FM deemphasis variant implemented. A struct radio_hal_fm_band_config can list one or more. */
+enum {
+ RADIO_DEEMPHASIS_50 = 0x1,
+ RADIO_DEEMPHASIS_75 = 0x2,
+};
+typedef unsigned int radio_deemphasis_t;
+
+/* Region a particular radio band configuration corresponds to. Not used at the HAL.
+ * Derived by the framework when converting the band descriptors retrieved from the HAL to
+ * individual band descriptors for each supported region. */
+typedef enum {
+ RADIO_REGION_NONE = -1,
+ RADIO_REGION_ITU_1 = 0,
+ RADIO_REGION_ITU_2 = 1,
+ RADIO_REGION_OIRT = 2,
+ RADIO_REGION_JAPAN = 3,
+ RADIO_REGION_KOREA = 4,
+} radio_region_t;
+
+/* scanning direction for scan() and step() tuner APIs */
+typedef enum {
+ RADIO_DIRECTION_UP,
+ RADIO_DIRECTION_DOWN
+} radio_direction_t;
+
+/* unique handle allocated to a radio module */
+typedef unsigned int radio_handle_t;
+
+/* Opaque meta data structure used by radio meta data API (see system/radio_metadata.h) */
+typedef struct radio_metadata radio_metadata_t;
+
+
+/* Additional attributes for an FM band configuration */
+typedef struct radio_hal_fm_band_config {
+ radio_deemphasis_t deemphasis; /* deemphasis variant */
+ bool stereo; /* stereo supported */
+ radio_rds_t rds; /* RDS variants supported */
+ bool ta; /* Traffic Announcement supported */
+ bool af; /* Alternate Frequency supported */
+ bool ea; /* Emergency announcements supported */
+} radio_hal_fm_band_config_t;
+
+/* Additional attributes for an AM band configuration */
+typedef struct radio_hal_am_band_config {
+ bool stereo; /* stereo supported */
+} radio_hal_am_band_config_t;
+
+/* Radio band configuration. Describes a given band supported by the radio module.
+ * The HAL can expose only one band per type with the the maximum range supported and all options.
+ * THe framework will derive the actual regions were this module can operate and expose separate
+ * band configurations for applications to chose from. */
+typedef struct radio_hal_band_config {
+ radio_band_t type;
+ bool antenna_connected;
+ unsigned int lower_limit;
+ unsigned int upper_limit;
+ unsigned int num_spacings;
+ unsigned int spacings[RADIO_NUM_SPACINGS_MAX];
+ union {
+ radio_hal_fm_band_config_t fm;
+ radio_hal_am_band_config_t am;
+ };
+} radio_hal_band_config_t;
+
+/* Used internally by the framework to represent a band for s specific region */
+typedef struct radio_band_config {
+ radio_region_t region;
+ radio_hal_band_config_t band;
+} radio_band_config_t;
+
+
+/* Exposes properties of a given hardware radio module.
+ * NOTE: current framework implementation supports only one audio source (num_audio_sources = 1).
+ * The source corresponds to AUDIO_DEVICE_IN_FM_TUNER.
+ * If more than one tuner is supported (num_tuners > 1), only one can be connected to the audio
+ * source. */
+typedef struct radio_hal_properties {
+ radio_class_t class_id; /* Class of this module. E.g RADIO_CLASS_AM_FM */
+ char implementor[RADIO_STRING_LEN_MAX]; /* implementor name */
+ char product[RADIO_STRING_LEN_MAX]; /* product name */
+ char version[RADIO_STRING_LEN_MAX]; /* product version */
+ char serial[RADIO_STRING_LEN_MAX]; /* serial number (for subscription services) */
+ unsigned int num_tuners; /* number of tuners controllable independently */
+ unsigned int num_audio_sources; /* number of audio sources driven simultaneously */
+ bool supports_capture; /* the hardware supports capture of audio source audio HAL */
+ unsigned int num_bands; /* number of band descriptors */
+ radio_hal_band_config_t bands[RADIO_NUM_BANDS_MAX]; /* band descriptors */
+} radio_hal_properties_t;
+
+/* Used internally by the framework. Same information as in struct radio_hal_properties plus a
+ * unique handle and one band configuration per region. */
+typedef struct radio_properties {
+ radio_handle_t handle;
+ radio_class_t class_id;
+ char implementor[RADIO_STRING_LEN_MAX];
+ char product[RADIO_STRING_LEN_MAX];
+ char version[RADIO_STRING_LEN_MAX];
+ char serial[RADIO_STRING_LEN_MAX];
+ unsigned int num_tuners;
+ unsigned int num_audio_sources;
+ bool supports_capture;
+ unsigned int num_bands;
+ radio_band_config_t bands[RADIO_NUM_BANDS_MAX];
+} radio_properties_t;
+
+/* Radio program information. Returned by the HAL with event RADIO_EVENT_TUNED.
+ * Contains information on currently tuned channel.
+ */
+typedef struct radio_program_info {
+ unsigned int channel; /* current channel. (e.g kHz for band type RADIO_BAND_FM) */
+ unsigned int sub_channel; /* current sub channel. (used for RADIO_BAND_FM_HD) */
+ bool tuned; /* tuned to a program or not */
+ bool stereo; /* program is stereo or not */
+ bool digital; /* digital program or not (e.g HD Radio program) */
+ unsigned int signal_strength; /* signal strength from 0 to 100 */
+ /* meta data (e.g PTY, song title ...), must not be NULL */
+ __attribute__((aligned(8))) radio_metadata_t *metadata;
+} radio_program_info_t;
+
+
+/* Events sent to the framework via the HAL callback. An event can notify the completion of an
+ * asynchronous command (configuration, tune, scan ...) or a spontaneous change (antenna connection,
+ * failure, AF switching, meta data reception... */
+enum {
+ RADIO_EVENT_HW_FAILURE = 0, /* hardware module failure. Requires reopening the tuner */
+ RADIO_EVENT_CONFIG = 1, /* configuration change completed */
+ RADIO_EVENT_ANTENNA = 2, /* Antenna connected, disconnected */
+ RADIO_EVENT_TUNED = 3, /* tune, step, scan completed */
+ RADIO_EVENT_METADATA = 4, /* New meta data received */
+ RADIO_EVENT_TA = 5, /* Traffic announcement start or stop */
+ RADIO_EVENT_AF_SWITCH = 6, /* Switch to Alternate Frequency */
+ RADIO_EVENT_EA = 7, /* Emergency announcement start or stop */
+ // begin framework only events
+ RADIO_EVENT_CONTROL = 100, /* loss/gain of tuner control */
+ RADIO_EVENT_SERVER_DIED = 101, /* radio service died */
+};
+typedef unsigned int radio_event_type_t;
+
+/* Event passed to the framework by the HAL callback */
+typedef struct radio_hal_event {
+ radio_event_type_t type; /* event type */
+ int status; /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */
+ union {
+ /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA, RADIO_EVENT_EA */
+ bool on;
+ radio_hal_band_config_t config; /* RADIO_EVENT_CONFIG */
+ radio_program_info_t info; /* RADIO_EVENT_TUNED, RADIO_EVENT_AF_SWITCH */
+ radio_metadata_t *metadata; /* RADIO_EVENT_METADATA */
+ };
+} radio_hal_event_t;
+
+/* Used internally by the framework. Same information as in struct radio_hal_event */
+typedef struct radio_event {
+ radio_event_type_t type;
+ int status;
+ union {
+ bool on;
+ radio_band_config_t config;
+ radio_program_info_t info;
+ radio_metadata_t *metadata; /* offset from start of struct when in shared memory */
+ };
+} radio_event_t;
+
+
+static inline
+radio_rds_t radio_rds_for_region(bool rds, radio_region_t region) {
+ if (!rds)
+ return RADIO_RDS_NONE;
+ switch(region) {
+ case RADIO_REGION_ITU_1:
+ case RADIO_REGION_OIRT:
+ case RADIO_REGION_JAPAN:
+ case RADIO_REGION_KOREA:
+ return RADIO_RDS_WORLD;
+ case RADIO_REGION_ITU_2:
+ return RADIO_RDS_US;
+ default:
+ return RADIO_REGION_NONE;
+ }
+}
+
+static inline
+radio_deemphasis_t radio_demephasis_for_region(radio_region_t region) {
+ switch(region) {
+ case RADIO_REGION_KOREA:
+ case RADIO_REGION_ITU_2:
+ return RADIO_DEEMPHASIS_75;
+ case RADIO_REGION_ITU_1:
+ case RADIO_REGION_OIRT:
+ case RADIO_REGION_JAPAN:
+ default:
+ return RADIO_DEEMPHASIS_50;
+ }
+}
+
+#endif // ANDROID_RADIO_H
diff --git a/include/system/sound_trigger.h b/include/system/sound_trigger.h
deleted file mode 100644
index 773e4f7..0000000
--- a/include/system/sound_trigger.h
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SOUND_TRIGGER_H
-#define ANDROID_SOUND_TRIGGER_H
-
-#include <stdbool.h>
-#include <system/audio.h>
-
-#define SOUND_TRIGGER_MAX_STRING_LEN 64 /* max length of strings in properties or
- descriptor structs */
-#define SOUND_TRIGGER_MAX_LOCALE_LEN 6 /* max length of locale string. e.g en_US */
-#define SOUND_TRIGGER_MAX_USERS 10 /* max number of concurrent users */
-#define SOUND_TRIGGER_MAX_PHRASES 10 /* max number of concurrent phrases */
-
-typedef enum {
- SOUND_TRIGGER_STATE_NO_INIT = -1, /* The sound trigger service is not initialized */
- SOUND_TRIGGER_STATE_ENABLED = 0, /* The sound trigger service is enabled */
- SOUND_TRIGGER_STATE_DISABLED = 1 /* The sound trigger service is disabled */
-} sound_trigger_service_state_t;
-
-#define RECOGNITION_MODE_VOICE_TRIGGER 0x1 /* simple voice trigger */
-#define RECOGNITION_MODE_USER_IDENTIFICATION 0x2 /* trigger only if one user in model identified */
-#define RECOGNITION_MODE_USER_AUTHENTICATION 0x4 /* trigger only if one user in mode
- authenticated */
-#define RECOGNITION_STATUS_SUCCESS 0
-#define RECOGNITION_STATUS_ABORT 1
-#define RECOGNITION_STATUS_FAILURE 2
-
-#define SOUND_MODEL_STATUS_UPDATED 0
-
-typedef enum {
- SOUND_MODEL_TYPE_UNKNOWN = -1, /* use for unspecified sound model type */
- SOUND_MODEL_TYPE_KEYPHRASE = 0 /* use for key phrase sound models */
-} sound_trigger_sound_model_type_t;
-
-typedef struct sound_trigger_uuid_s {
- unsigned int timeLow;
- unsigned short timeMid;
- unsigned short timeHiAndVersion;
- unsigned short clockSeq;
- unsigned char node[6];
-} sound_trigger_uuid_t;
-
-/*
- * sound trigger implementation descriptor read by the framework via get_properties().
- * Used by SoundTrigger service to report to applications and manage concurrency and policy.
- */
-struct sound_trigger_properties {
- char implementor[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementor name */
- char description[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementation description */
- unsigned int version; /* implementation version */
- sound_trigger_uuid_t uuid; /* unique implementation ID.
- Must change with version each version */
- unsigned int max_sound_models; /* maximum number of concurrent sound models
- loaded */
- unsigned int max_key_phrases; /* maximum number of key phrases */
- unsigned int max_users; /* maximum number of concurrent users detected */
- unsigned int recognition_modes; /* all supported modes.
- e.g RECOGNITION_MODE_VOICE_TRIGGER */
- bool capture_transition; /* supports seamless transition from detection
- to capture */
- unsigned int max_buffer_ms; /* maximum buffering capacity in ms if
- capture_transition is true*/
- bool concurrent_capture; /* supports capture by other use cases while
- detection is active */
- bool trigger_in_event; /* returns the trigger capture in event */
- unsigned int power_consumption_mw; /* Rated power consumption when detection is active
- with TDB silence/sound/speech ratio */
-};
-
-typedef int sound_trigger_module_handle_t;
-
-struct sound_trigger_module_descriptor {
- sound_trigger_module_handle_t handle;
- struct sound_trigger_properties properties;
-};
-
-typedef int sound_model_handle_t;
-
-/*
- * Generic sound model descriptor. This struct is the header of a larger block passed to
- * load_sound_model() and containing the binary data of the sound model.
- * Proprietary representation of users in binary data must match information indicated
- * by users field
- */
-struct sound_trigger_sound_model {
- sound_trigger_sound_model_type_t type; /* model type. e.g. SOUND_MODEL_TYPE_KEYPHRASE */
- sound_trigger_uuid_t uuid; /* unique sound model ID. */
- sound_trigger_uuid_t vendor_uuid; /* unique vendor ID. Identifies the engine the
- sound model was build for */
- unsigned int data_size; /* size of opaque model data */
- unsigned int data_offset; /* offset of opaque data start from head of struct
- (e.g sizeof struct sound_trigger_sound_model) */
-};
-
-/* key phrase descriptor */
-struct sound_trigger_phrase {
- unsigned int id; /* keyphrase ID */
- unsigned int recognition_mode; /* recognition modes supported by this key phrase */
- unsigned int num_users; /* number of users in the key phrase */
- unsigned int users[SOUND_TRIGGER_MAX_USERS]; /* users ids: (not uid_t but sound trigger
- specific IDs */
- char locale[SOUND_TRIGGER_MAX_LOCALE_LEN]; /* locale - JAVA Locale style (e.g. en_US) */
- char text[SOUND_TRIGGER_MAX_STRING_LEN]; /* phrase text in UTF-8 format. */
-};
-
-/*
- * Specialized sound model for key phrase detection.
- * Proprietary representation of key phrases in binary data must match information indicated
- * by phrases field
- */
-struct sound_trigger_phrase_sound_model {
- struct sound_trigger_sound_model common;
- unsigned int num_phrases; /* number of key phrases in model */
- struct sound_trigger_phrase phrases[SOUND_TRIGGER_MAX_PHRASES];
-};
-
-
-/*
- * Generic recognition event sent via recognition callback
- */
-struct sound_trigger_recognition_event {
- int status; /* recognition status e.g.
- RECOGNITION_STATUS_SUCCESS */
- sound_trigger_sound_model_type_t type; /* event type, same as sound model type.
- e.g. SOUND_MODEL_TYPE_KEYPHRASE */
- sound_model_handle_t model; /* loaded sound model that triggered the
- event */
- bool capture_available; /* it is possible to capture audio from this
- utterance buffered by the
- implementation */
- int capture_session; /* audio session ID. framework use */
- int capture_delay_ms; /* delay in ms between end of model
- detection and start of audio available
- for capture. A negative value is possible
- (e.g. if key phrase is also available for
- capture */
- int capture_preamble_ms; /* duration in ms of audio captured
- before the start of the trigger.
- 0 if none. */
- bool trigger_in_data; /* the opaque data is the capture of
- the trigger sound */
- audio_config_t audio_config; /* audio format of either the trigger in
- event data or to use for capture of the
- rest of the utterance */
- unsigned int data_size; /* size of opaque event data */
- unsigned int data_offset; /* offset of opaque data start from start of
- this struct (e.g sizeof struct
- sound_trigger_phrase_recognition_event) */
-};
-
-/*
- * Confidence level for each user in struct sound_trigger_phrase_recognition_extra
- */
-struct sound_trigger_confidence_level {
- unsigned int user_id; /* user ID */
- unsigned int level; /* confidence level in percent (0 - 100).
- - min level for recognition configuration
- - detected level for recognition event */
-};
-
-/*
- * Specialized recognition event for key phrase detection
- */
-struct sound_trigger_phrase_recognition_extra {
- unsigned int id; /* keyphrase ID */
- unsigned int recognition_modes; /* recognition modes used for this keyphrase */
- unsigned int confidence_level; /* confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */
- unsigned int num_levels; /* number of user confidence levels */
- struct sound_trigger_confidence_level levels[SOUND_TRIGGER_MAX_USERS];
-};
-
-struct sound_trigger_phrase_recognition_event {
- struct sound_trigger_recognition_event common;
- unsigned int num_phrases;
- struct sound_trigger_phrase_recognition_extra phrase_extras[SOUND_TRIGGER_MAX_PHRASES];
-};
-
-/*
- * configuration for sound trigger capture session passed to start_recognition()
- */
-struct sound_trigger_recognition_config {
- audio_io_handle_t capture_handle; /* IO handle that will be used for capture.
- N/A if capture_requested is false */
- audio_devices_t capture_device; /* input device requested for detection capture */
- bool capture_requested; /* capture and buffer audio for this recognition
- instance */
- unsigned int num_phrases; /* number of key phrases recognition extras */
- struct sound_trigger_phrase_recognition_extra phrases[SOUND_TRIGGER_MAX_PHRASES];
- /* configuration for each key phrase */
- unsigned int data_size; /* size of opaque capture configuration data */
- unsigned int data_offset; /* offset of opaque data start from start of this struct
- (e.g sizeof struct sound_trigger_recognition_config) */
-};
-
-/*
- * Event sent via load sound model callback
- */
-struct sound_trigger_model_event {
- int status; /* sound model status e.g. SOUND_MODEL_STATUS_UPDATED */
- sound_model_handle_t model; /* loaded sound model that triggered the event */
- unsigned int data_size; /* size of event data if any. Size of updated sound model if
- status is SOUND_MODEL_STATUS_UPDATED */
- unsigned int data_offset; /* offset of data start from start of this struct
- (e.g sizeof struct sound_trigger_model_event) */
-};
-
-
-#endif // ANDROID_SOUND_TRIGGER_H
diff --git a/include/system/window.h b/include/system/window.h
index 508ce00..f439705 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -25,6 +25,7 @@
#include <sys/cdefs.h>
#include <system/graphics.h>
#include <unistd.h>
+#include <stdbool.h>
#ifndef __UNUSED
#define __UNUSED __attribute__((__unused__))
@@ -37,8 +38,17 @@
/*****************************************************************************/
+#ifdef __cplusplus
+#define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast<unsigned int>(x)
+#else
+#define ANDROID_NATIVE_UNSIGNED_CAST(x) ((unsigned int)(x))
+#endif
+
#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \
- (((unsigned)(a)<<24)|((unsigned)(b)<<16)|((unsigned)(c)<<8)|(unsigned)(d))
+ ((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \
+ (ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \
+ (ANDROID_NATIVE_UNSIGNED_CAST(c) << 8) | \
+ (ANDROID_NATIVE_UNSIGNED_CAST(d)))
#define ANDROID_NATIVE_WINDOW_MAGIC \
ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
@@ -277,6 +287,16 @@
* age will be 0.
*/
NATIVE_WINDOW_BUFFER_AGE = 13,
+
+ /*
+ * Returns the duration of the last dequeueBuffer call in microseconds
+ */
+ NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
+
+ /*
+ * Returns the duration of the last queueBuffer call in microseconds
+ */
+ NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
};
/* Valid operations for the (*perform)() hook.
@@ -311,6 +331,9 @@
NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
+ NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21,
+ NATIVE_WINDOW_SET_AUTO_REFRESH = 22,
+ NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 23,
};
/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -351,7 +374,8 @@
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
};
-/* parameter for NATIVE_WINDOW_SET_SCALING_MODE */
+/* parameter for NATIVE_WINDOW_SET_SCALING_MODE
+ * keep in sync with Surface.java in frameworks/base */
enum {
/* the window content is not updated (frozen) until a buffer of
* the window size is received (enqueued)
@@ -949,6 +973,41 @@
rects, numRects);
}
+/*
+ * native_window_set_shared_buffer_mode(..., bool sharedBufferMode)
+ * Enable/disable shared buffer mode
+ */
+static inline int native_window_set_shared_buffer_mode(
+ struct ANativeWindow* window,
+ bool sharedBufferMode)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE,
+ sharedBufferMode);
+}
+
+/*
+ * native_window_set_auto_refresh(..., autoRefresh)
+ * Enable/disable auto refresh when in shared buffer mode
+ */
+static inline int native_window_set_auto_refresh(
+ struct ANativeWindow* window,
+ bool autoRefresh)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh);
+}
+
+static inline int native_window_get_frame_timestamps(
+ struct ANativeWindow* window, uint32_t framesAgo,
+ int64_t* outPostedTime, int64_t* outAcquireTime,
+ int64_t* outRefreshStartTime, int64_t* outGlCompositionDoneTime,
+ int64_t* outDisplayRetireTime, int64_t* outReleaseTime)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS,
+ framesAgo, outPostedTime, outAcquireTime, outRefreshStartTime,
+ outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
+}
+
+
__END_DECLS
#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
diff --git a/include/sysutils b/include/sysutils
new file mode 120000
index 0000000..1c8e85b
--- /dev/null
+++ b/include/sysutils
@@ -0,0 +1 @@
+../libsysutils/include/sysutils/
\ No newline at end of file
diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h
deleted file mode 100644
index 18049cd..0000000
--- a/include/sysutils/FrameworkListener.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 _FRAMEWORKSOCKETLISTENER_H
-#define _FRAMEWORKSOCKETLISTENER_H
-
-#include "SocketListener.h"
-#include "FrameworkCommand.h"
-
-class SocketClient;
-
-class FrameworkListener : public SocketListener {
-public:
- static const int CMD_ARGS_MAX = 26;
-
- /* 1 out of errorRate will be dropped */
- int errorRate;
-
-private:
- int mCommandCount;
- bool mWithSeq;
- FrameworkCommandCollection *mCommands;
-
-public:
- FrameworkListener(const char *socketName);
- FrameworkListener(const char *socketName, bool withSeq);
- FrameworkListener(int sock);
- virtual ~FrameworkListener() {}
-
-protected:
- void registerCmd(FrameworkCommand *cmd);
- virtual bool onDataAvailable(SocketClient *c);
-
-private:
- void dispatchCommand(SocketClient *c, char *data);
- void init(const char *socketName, bool withSeq);
-};
-#endif
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h
deleted file mode 100644
index 4fa49c5..0000000
--- a/include/sysutils/NetlinkEvent.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 _NETLINKEVENT_H
-#define _NETLINKEVENT_H
-
-#include <sysutils/NetlinkListener.h>
-
-#define NL_PARAMS_MAX 32
-
-class NetlinkEvent {
- int mSeq;
- char *mPath;
- int mAction;
- char *mSubsystem;
- char *mParams[NL_PARAMS_MAX];
-
-public:
- const static int NlActionUnknown;
- const static int NlActionAdd;
- const static int NlActionRemove;
- const static int NlActionChange;
- const static int NlActionLinkDown;
- const static int NlActionLinkUp;
- const static int NlActionAddressUpdated;
- const static int NlActionAddressRemoved;
- const static int NlActionRdnss;
- const static int NlActionRouteUpdated;
- const static int NlActionRouteRemoved;
-
- NetlinkEvent();
- virtual ~NetlinkEvent();
-
- bool decode(char *buffer, int size, int format = NetlinkListener::NETLINK_FORMAT_ASCII);
- const char *findParam(const char *paramName);
-
- const char *getSubsystem() { return mSubsystem; }
- int getAction() { return mAction; }
-
- void dump();
-
- protected:
- bool parseBinaryNetlinkMessage(char *buffer, int size);
- bool parseAsciiNetlinkMessage(char *buffer, int size);
- bool parseIfInfoMessage(const struct nlmsghdr *nh);
- bool parseIfAddrMessage(const struct nlmsghdr *nh);
- bool parseUlogPacketMessage(const struct nlmsghdr *nh);
- bool parseNfPacketMessage(struct nlmsghdr *nh);
- bool parseRtMessage(const struct nlmsghdr *nh);
- bool parseNdUserOptMessage(const struct nlmsghdr *nh);
-};
-
-#endif
diff --git a/include/usbhost/usbhost.h b/include/usbhost/usbhost.h
deleted file mode 100644
index d26e931..0000000
--- a/include/usbhost/usbhost.h
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __USB_HOST_H
-#define __USB_HOST_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-#include <linux/version.h>
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
-#include <linux/usb/ch9.h>
-#else
-#include <linux/usb_ch9.h>
-#endif
-
-struct usb_host_context;
-struct usb_endpoint_descriptor;
-
-struct usb_descriptor_iter {
- unsigned char* config;
- unsigned char* config_end;
- unsigned char* curr_desc;
-};
-
-struct usb_request
-{
- struct usb_device *dev;
- void* buffer;
- int buffer_length;
- int actual_length;
- int max_packet_size;
- void *private_data; /* struct usbdevfs_urb* */
- int endpoint;
- void *client_data; /* free for use by client */
-};
-
-/* Callback for notification when new USB devices are attached.
- * Return true to exit from usb_host_run.
- */
-typedef int (* usb_device_added_cb)(const char *dev_name, void *client_data);
-
-/* Callback for notification when USB devices are removed.
- * Return true to exit from usb_host_run.
- */
-typedef int (* usb_device_removed_cb)(const char *dev_name, void *client_data);
-
-/* Callback indicating that initial device discovery is done.
- * Return true to exit from usb_host_run.
- */
-typedef int (* usb_discovery_done_cb)(void *client_data);
-
-/* Call this to initialize the USB host library. */
-struct usb_host_context *usb_host_init(void);
-
-/* Call this to cleanup the USB host library. */
-void usb_host_cleanup(struct usb_host_context *context);
-
-/* Call this to get the inotify file descriptor. */
-int usb_host_get_fd(struct usb_host_context *context);
-
-/* Call this to initialize the usb host context. */
-int usb_host_load(struct usb_host_context *context,
- usb_device_added_cb added_cb,
- usb_device_removed_cb removed_cb,
- usb_discovery_done_cb discovery_done_cb,
- void *client_data);
-
-/* Call this to read and handle occuring usb event. */
-int usb_host_read_event(struct usb_host_context *context);
-
-/* Call this to monitor the USB bus for new and removed devices.
- * This is intended to be called from a dedicated thread,
- * as it will not return until one of the callbacks returns true.
- * added_cb will be called immediately for each existing USB device,
- * and subsequently each time a new device is added.
- * removed_cb is called when USB devices are removed from the bus.
- * discovery_done_cb is called after the initial discovery of already
- * connected devices is complete.
- */
-void usb_host_run(struct usb_host_context *context,
- usb_device_added_cb added_cb,
- usb_device_removed_cb removed_cb,
- usb_discovery_done_cb discovery_done_cb,
- void *client_data);
-
-/* Creates a usb_device object for a USB device */
-struct usb_device *usb_device_open(const char *dev_name);
-
-/* Releases all resources associated with the USB device */
-void usb_device_close(struct usb_device *device);
-
-/* Creates a usb_device object for already open USB device */
-struct usb_device *usb_device_new(const char *dev_name, int fd);
-
-/* Returns the file descriptor for the usb_device */
-int usb_device_get_fd(struct usb_device *device);
-
-/* Returns the name for the USB device, which is the same as
- * the dev_name passed to usb_device_open()
- */
-const char* usb_device_get_name(struct usb_device *device);
-
-/* Returns a unique ID for the device.
- *Currently this is generated from the dev_name path.
- */
-int usb_device_get_unique_id(struct usb_device *device);
-
-/* Returns a unique ID for the device name.
- * Currently this is generated from the device path.
- */
-int usb_device_get_unique_id_from_name(const char* name);
-
-/* Returns the device name for the unique ID.
- * Call free() to deallocate the returned string */
-char* usb_device_get_name_from_unique_id(int id);
-
-/* Returns the USB vendor ID from the device descriptor for the USB device */
-uint16_t usb_device_get_vendor_id(struct usb_device *device);
-
-/* Returns the USB product ID from the device descriptor for the USB device */
-uint16_t usb_device_get_product_id(struct usb_device *device);
-
-const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
-
-/* Returns a USB descriptor string for the given string ID.
- * Used to implement usb_device_get_manufacturer_name,
- * usb_device_get_product_name and usb_device_get_serial.
- * Call free() to free the result when you are done with it.
- */
-char* usb_device_get_string(struct usb_device *device, int id);
-
-/* Returns the manufacturer name for the USB device.
- * Call free() to free the result when you are done with it.
- */
-char* usb_device_get_manufacturer_name(struct usb_device *device);
-
-/* Returns the product name for the USB device.
- * Call free() to free the result when you are done with it.
- */
-char* usb_device_get_product_name(struct usb_device *device);
-
-/* Returns the USB serial number for the USB device.
- * Call free() to free the result when you are done with it.
- */
-char* usb_device_get_serial(struct usb_device *device);
-
-/* Returns true if we have write access to the USB device,
- * and false if we only have access to the USB device configuration.
- */
-int usb_device_is_writeable(struct usb_device *device);
-
-/* Initializes a usb_descriptor_iter, which can be used to iterate through all
- * the USB descriptors for a USB device.
- */
-void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter);
-
-/* Returns the next USB descriptor for a device, or NULL if we have reached the
- * end of the list.
- */
-struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter);
-
-/* Claims the specified interface of a USB device */
-int usb_device_claim_interface(struct usb_device *device, unsigned int interface);
-
-/* Releases the specified interface of a USB device */
-int usb_device_release_interface(struct usb_device *device, unsigned int interface);
-
-/* Requests the kernel to connect or disconnect its driver for the specified interface.
- * This can be used to ask the kernel to disconnect its driver for a device
- * so usb_device_claim_interface can claim it instead.
- */
-int usb_device_connect_kernel_driver(struct usb_device *device,
- unsigned int interface, int connect);
-
-/* Sets the current configuration for the device to the specified configuration */
-int usb_device_set_configuration(struct usb_device *device, int configuration);
-
-/* Sets the specified interface of a USB device */
-int usb_device_set_interface(struct usb_device *device, unsigned int interface,
- unsigned int alt_setting);
-
-/* Sends a control message to the specified device on endpoint zero */
-int usb_device_control_transfer(struct usb_device *device,
- int requestType,
- int request,
- int value,
- int index,
- void* buffer,
- int length,
- unsigned int timeout);
-
-/* Reads or writes on a bulk endpoint.
- * Returns number of bytes transferred, or negative value for error.
- */
-int usb_device_bulk_transfer(struct usb_device *device,
- int endpoint,
- void* buffer,
- int length,
- unsigned int timeout);
-
-/* Creates a new usb_request. */
-struct usb_request *usb_request_new(struct usb_device *dev,
- const struct usb_endpoint_descriptor *ep_desc);
-
-/* Releases all resources associated with the request */
-void usb_request_free(struct usb_request *req);
-
-/* Submits a read or write request on the specified device */
-int usb_request_queue(struct usb_request *req);
-
- /* Waits for the results of a previous usb_request_queue operation.
- * Returns a usb_request, or NULL for error.
- */
-struct usb_request *usb_request_wait(struct usb_device *dev);
-
-/* Cancels a pending usb_request_queue() operation. */
-int usb_request_cancel(struct usb_request *req);
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* __USB_HOST_H */
diff --git a/include/utils b/include/utils
new file mode 120000
index 0000000..e8476fd
--- /dev/null
+++ b/include/utils
@@ -0,0 +1 @@
+../libutils/include/utils/
\ No newline at end of file
diff --git a/include/utils/BasicHashtable.h b/include/utils/BasicHashtable.h
deleted file mode 100644
index c235d62..0000000
--- a/include/utils/BasicHashtable.h
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_BASIC_HASHTABLE_H
-#define ANDROID_BASIC_HASHTABLE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/SharedBuffer.h>
-#include <utils/TypeHelpers.h>
-
-namespace android {
-
-/* Implementation type. Nothing to see here. */
-class BasicHashtableImpl {
-protected:
- struct Bucket {
- // The collision flag indicates that the bucket is part of a collision chain
- // such that at least two entries both hash to this bucket. When true, we
- // may need to seek further along the chain to find the entry.
- static const uint32_t COLLISION = 0x80000000UL;
-
- // The present flag indicates that the bucket contains an initialized entry value.
- static const uint32_t PRESENT = 0x40000000UL;
-
- // Mask for 30 bits worth of the hash code that are stored within the bucket to
- // speed up lookups and rehashing by eliminating the need to recalculate the
- // hash code of the entry's key.
- static const uint32_t HASH_MASK = 0x3fffffffUL;
-
- // Combined value that stores the collision and present flags as well as
- // a 30 bit hash code.
- uint32_t cookie;
-
- // Storage for the entry begins here.
- char entry[0];
- };
-
- BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor,
- size_t minimumInitialCapacity, float loadFactor);
- BasicHashtableImpl(const BasicHashtableImpl& other);
- virtual ~BasicHashtableImpl();
-
- void dispose();
-
- inline void edit() {
- if (mBuckets && !SharedBuffer::bufferFromData(mBuckets)->onlyOwner()) {
- clone();
- }
- }
-
- void setTo(const BasicHashtableImpl& other);
- void clear();
-
- ssize_t next(ssize_t index) const;
- ssize_t find(ssize_t index, hash_t hash, const void* __restrict__ key) const;
- size_t add(hash_t hash, const void* __restrict__ entry);
- void removeAt(size_t index);
- void rehash(size_t minimumCapacity, float loadFactor);
-
- const size_t mBucketSize; // number of bytes per bucket including the entry
- const bool mHasTrivialDestructor; // true if the entry type does not require destruction
- size_t mCapacity; // number of buckets that can be filled before exceeding load factor
- float mLoadFactor; // load factor
- size_t mSize; // number of elements actually in the table
- size_t mFilledBuckets; // number of buckets for which collision or present is true
- size_t mBucketCount; // number of slots in the mBuckets array
- void* mBuckets; // array of buckets, as a SharedBuffer
-
- inline const Bucket& bucketAt(const void* __restrict__ buckets, size_t index) const {
- return *reinterpret_cast<const Bucket*>(
- static_cast<const uint8_t*>(buckets) + index * mBucketSize);
- }
-
- inline Bucket& bucketAt(void* __restrict__ buckets, size_t index) const {
- return *reinterpret_cast<Bucket*>(static_cast<uint8_t*>(buckets) + index * mBucketSize);
- }
-
- virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const = 0;
- virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const = 0;
- virtual void destroyBucketEntry(Bucket& bucket) const = 0;
-
-private:
- void clone();
-
- // Allocates a bucket array as a SharedBuffer.
- void* allocateBuckets(size_t count) const;
-
- // Releases a bucket array's associated SharedBuffer.
- void releaseBuckets(void* __restrict__ buckets, size_t count) const;
-
- // Destroys the contents of buckets (invokes destroyBucketEntry for each
- // populated bucket if needed).
- void destroyBuckets(void* __restrict__ buckets, size_t count) const;
-
- // Copies the content of buckets (copies the cookie and invokes copyBucketEntry
- // for each populated bucket if needed).
- void copyBuckets(const void* __restrict__ fromBuckets,
- void* __restrict__ toBuckets, size_t count) const;
-
- // Determines the appropriate size of a bucket array to store a certain minimum
- // number of entries and returns its effective capacity.
- static void determineCapacity(size_t minimumCapacity, float loadFactor,
- size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity);
-
- // Trim a hash code to 30 bits to match what we store in the bucket's cookie.
- inline static hash_t trimHash(hash_t hash) {
- return (hash & Bucket::HASH_MASK) ^ (hash >> 30);
- }
-
- // Returns the index of the first bucket that is in the collision chain
- // for the specified hash code, given the total number of buckets.
- // (Primary hash)
- inline static size_t chainStart(hash_t hash, size_t count) {
- return hash % count;
- }
-
- // Returns the increment to add to a bucket index to seek to the next bucket
- // in the collision chain for the specified hash code, given the total number of buckets.
- // (Secondary hash)
- inline static size_t chainIncrement(hash_t hash, size_t count) {
- return ((hash >> 7) | (hash << 25)) % (count - 1) + 1;
- }
-
- // Returns the index of the next bucket that is in the collision chain
- // that is defined by the specified increment, given the total number of buckets.
- inline static size_t chainSeek(size_t index, size_t increment, size_t count) {
- return (index + increment) % count;
- }
-};
-
-/*
- * A BasicHashtable stores entries that are indexed by hash code in place
- * within an array. The basic operations are finding entries by key,
- * adding new entries and removing existing entries.
- *
- * This class provides a very limited set of operations with simple semantics.
- * It is intended to be used as a building block to construct more complex
- * and interesting data structures such as HashMap. Think very hard before
- * adding anything extra to BasicHashtable, it probably belongs at a
- * higher level of abstraction.
- *
- * TKey: The key type.
- * TEntry: The entry type which is what is actually stored in the array.
- *
- * TKey must support the following contract:
- * bool operator==(const TKey& other) const; // return true if equal
- * bool operator!=(const TKey& other) const; // return true if unequal
- *
- * TEntry must support the following contract:
- * const TKey& getKey() const; // get the key from the entry
- *
- * This class supports storing entries with duplicate keys. Of course, it can't
- * tell them apart during removal so only the first entry will be removed.
- * We do this because it means that operations like add() can't fail.
- */
-template <typename TKey, typename TEntry>
-class BasicHashtable : private BasicHashtableImpl {
-public:
- /* Creates a hashtable with the specified minimum initial capacity.
- * The underlying array will be created when the first entry is added.
- *
- * minimumInitialCapacity: The minimum initial capacity for the hashtable.
- * Default is 0.
- * loadFactor: The desired load factor for the hashtable, between 0 and 1.
- * Default is 0.75.
- */
- BasicHashtable(size_t minimumInitialCapacity = 0, float loadFactor = 0.75f);
-
- /* Copies a hashtable.
- * The underlying storage is shared copy-on-write.
- */
- BasicHashtable(const BasicHashtable& other);
-
- /* Clears and destroys the hashtable.
- */
- virtual ~BasicHashtable();
-
- /* Making this hashtable a copy of the other hashtable.
- * The underlying storage is shared copy-on-write.
- *
- * other: The hashtable to copy.
- */
- inline BasicHashtable<TKey, TEntry>& operator =(const BasicHashtable<TKey, TEntry> & other) {
- setTo(other);
- return *this;
- }
-
- /* Returns the number of entries in the hashtable.
- */
- inline size_t size() const {
- return mSize;
- }
-
- /* Returns the capacity of the hashtable, which is the number of elements that can
- * added to the hashtable without requiring it to be grown.
- */
- inline size_t capacity() const {
- return mCapacity;
- }
-
- /* Returns the number of buckets that the hashtable has, which is the size of its
- * underlying array.
- */
- inline size_t bucketCount() const {
- return mBucketCount;
- }
-
- /* Returns the load factor of the hashtable. */
- inline float loadFactor() const {
- return mLoadFactor;
- };
-
- /* Returns a const reference to the entry at the specified index.
- *
- * index: The index of the entry to retrieve. Must be a valid index within
- * the bounds of the hashtable.
- */
- inline const TEntry& entryAt(size_t index) const {
- return entryFor(bucketAt(mBuckets, index));
- }
-
- /* Returns a non-const reference to the entry at the specified index.
- *
- * index: The index of the entry to edit. Must be a valid index within
- * the bounds of the hashtable.
- */
- inline TEntry& editEntryAt(size_t index) {
- edit();
- return entryFor(bucketAt(mBuckets, index));
- }
-
- /* Clears the hashtable.
- * All entries in the hashtable are destroyed immediately.
- * If you need to do something special with the entries in the hashtable then iterate
- * over them and do what you need before clearing the hashtable.
- */
- inline void clear() {
- BasicHashtableImpl::clear();
- }
-
- /* Returns the index of the next entry in the hashtable given the index of a previous entry.
- * If the given index is -1, then returns the index of the first entry in the hashtable,
- * if there is one, or -1 otherwise.
- * If the given index is not -1, then returns the index of the next entry in the hashtable,
- * in strictly increasing order, or -1 if there are none left.
- *
- * index: The index of the previous entry that was iterated, or -1 to begin
- * iteration at the beginning of the hashtable.
- */
- inline ssize_t next(ssize_t index) const {
- return BasicHashtableImpl::next(index);
- }
-
- /* Finds the index of an entry with the specified key.
- * If the given index is -1, then returns the index of the first matching entry,
- * otherwise returns the index of the next matching entry.
- * If the hashtable contains multiple entries with keys that match the requested
- * key, then the sequence of entries returned is arbitrary.
- * Returns -1 if no entry was found.
- *
- * index: The index of the previous entry with the specified key, or -1 to
- * find the first matching entry.
- * hash: The hashcode of the key.
- * key: The key.
- */
- inline ssize_t find(ssize_t index, hash_t hash, const TKey& key) const {
- return BasicHashtableImpl::find(index, hash, &key);
- }
-
- /* Adds the entry to the hashtable.
- * Returns the index of the newly added entry.
- * If an entry with the same key already exists, then a duplicate entry is added.
- * If the entry will not fit, then the hashtable's capacity is increased and
- * its contents are rehashed. See rehash().
- *
- * hash: The hashcode of the key.
- * entry: The entry to add.
- */
- inline size_t add(hash_t hash, const TEntry& entry) {
- return BasicHashtableImpl::add(hash, &entry);
- }
-
- /* Removes the entry with the specified index from the hashtable.
- * The entry is destroyed immediately.
- * The index must be valid.
- *
- * The hashtable is not compacted after an item is removed, so it is legal
- * to continue iterating over the hashtable using next() or find().
- *
- * index: The index of the entry to remove. Must be a valid index within the
- * bounds of the hashtable, and it must refer to an existing entry.
- */
- inline void removeAt(size_t index) {
- BasicHashtableImpl::removeAt(index);
- }
-
- /* Rehashes the contents of the hashtable.
- * Grows the hashtable to at least the specified minimum capacity or the
- * current number of elements, whichever is larger.
- *
- * Rehashing causes all entries to be copied and the entry indices may change.
- * Although the hash codes are cached by the hashtable, rehashing can be an
- * expensive operation and should be avoided unless the hashtable's size
- * needs to be changed.
- *
- * Rehashing is the only way to change the capacity or load factor of the
- * hashtable once it has been created. It can be used to compact the
- * hashtable by choosing a minimum capacity that is smaller than the current
- * capacity (such as 0).
- *
- * minimumCapacity: The desired minimum capacity after rehashing.
- * loadFactor: The desired load factor after rehashing.
- */
- inline void rehash(size_t minimumCapacity, float loadFactor) {
- BasicHashtableImpl::rehash(minimumCapacity, loadFactor);
- }
-
- /* Determines whether there is room to add another entry without rehashing.
- * When this returns true, a subsequent add() operation is guaranteed to
- * complete without performing a rehash.
- */
- inline bool hasMoreRoom() const {
- return mCapacity > mFilledBuckets;
- }
-
-protected:
- static inline const TEntry& entryFor(const Bucket& bucket) {
- return reinterpret_cast<const TEntry&>(bucket.entry);
- }
-
- static inline TEntry& entryFor(Bucket& bucket) {
- return reinterpret_cast<TEntry&>(bucket.entry);
- }
-
- virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const;
- virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const;
- virtual void destroyBucketEntry(Bucket& bucket) const;
-
-private:
- // For dumping the raw contents of a hashtable during testing.
- friend class BasicHashtableTest;
- inline uint32_t cookieAt(size_t index) const {
- return bucketAt(mBuckets, index).cookie;
- }
-};
-
-template <typename TKey, typename TEntry>
-BasicHashtable<TKey, TEntry>::BasicHashtable(size_t minimumInitialCapacity, float loadFactor) :
- BasicHashtableImpl(sizeof(TEntry), traits<TEntry>::has_trivial_dtor,
- minimumInitialCapacity, loadFactor) {
-}
-
-template <typename TKey, typename TEntry>
-BasicHashtable<TKey, TEntry>::BasicHashtable(const BasicHashtable<TKey, TEntry>& other) :
- BasicHashtableImpl(other) {
-}
-
-template <typename TKey, typename TEntry>
-BasicHashtable<TKey, TEntry>::~BasicHashtable() {
- dispose();
-}
-
-template <typename TKey, typename TEntry>
-bool BasicHashtable<TKey, TEntry>::compareBucketKey(const Bucket& bucket,
- const void* __restrict__ key) const {
- return entryFor(bucket).getKey() == *static_cast<const TKey*>(key);
-}
-
-template <typename TKey, typename TEntry>
-void BasicHashtable<TKey, TEntry>::initializeBucketEntry(Bucket& bucket,
- const void* __restrict__ entry) const {
- if (!traits<TEntry>::has_trivial_copy) {
- new (&entryFor(bucket)) TEntry(*(static_cast<const TEntry*>(entry)));
- } else {
- memcpy(&entryFor(bucket), entry, sizeof(TEntry));
- }
-}
-
-template <typename TKey, typename TEntry>
-void BasicHashtable<TKey, TEntry>::destroyBucketEntry(Bucket& bucket) const {
- if (!traits<TEntry>::has_trivial_dtor) {
- entryFor(bucket).~TEntry();
- }
-}
-
-}; // namespace android
-
-#endif // ANDROID_BASIC_HASHTABLE_H
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h
deleted file mode 100644
index 7d621e4..0000000
--- a/include/utils/BlobCache.h
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- ** Copyright 2011, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT 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_BLOB_CACHE_H
-#define ANDROID_BLOB_CACHE_H
-
-#include <stddef.h>
-
-#include <utils/Flattenable.h>
-#include <utils/RefBase.h>
-#include <utils/SortedVector.h>
-#include <utils/threads.h>
-
-namespace android {
-
-// A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache
-// does NOT provide any thread-safety guarantees.
-//
-// The cache contents can be serialized to an in-memory buffer or mmap'd file
-// and then reloaded in a subsequent execution of the program. This
-// serialization is non-portable and the data should only be used by the device
-// that generated it.
-class BlobCache : public RefBase {
-
-public:
-
- // Create an empty blob cache. The blob cache will cache key/value pairs
- // with key and value sizes less than or equal to maxKeySize and
- // maxValueSize, respectively. The total combined size of ALL cache entries
- // (key sizes plus value sizes) will not exceed maxTotalSize.
- BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
-
- // set inserts a new binary value into the cache and associates it with the
- // given binary key. If the key or value are too large for the cache then
- // the cache remains unchanged. This includes the case where a different
- // value was previously associated with the given key - the old value will
- // remain in the cache. If the given key and value are small enough to be
- // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
- // values specified to the BlobCache constructor), then the key/value pair
- // will be in the cache after set returns. Note, however, that a subsequent
- // call to set may evict old key/value pairs from the cache.
- //
- // Preconditions:
- // key != NULL
- // 0 < keySize
- // value != NULL
- // 0 < valueSize
- void set(const void* key, size_t keySize, const void* value,
- size_t valueSize);
-
- // get retrieves from the cache the binary value associated with a given
- // binary key. If the key is present in the cache then the length of the
- // binary value associated with that key is returned. If the value argument
- // is non-NULL and the size of the cached value is less than valueSize bytes
- // then the cached value is copied into the buffer pointed to by the value
- // argument. If the key is not present in the cache then 0 is returned and
- // the buffer pointed to by the value argument is not modified.
- //
- // Note that when calling get multiple times with the same key, the later
- // calls may fail, returning 0, even if earlier calls succeeded. The return
- // value must be checked for each call.
- //
- // Preconditions:
- // key != NULL
- // 0 < keySize
- // 0 <= valueSize
- size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
-
-
- // getFlattenedSize returns the number of bytes needed to store the entire
- // serialized cache.
- size_t getFlattenedSize() const;
-
- // flatten serializes the current contents of the cache into the memory
- // pointed to by 'buffer'. The serialized cache contents can later be
- // loaded into a BlobCache object using the unflatten method. The contents
- // of the BlobCache object will not be modified.
- //
- // Preconditions:
- // size >= this.getFlattenedSize()
- status_t flatten(void* buffer, size_t size) const;
-
- // unflatten replaces the contents of the cache with the serialized cache
- // contents in the memory pointed to by 'buffer'. The previous contents of
- // the BlobCache will be evicted from the cache. If an error occurs while
- // unflattening the serialized cache contents then the BlobCache will be
- // left in an empty state.
- //
- status_t unflatten(void const* buffer, size_t size);
-
-private:
- // Copying is disallowed.
- BlobCache(const BlobCache&);
- void operator=(const BlobCache&);
-
- // A random function helper to get around MinGW not having nrand48()
- long int blob_random();
-
- // clean evicts a randomly chosen set of entries from the cache such that
- // the total size of all remaining entries is less than mMaxTotalSize/2.
- void clean();
-
- // isCleanable returns true if the cache is full enough for the clean method
- // to have some effect, and false otherwise.
- bool isCleanable() const;
-
- // A Blob is an immutable sized unstructured data blob.
- class Blob : public RefBase {
- public:
- Blob(const void* data, size_t size, bool copyData);
- ~Blob();
-
- bool operator<(const Blob& rhs) const;
-
- const void* getData() const;
- size_t getSize() const;
-
- private:
- // Copying is not allowed.
- Blob(const Blob&);
- void operator=(const Blob&);
-
- // mData points to the buffer containing the blob data.
- const void* mData;
-
- // mSize is the size of the blob data in bytes.
- size_t mSize;
-
- // mOwnsData indicates whether or not this Blob object should free the
- // memory pointed to by mData when the Blob gets destructed.
- bool mOwnsData;
- };
-
- // A CacheEntry is a single key/value pair in the cache.
- class CacheEntry {
- public:
- CacheEntry();
- CacheEntry(const sp<Blob>& key, const sp<Blob>& value);
- CacheEntry(const CacheEntry& ce);
-
- bool operator<(const CacheEntry& rhs) const;
- const CacheEntry& operator=(const CacheEntry&);
-
- sp<Blob> getKey() const;
- sp<Blob> getValue() const;
-
- void setValue(const sp<Blob>& value);
-
- private:
-
- // mKey is the key that identifies the cache entry.
- sp<Blob> mKey;
-
- // mValue is the cached data associated with the key.
- sp<Blob> mValue;
- };
-
- // A Header is the header for the entire BlobCache serialization format. No
- // need to make this portable, so we simply write the struct out.
- struct Header {
- // mMagicNumber is the magic number that identifies the data as
- // serialized BlobCache contents. It must always contain 'Blb$'.
- uint32_t mMagicNumber;
-
- // mBlobCacheVersion is the serialization format version.
- uint32_t mBlobCacheVersion;
-
- // mDeviceVersion is the device-specific version of the cache. This can
- // be used to invalidate the cache.
- uint32_t mDeviceVersion;
-
- // mNumEntries is number of cache entries following the header in the
- // data.
- size_t mNumEntries;
- };
-
- // An EntryHeader is the header for a serialized cache entry. No need to
- // make this portable, so we simply write the struct out. Each EntryHeader
- // is followed imediately by the key data and then the value data.
- //
- // The beginning of each serialized EntryHeader is 4-byte aligned, so the
- // number of bytes that a serialized cache entry will occupy is:
- //
- // ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
- //
- struct EntryHeader {
- // mKeySize is the size of the entry key in bytes.
- size_t mKeySize;
-
- // mValueSize is the size of the entry value in bytes.
- size_t mValueSize;
-
- // mData contains both the key and value data for the cache entry. The
- // key comes first followed immediately by the value.
- uint8_t mData[];
- };
-
- // mMaxKeySize is the maximum key size that will be cached. Calls to
- // BlobCache::set with a keySize parameter larger than mMaxKeySize will
- // simply not add the key/value pair to the cache.
- const size_t mMaxKeySize;
-
- // mMaxValueSize is the maximum value size that will be cached. Calls to
- // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
- // simply not add the key/value pair to the cache.
- const size_t mMaxValueSize;
-
- // mMaxTotalSize is the maximum size that all cache entries can occupy. This
- // includes space for both keys and values. When a call to BlobCache::set
- // would otherwise cause this limit to be exceeded, either the key/value
- // pair passed to BlobCache::set will not be cached or other cache entries
- // will be evicted from the cache to make room for the new entry.
- const size_t mMaxTotalSize;
-
- // mTotalSize is the total combined size of all keys and values currently in
- // the cache.
- size_t mTotalSize;
-
- // mRandState is the pseudo-random number generator state. It is passed to
- // nrand48 to generate random numbers when needed.
- unsigned short mRandState[3];
-
- // mCacheEntries stores all the cache entries that are resident in memory.
- // Cache entries are added to it by the 'set' method.
- SortedVector<CacheEntry> mCacheEntries;
-};
-
-}
-
-#endif // ANDROID_BLOB_CACHE_H
diff --git a/include/utils/Compat.h b/include/utils/Compat.h
deleted file mode 100644
index c5f9d6a..0000000
--- a/include/utils/Compat.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LIB_UTILS_COMPAT_H
-#define __LIB_UTILS_COMPAT_H
-
-#include <unistd.h>
-
-#if defined(__APPLE__)
-
-/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-
-typedef off_t off64_t;
-
-static inline off64_t lseek64(int fd, off64_t offset, int whence) {
- return lseek(fd, offset, whence);
-}
-
-static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) {
- return pread(fd, buf, nbytes, offset);
-}
-
-#endif /* __APPLE__ */
-
-#if defined(_WIN32)
-#define O_CLOEXEC O_NOINHERIT
-#define O_NOFOLLOW 0
-#define DEFFILEMODE 0666
-#endif /* _WIN32 */
-
-#if defined(_WIN32)
-#define ZD "%ld"
-#define ZD_TYPE long
-#else
-#define ZD "%zd"
-#define ZD_TYPE ssize_t
-#endif
-
-/*
- * Needed for cases where something should be constexpr if possible, but not
- * being constexpr is fine if in pre-C++11 code (such as a const static float
- * member variable).
- */
-#if __cplusplus >= 201103L
-#define CONSTEXPR constexpr
-#else
-#define CONSTEXPR
-#endif
-
-/*
- * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
- * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
- * not already defined, then define it here.
- */
-#ifndef TEMP_FAILURE_RETRY
-/* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp) ({ \
- typeof (exp) _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; })
-#endif
-
-#if defined(_WIN32)
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
-
-#endif /* __LIB_UTILS_COMPAT_H */
diff --git a/include/utils/Condition.h b/include/utils/Condition.h
deleted file mode 100644
index 5a72519..0000000
--- a/include/utils/Condition.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_UTILS_CONDITION_H
-#define _LIBS_UTILS_CONDITION_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <time.h>
-
-#if !defined(_WIN32)
-# include <pthread.h>
-#endif
-
-#include <utils/Errors.h>
-#include <utils/Mutex.h>
-#include <utils/Timers.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-/*
- * Condition variable class. The implementation is system-dependent.
- *
- * Condition variables are paired up with mutexes. Lock the mutex,
- * call wait(), then either re-wait() if things aren't quite what you want,
- * or unlock the mutex and continue. All threads calling wait() must
- * use the same mutex for a given Condition.
- */
-class Condition {
-public:
- enum {
- PRIVATE = 0,
- SHARED = 1
- };
-
- enum WakeUpType {
- WAKE_UP_ONE = 0,
- WAKE_UP_ALL = 1
- };
-
- Condition();
- Condition(int type);
- ~Condition();
- // Wait on the condition variable. Lock the mutex before calling.
- status_t wait(Mutex& mutex);
- // same with relative timeout
- status_t waitRelative(Mutex& mutex, nsecs_t reltime);
- // Signal the condition variable, allowing exactly one thread to continue.
- void signal();
- // Signal the condition variable, allowing one or all threads to continue.
- void signal(WakeUpType type) {
- if (type == WAKE_UP_ONE) {
- signal();
- } else {
- broadcast();
- }
- }
- // Signal the condition variable, allowing all threads to continue.
- void broadcast();
-
-private:
-#if !defined(_WIN32)
- pthread_cond_t mCond;
-#else
- void* mState;
-#endif
-};
-
-// ---------------------------------------------------------------------------
-
-#if !defined(_WIN32)
-
-inline Condition::Condition() {
- pthread_cond_init(&mCond, NULL);
-}
-inline Condition::Condition(int type) {
- if (type == SHARED) {
- pthread_condattr_t attr;
- pthread_condattr_init(&attr);
- pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
- pthread_cond_init(&mCond, &attr);
- pthread_condattr_destroy(&attr);
- } else {
- pthread_cond_init(&mCond, NULL);
- }
-}
-inline Condition::~Condition() {
- pthread_cond_destroy(&mCond);
-}
-inline status_t Condition::wait(Mutex& mutex) {
- return -pthread_cond_wait(&mCond, &mutex.mMutex);
-}
-inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
-#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
- struct timespec ts;
- ts.tv_sec = reltime/1000000000;
- ts.tv_nsec = reltime%1000000000;
- return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts);
-#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
- struct timespec ts;
-#if defined(__linux__)
- clock_gettime(CLOCK_REALTIME, &ts);
-#else // __APPLE__
- // we don't support the clocks here.
- struct timeval t;
- gettimeofday(&t, NULL);
- ts.tv_sec = t.tv_sec;
- ts.tv_nsec= t.tv_usec*1000;
-#endif
- ts.tv_sec += reltime/1000000000;
- ts.tv_nsec+= reltime%1000000000;
- if (ts.tv_nsec >= 1000000000) {
- ts.tv_nsec -= 1000000000;
- ts.tv_sec += 1;
- }
- return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
-#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
-}
-inline void Condition::signal() {
- /*
- * POSIX says pthread_cond_signal wakes up "one or more" waiting threads.
- * However bionic follows the glibc guarantee which wakes up "exactly one"
- * waiting thread.
- *
- * man 3 pthread_cond_signal
- * pthread_cond_signal restarts one of the threads that are waiting on
- * the condition variable cond. If no threads are waiting on cond,
- * nothing happens. If several threads are waiting on cond, exactly one
- * is restarted, but it is not specified which.
- */
- pthread_cond_signal(&mCond);
-}
-inline void Condition::broadcast() {
- pthread_cond_broadcast(&mCond);
-}
-
-#endif // !defined(_WIN32)
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // _LIBS_UTILS_CONDITON_H
diff --git a/include/utils/Errors.h b/include/utils/Errors.h
deleted file mode 100644
index 08ddcd2..0000000
--- a/include/utils/Errors.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ERRORS_H
-#define ANDROID_ERRORS_H
-
-#include <sys/types.h>
-#include <errno.h>
-
-namespace android {
-
-// use this type to return error codes
-#ifdef _WIN32
-typedef int status_t;
-#else
-typedef int32_t status_t;
-#endif
-
-/* the MS C runtime lacks a few error codes */
-
-/*
- * Error codes.
- * All error codes are negative values.
- */
-
-// Win32 #defines NO_ERROR as well. It has the same value, so there's no
-// real conflict, though it's a bit awkward.
-#ifdef _WIN32
-# undef NO_ERROR
-#endif
-
-enum {
- OK = 0, // Everything's swell.
- NO_ERROR = 0, // No errors.
-
- UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value
-
- NO_MEMORY = -ENOMEM,
- INVALID_OPERATION = -ENOSYS,
- BAD_VALUE = -EINVAL,
- BAD_TYPE = (UNKNOWN_ERROR + 1),
- NAME_NOT_FOUND = -ENOENT,
- PERMISSION_DENIED = -EPERM,
- NO_INIT = -ENODEV,
- ALREADY_EXISTS = -EEXIST,
- DEAD_OBJECT = -EPIPE,
- FAILED_TRANSACTION = (UNKNOWN_ERROR + 2),
-#if !defined(_WIN32)
- BAD_INDEX = -EOVERFLOW,
- NOT_ENOUGH_DATA = -ENODATA,
- WOULD_BLOCK = -EWOULDBLOCK,
- TIMED_OUT = -ETIMEDOUT,
- UNKNOWN_TRANSACTION = -EBADMSG,
-#else
- BAD_INDEX = -E2BIG,
- NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3),
- WOULD_BLOCK = (UNKNOWN_ERROR + 4),
- TIMED_OUT = (UNKNOWN_ERROR + 5),
- UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
-#endif
- FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7),
-};
-
-// Restore define; enumeration is in "android" namespace, so the value defined
-// there won't work for Win32 code in a different namespace.
-#ifdef _WIN32
-# define NO_ERROR 0L
-#endif
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_ERRORS_H
diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h
deleted file mode 100644
index afd7bfd..0000000
--- a/include/utils/FileMap.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Encapsulate a shared file mapping.
-//
-#ifndef __LIBS_FILE_MAP_H
-#define __LIBS_FILE_MAP_H
-
-#include <sys/types.h>
-
-#include <utils/Compat.h>
-
-#if defined(__MINGW32__)
-// Ensure that we always pull in winsock2.h before windows.h
-#if defined(_WIN32)
-#include <winsock2.h>
-#endif
-#include <windows.h>
-#endif
-
-namespace android {
-
-/*
- * This represents a memory-mapped file. It might be the entire file or
- * only part of it. This requires a little bookkeeping because the mapping
- * needs to be aligned on page boundaries, and in some cases we'd like to
- * have multiple references to the mapped area without creating additional
- * maps.
- *
- * This always uses MAP_SHARED.
- *
- * TODO: we should be able to create a new FileMap that is a subset of
- * an existing FileMap and shares the underlying mapped pages. Requires
- * completing the refcounting stuff and possibly introducing the notion
- * of a FileMap hierarchy.
- */
-class FileMap {
-public:
- FileMap(void);
-
- /*
- * Create a new mapping on an open file.
- *
- * Closing the file descriptor does not unmap the pages, so we don't
- * claim ownership of the fd.
- *
- * Returns "false" on failure.
- */
- bool create(const char* origFileName, int fd,
- off64_t offset, size_t length, bool readOnly);
-
- ~FileMap(void);
-
- /*
- * Return the name of the file this map came from, if known.
- */
- const char* getFileName(void) const { return mFileName; }
-
- /*
- * Get a pointer to the piece of the file we requested.
- */
- void* getDataPtr(void) const { return mDataPtr; }
-
- /*
- * Get the length we requested.
- */
- size_t getDataLength(void) const { return mDataLength; }
-
- /*
- * Get the data offset used to create this map.
- */
- off64_t getDataOffset(void) const { return mDataOffset; }
-
- /*
- * This maps directly to madvise() values, but allows us to avoid
- * including <sys/mman.h> everywhere.
- */
- enum MapAdvice {
- NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED
- };
-
- /*
- * Apply an madvise() call to the entire file.
- *
- * Returns 0 on success, -1 on failure.
- */
- int advise(MapAdvice advice);
-
-protected:
-
-private:
- // these are not implemented
- FileMap(const FileMap& src);
- const FileMap& operator=(const FileMap& src);
-
- char* mFileName; // original file name, if known
- void* mBasePtr; // base of mmap area; page aligned
- size_t mBaseLength; // length, measured from "mBasePtr"
- off64_t mDataOffset; // offset used when map was created
- void* mDataPtr; // start of requested data, offset from base
- size_t mDataLength; // length, measured from "mDataPtr"
-#if defined(__MINGW32__)
- HANDLE mFileHandle; // Win32 file handle
- HANDLE mFileMapping; // Win32 file mapping handle
-#endif
-
- static long mPageSize;
-};
-
-}; // namespace android
-
-#endif // __LIBS_FILE_MAP_H
diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h
deleted file mode 100644
index 882a8b2..0000000
--- a/include/utils/Flattenable.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_UTILS_FLATTENABLE_H
-#define ANDROID_UTILS_FLATTENABLE_H
-
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Errors.h>
-#include <utils/Debug.h>
-
-namespace android {
-
-
-class FlattenableUtils {
-public:
- template<int N>
- static size_t align(size_t size) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
- return (size + (N-1)) & ~(N-1);
- }
-
- template<int N>
- static size_t align(void const*& buffer) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
- intptr_t b = intptr_t(buffer);
- buffer = (void*)((intptr_t(buffer) + (N-1)) & ~(N-1));
- return size_t(intptr_t(buffer) - b);
- }
-
- template<int N>
- static size_t align(void*& buffer) {
- return align<N>( const_cast<void const*&>(buffer) );
- }
-
- static void advance(void*& buffer, size_t& size, size_t offset) {
- buffer = reinterpret_cast<void*>( intptr_t(buffer) + offset );
- size -= offset;
- }
-
- static void advance(void const*& buffer, size_t& size, size_t offset) {
- buffer = reinterpret_cast<void const*>( intptr_t(buffer) + offset );
- size -= offset;
- }
-
- // write a POD structure
- template<typename T>
- static void write(void*& buffer, size_t& size, const T& value) {
- *static_cast<T*>(buffer) = value;
- advance(buffer, size, sizeof(T));
- }
-
- // read a POD structure
- template<typename T>
- static void read(void const*& buffer, size_t& size, T& value) {
- value = *static_cast<T const*>(buffer);
- advance(buffer, size, sizeof(T));
- }
-};
-
-
-/*
- * The Flattenable protocol allows an object to serialize itself out
- * to a byte-buffer and an array of file descriptors.
- * Flattenable objects must implement this protocol.
- */
-
-template <typename T>
-class Flattenable {
-public:
- // size in bytes of the flattened object
- inline size_t getFlattenedSize() const;
-
- // number of file descriptors to flatten
- inline size_t getFdCount() const;
-
- // flattens the object into buffer.
- // size should be at least of getFlattenedSize()
- // file descriptors are written in the fds[] array but ownership is
- // not transfered (ie: they must be dupped by the caller of
- // flatten() if needed).
- inline status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
-
- // unflattens the object from buffer.
- // size should be equal to the value of getFlattenedSize() when the
- // object was flattened.
- // unflattened file descriptors are found in the fds[] array and
- // don't need to be dupped(). ie: the caller of unflatten doesn't
- // keep ownership. If a fd is not retained by unflatten() it must be
- // explicitly closed.
- inline status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
-};
-
-template<typename T>
-inline size_t Flattenable<T>::getFlattenedSize() const {
- return static_cast<T const*>(this)->T::getFlattenedSize();
-}
-template<typename T>
-inline size_t Flattenable<T>::getFdCount() const {
- return static_cast<T const*>(this)->T::getFdCount();
-}
-template<typename T>
-inline status_t Flattenable<T>::flatten(
- void*& buffer, size_t& size, int*& fds, size_t& count) const {
- return static_cast<T const*>(this)->T::flatten(buffer, size, fds, count);
-}
-template<typename T>
-inline status_t Flattenable<T>::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count) {
- return static_cast<T*>(this)->T::unflatten(buffer, size, fds, count);
-}
-
-/*
- * LightFlattenable is a protocol allowing object to serialize themselves out
- * to a byte-buffer. Because it doesn't handle file-descriptors,
- * LightFlattenable is usually more size efficient than Flattenable.
- * LightFlattenable objects must implement this protocol.
- */
-template <typename T>
-class LightFlattenable {
-public:
- // returns whether this object always flatten into the same size.
- // for efficiency, this should always be inline.
- inline bool isFixedSize() const;
-
- // returns size in bytes of the flattened object. must be a constant.
- inline size_t getFlattenedSize() const;
-
- // flattens the object into buffer.
- inline status_t flatten(void* buffer, size_t size) const;
-
- // unflattens the object from buffer of given size.
- inline status_t unflatten(void const* buffer, size_t size);
-};
-
-template <typename T>
-inline bool LightFlattenable<T>::isFixedSize() const {
- return static_cast<T const*>(this)->T::isFixedSize();
-}
-template <typename T>
-inline size_t LightFlattenable<T>::getFlattenedSize() const {
- return static_cast<T const*>(this)->T::getFlattenedSize();
-}
-template <typename T>
-inline status_t LightFlattenable<T>::flatten(void* buffer, size_t size) const {
- return static_cast<T const*>(this)->T::flatten(buffer, size);
-}
-template <typename T>
-inline status_t LightFlattenable<T>::unflatten(void const* buffer, size_t size) {
- return static_cast<T*>(this)->T::unflatten(buffer, size);
-}
-
-/*
- * LightFlattenablePod is an implementation of the LightFlattenable protocol
- * for POD (plain-old-data) objects.
- * Simply derive from LightFlattenablePod<Foo> to make Foo flattenable; no
- * need to implement any methods; obviously Foo must be a POD structure.
- */
-template <typename T>
-class LightFlattenablePod : public LightFlattenable<T> {
-public:
- inline bool isFixedSize() const {
- return true;
- }
-
- inline size_t getFlattenedSize() const {
- return sizeof(T);
- }
- inline status_t flatten(void* buffer, size_t size) const {
- if (size < sizeof(T)) return NO_MEMORY;
- *reinterpret_cast<T*>(buffer) = *static_cast<T const*>(this);
- return NO_ERROR;
- }
- inline status_t unflatten(void const* buffer, size_t) {
- *static_cast<T*>(this) = *reinterpret_cast<T const*>(buffer);
- return NO_ERROR;
- }
-};
-
-
-}; // namespace android
-
-
-#endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/include/utils/JenkinsHash.h b/include/utils/JenkinsHash.h
deleted file mode 100644
index 7da5dbd..0000000
--- a/include/utils/JenkinsHash.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* Implementation of Jenkins one-at-a-time hash function. These choices are
- * optimized for code size and portability, rather than raw speed. But speed
- * should still be quite good.
- **/
-
-#ifndef ANDROID_JENKINS_HASH_H
-#define ANDROID_JENKINS_HASH_H
-
-#include <utils/TypeHelpers.h>
-
-namespace android {
-
-/* The Jenkins hash of a sequence of 32 bit words A, B, C is:
- * Whiten(Mix(Mix(Mix(0, A), B), C)) */
-
-inline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) {
- hash += data;
- hash += (hash << 10);
- hash ^= (hash >> 6);
- return hash;
-}
-
-hash_t JenkinsHashWhiten(uint32_t hash);
-
-/* Helpful utility functions for hashing data in 32 bit chunks */
-uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size);
-
-uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size);
-
-}
-
-#endif // ANDROID_JENKINS_HASH_H
diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h
deleted file mode 100644
index c4faae0..0000000
--- a/include/utils/KeyedVector.h
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_KEYED_VECTOR_H
-#define ANDROID_KEYED_VECTOR_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <cutils/log.h>
-
-#include <utils/SortedVector.h>
-#include <utils/TypeHelpers.h>
-#include <utils/Errors.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-template <typename KEY, typename VALUE>
-class KeyedVector
-{
-public:
- typedef KEY key_type;
- typedef VALUE value_type;
-
- inline KeyedVector();
-
- /*
- * empty the vector
- */
-
- inline void clear() { mVector.clear(); }
-
- /*!
- * vector stats
- */
-
- //! returns number of items in the vector
- inline size_t size() const { return mVector.size(); }
- //! returns whether or not the vector is empty
- inline bool isEmpty() const { return mVector.isEmpty(); }
- //! returns how many items can be stored without reallocating the backing store
- inline size_t capacity() const { return mVector.capacity(); }
- //! sets the capacity. capacity can never be reduced less than size()
- inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); }
-
- // returns true if the arguments is known to be identical to this vector
- inline bool isIdenticalTo(const KeyedVector& rhs) const;
-
- /*!
- * accessors
- */
- const VALUE& valueFor(const KEY& key) const;
- const VALUE& valueAt(size_t index) const;
- const KEY& keyAt(size_t index) const;
- ssize_t indexOfKey(const KEY& key) const;
- const VALUE& operator[] (size_t index) const;
-
- /*!
- * modifying the array
- */
-
- VALUE& editValueFor(const KEY& key);
- VALUE& editValueAt(size_t index);
-
- /*!
- * add/insert/replace items
- */
-
- ssize_t add(const KEY& key, const VALUE& item);
- ssize_t replaceValueFor(const KEY& key, const VALUE& item);
- ssize_t replaceValueAt(size_t index, const VALUE& item);
-
- /*!
- * remove items
- */
-
- ssize_t removeItem(const KEY& key);
- ssize_t removeItemsAt(size_t index, size_t count = 1);
-
-private:
- SortedVector< key_value_pair_t<KEY, VALUE> > mVector;
-};
-
-// KeyedVector<KEY, VALUE> can be trivially moved using memcpy() because its
-// underlying SortedVector can be trivially moved.
-template<typename KEY, typename VALUE> struct trait_trivial_move<KeyedVector<KEY, VALUE> > {
- enum { value = trait_trivial_move<SortedVector< key_value_pair_t<KEY, VALUE> > >::value };
-};
-
-
-// ---------------------------------------------------------------------------
-
-/**
- * Variation of KeyedVector that holds a default value to return when
- * valueFor() is called with a key that doesn't exist.
- */
-template <typename KEY, typename VALUE>
-class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
-{
-public:
- inline DefaultKeyedVector(const VALUE& defValue = VALUE());
- const VALUE& valueFor(const KEY& key) const;
-
-private:
- VALUE mDefault;
-};
-
-// ---------------------------------------------------------------------------
-
-template<typename KEY, typename VALUE> inline
-KeyedVector<KEY,VALUE>::KeyedVector()
-{
-}
-
-template<typename KEY, typename VALUE> inline
-bool KeyedVector<KEY,VALUE>::isIdenticalTo(const KeyedVector<KEY,VALUE>& rhs) const {
- return mVector.array() == rhs.mVector.array();
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
- return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
- ssize_t i = this->indexOfKey(key);
- LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__);
- return mVector.itemAt(i).value;
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
- return mVector.itemAt(index).value;
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& KeyedVector<KEY,VALUE>::operator[] (size_t index) const {
- return valueAt(index);
-}
-
-template<typename KEY, typename VALUE> inline
-const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
- return mVector.itemAt(index).key;
-}
-
-template<typename KEY, typename VALUE> inline
-VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
- ssize_t i = this->indexOfKey(key);
- LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__);
- return mVector.editItemAt(i).value;
-}
-
-template<typename KEY, typename VALUE> inline
-VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
- return mVector.editItemAt(index).value;
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
- return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
- key_value_pair_t<KEY,VALUE> pair(key, value);
- mVector.remove(pair);
- return mVector.add(pair);
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
- if (index<size()) {
- mVector.editItemAt(index).value = item;
- return index;
- }
- return BAD_INDEX;
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
- return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
- return mVector.removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<typename KEY, typename VALUE> inline
-DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
- : mDefault(defValue)
-{
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
- ssize_t i = this->indexOfKey(key);
- return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
-}
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_KEYED_VECTOR_H
diff --git a/include/utils/LinearAllocator.h b/include/utils/LinearAllocator.h
deleted file mode 100644
index 4772bc8..0000000
--- a/include/utils/LinearAllocator.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * 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 ``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.
- */
-
-#ifndef ANDROID_LINEARALLOCATOR_H
-#define ANDROID_LINEARALLOCATOR_H
-
-#include <stddef.h>
-
-namespace android {
-
-/**
- * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids
- * the overhead of malloc when many objects are allocated. It is most useful when creating many
- * small objects with a similar lifetime, and doesn't add significant overhead for large
- * allocations.
- */
-class LinearAllocator {
-public:
- LinearAllocator();
- ~LinearAllocator();
-
- /**
- * Reserves and returns a region of memory of at least size 'size', aligning as needed.
- * Typically this is used in an object's overridden new() method or as a replacement for malloc.
- *
- * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
- * delete() on an object stored in a buffer is needed, it should be overridden to use
- * rewindIfLastAlloc()
- */
- void* alloc(size_t size);
-
- /**
- * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its
- * state if possible. No destructors are called.
- */
- void rewindIfLastAlloc(void* ptr, size_t allocSize);
-
- /**
- * Dump memory usage statistics to the log (allocated and wasted space)
- */
- void dumpMemoryStats(const char* prefix = "");
-
- /**
- * The number of bytes used for buffers allocated in the LinearAllocator (does not count space
- * wasted)
- */
- size_t usedSize() const { return mTotalAllocated - mWastedSpace; }
-
-private:
- LinearAllocator(const LinearAllocator& other);
-
- class Page;
-
- Page* newPage(size_t pageSize);
- bool fitsInCurrentPage(size_t size);
- void ensureNext(size_t size);
- void* start(Page *p);
- void* end(Page* p);
-
- size_t mPageSize;
- size_t mMaxAllocSize;
- void* mNext;
- Page* mCurrentPage;
- Page* mPages;
-
- // Memory usage tracking
- size_t mTotalAllocated;
- size_t mWastedSpace;
- size_t mPageCount;
- size_t mDedicatedPageCount;
-};
-
-}; // namespace android
-
-#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/include/utils/Log.h b/include/utils/Log.h
deleted file mode 100644
index 4259c86..0000000
--- a/include/utils/Log.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// C/C++ logging functions. See the logging documentation for API details.
-//
-// We'd like these to be available from C code (in case we import some from
-// somewhere), so this has a C interface.
-//
-// The output will be correct when the log file is shared between multiple
-// threads and/or multiple processes so long as the operating system
-// supports O_APPEND. These calls have mutex-protected data structures
-// and so are NOT reentrant. Do not use LOG in a signal handler.
-//
-#ifndef _LIBS_UTILS_LOG_H
-#define _LIBS_UTILS_LOG_H
-
-#include <cutils/log.h>
-#include <sys/types.h>
-
-#ifdef __cplusplus
-
-namespace android {
-
-/*
- * A very simple utility that yells in the log when an operation takes too long.
- */
-class LogIfSlow {
-public:
- LogIfSlow(const char* tag, android_LogPriority priority,
- int timeoutMillis, const char* message);
- ~LogIfSlow();
-
-private:
- const char* const mTag;
- const android_LogPriority mPriority;
- const int mTimeoutMillis;
- const char* const mMessage;
- const int64_t mStart;
-};
-
-/*
- * Writes the specified debug log message if this block takes longer than the
- * specified number of milliseconds to run. Includes the time actually taken.
- *
- * {
- * ALOGD_IF_SLOW(50, "Excessive delay doing something.");
- * doSomething();
- * }
- */
-#define ALOGD_IF_SLOW(timeoutMillis, message) \
- android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message);
-
-} // namespace android
-
-#endif // __cplusplus
-
-#endif // _LIBS_UTILS_LOG_H
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
deleted file mode 100644
index 15c9891..0000000
--- a/include/utils/Looper.h
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UTILS_LOOPER_H
-#define UTILS_LOOPER_H
-
-#include <utils/threads.h>
-#include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
-#include <utils/Timers.h>
-
-#include <sys/epoll.h>
-
-namespace android {
-
-/*
- * NOTE: Since Looper is used to implement the NDK ALooper, the Looper
- * enums and the signature of Looper_callbackFunc need to align with
- * that implementation.
- */
-
-/**
- * For callback-based event loops, this is the prototype of the function
- * that is called when a file descriptor event occurs.
- * It is given the file descriptor it is associated with,
- * a bitmask of the poll events that were triggered (typically EVENT_INPUT),
- * and the data pointer that was originally supplied.
- *
- * Implementations should return 1 to continue receiving callbacks, or 0
- * to have this file descriptor and callback unregistered from the looper.
- */
-typedef int (*Looper_callbackFunc)(int fd, int events, void* data);
-
-/**
- * A message that can be posted to a Looper.
- */
-struct Message {
- Message() : what(0) { }
- Message(int what) : what(what) { }
-
- /* The message type. (interpretation is left up to the handler) */
- int what;
-};
-
-
-/**
- * Interface for a Looper message handler.
- *
- * The Looper holds a strong reference to the message handler whenever it has
- * a message to deliver to it. Make sure to call Looper::removeMessages
- * to remove any pending messages destined for the handler so that the handler
- * can be destroyed.
- */
-class MessageHandler : public virtual RefBase {
-protected:
- virtual ~MessageHandler() { }
-
-public:
- /**
- * Handles a message.
- */
- virtual void handleMessage(const Message& message) = 0;
-};
-
-
-/**
- * A simple proxy that holds a weak reference to a message handler.
- */
-class WeakMessageHandler : public MessageHandler {
-protected:
- virtual ~WeakMessageHandler();
-
-public:
- WeakMessageHandler(const wp<MessageHandler>& handler);
- virtual void handleMessage(const Message& message);
-
-private:
- wp<MessageHandler> mHandler;
-};
-
-
-/**
- * A looper callback.
- */
-class LooperCallback : public virtual RefBase {
-protected:
- virtual ~LooperCallback() { }
-
-public:
- /**
- * Handles a poll event for the given file descriptor.
- * It is given the file descriptor it is associated with,
- * a bitmask of the poll events that were triggered (typically EVENT_INPUT),
- * and the data pointer that was originally supplied.
- *
- * Implementations should return 1 to continue receiving callbacks, or 0
- * to have this file descriptor and callback unregistered from the looper.
- */
- virtual int handleEvent(int fd, int events, void* data) = 0;
-};
-
-/**
- * Wraps a Looper_callbackFunc function pointer.
- */
-class SimpleLooperCallback : public LooperCallback {
-protected:
- virtual ~SimpleLooperCallback();
-
-public:
- SimpleLooperCallback(Looper_callbackFunc callback);
- virtual int handleEvent(int fd, int events, void* data);
-
-private:
- Looper_callbackFunc mCallback;
-};
-
-/**
- * A polling loop that supports monitoring file descriptor events, optionally
- * using callbacks. The implementation uses epoll() internally.
- *
- * A looper can be associated with a thread although there is no requirement that it must be.
- */
-class Looper : public RefBase {
-protected:
- virtual ~Looper();
-
-public:
- enum {
- /**
- * Result from Looper_pollOnce() and Looper_pollAll():
- * The poll was awoken using wake() before the timeout expired
- * and no callbacks were executed and no other file descriptors were ready.
- */
- POLL_WAKE = -1,
-
- /**
- * Result from Looper_pollOnce() and Looper_pollAll():
- * One or more callbacks were executed.
- */
- POLL_CALLBACK = -2,
-
- /**
- * Result from Looper_pollOnce() and Looper_pollAll():
- * The timeout expired.
- */
- POLL_TIMEOUT = -3,
-
- /**
- * Result from Looper_pollOnce() and Looper_pollAll():
- * An error occurred.
- */
- POLL_ERROR = -4,
- };
-
- /**
- * Flags for file descriptor events that a looper can monitor.
- *
- * These flag bits can be combined to monitor multiple events at once.
- */
- enum {
- /**
- * The file descriptor is available for read operations.
- */
- EVENT_INPUT = 1 << 0,
-
- /**
- * The file descriptor is available for write operations.
- */
- EVENT_OUTPUT = 1 << 1,
-
- /**
- * The file descriptor has encountered an error condition.
- *
- * The looper always sends notifications about errors; it is not necessary
- * to specify this event flag in the requested event set.
- */
- EVENT_ERROR = 1 << 2,
-
- /**
- * The file descriptor was hung up.
- * For example, indicates that the remote end of a pipe or socket was closed.
- *
- * The looper always sends notifications about hangups; it is not necessary
- * to specify this event flag in the requested event set.
- */
- EVENT_HANGUP = 1 << 3,
-
- /**
- * The file descriptor is invalid.
- * For example, the file descriptor was closed prematurely.
- *
- * The looper always sends notifications about invalid file descriptors; it is not necessary
- * to specify this event flag in the requested event set.
- */
- EVENT_INVALID = 1 << 4,
- };
-
- enum {
- /**
- * Option for Looper_prepare: this looper will accept calls to
- * Looper_addFd() that do not have a callback (that is provide NULL
- * for the callback). In this case the caller of Looper_pollOnce()
- * or Looper_pollAll() MUST check the return from these functions to
- * discover when data is available on such fds and process it.
- */
- PREPARE_ALLOW_NON_CALLBACKS = 1<<0
- };
-
- /**
- * Creates a looper.
- *
- * If allowNonCallbaks is true, the looper will allow file descriptors to be
- * registered without associated callbacks. This assumes that the caller of
- * pollOnce() is prepared to handle callback-less events itself.
- */
- Looper(bool allowNonCallbacks);
-
- /**
- * Returns whether this looper instance allows the registration of file descriptors
- * using identifiers instead of callbacks.
- */
- bool getAllowNonCallbacks() const;
-
- /**
- * Waits for events to be available, with optional timeout in milliseconds.
- * Invokes callbacks for all file descriptors on which an event occurred.
- *
- * If the timeout is zero, returns immediately without blocking.
- * If the timeout is negative, waits indefinitely until an event appears.
- *
- * Returns POLL_WAKE if the poll was awoken using wake() before
- * the timeout expired and no callbacks were invoked and no other file
- * descriptors were ready.
- *
- * Returns POLL_CALLBACK if one or more callbacks were invoked.
- *
- * Returns POLL_TIMEOUT if there was no data before the given
- * timeout expired.
- *
- * Returns POLL_ERROR if an error occurred.
- *
- * Returns a value >= 0 containing an identifier if its file descriptor has data
- * and it has no callback function (requiring the caller here to handle it).
- * In this (and only this) case outFd, outEvents and outData will contain the poll
- * events and data associated with the fd, otherwise they will be set to NULL.
- *
- * This method does not return until it has finished invoking the appropriate callbacks
- * for all file descriptors that were signalled.
- */
- int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
- inline int pollOnce(int timeoutMillis) {
- return pollOnce(timeoutMillis, NULL, NULL, NULL);
- }
-
- /**
- * Like pollOnce(), but performs all pending callbacks until all
- * data has been consumed or a file descriptor is available with no callback.
- * This function will never return POLL_CALLBACK.
- */
- int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData);
- inline int pollAll(int timeoutMillis) {
- return pollAll(timeoutMillis, NULL, NULL, NULL);
- }
-
- /**
- * Wakes the poll asynchronously.
- *
- * This method can be called on any thread.
- * This method returns immediately.
- */
- void wake();
-
- /**
- * Adds a new file descriptor to be polled by the looper.
- * If the same file descriptor was previously added, it is replaced.
- *
- * "fd" is the file descriptor to be added.
- * "ident" is an identifier for this event, which is returned from pollOnce().
- * The identifier must be >= 0, or POLL_CALLBACK if providing a non-NULL callback.
- * "events" are the poll events to wake up on. Typically this is EVENT_INPUT.
- * "callback" is the function to call when there is an event on the file descriptor.
- * "data" is a private data pointer to supply to the callback.
- *
- * There are two main uses of this function:
- *
- * (1) If "callback" is non-NULL, then this function will be called when there is
- * data on the file descriptor. It should execute any events it has pending,
- * appropriately reading from the file descriptor. The 'ident' is ignored in this case.
- *
- * (2) If "callback" is NULL, the 'ident' will be returned by Looper_pollOnce
- * when its file descriptor has data available, requiring the caller to take
- * care of processing it.
- *
- * Returns 1 if the file descriptor was added, 0 if the arguments were invalid.
- *
- * This method can be called on any thread.
- * This method may block briefly if it needs to wake the poll.
- *
- * The callback may either be specified as a bare function pointer or as a smart
- * pointer callback object. The smart pointer should be preferred because it is
- * easier to avoid races when the callback is removed from a different thread.
- * See removeFd() for details.
- */
- int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data);
- int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
-
- /**
- * Removes a previously added file descriptor from the looper.
- *
- * When this method returns, it is safe to close the file descriptor since the looper
- * will no longer have a reference to it. However, it is possible for the callback to
- * already be running or for it to run one last time if the file descriptor was already
- * signalled. Calling code is responsible for ensuring that this case is safely handled.
- * For example, if the callback takes care of removing itself during its own execution either
- * by returning 0 or by calling this method, then it can be guaranteed to not be invoked
- * again at any later time unless registered anew.
- *
- * A simple way to avoid this problem is to use the version of addFd() that takes
- * a sp<LooperCallback> instead of a bare function pointer. The LooperCallback will
- * be released at the appropriate time by the Looper.
- *
- * Returns 1 if the file descriptor was removed, 0 if none was previously registered.
- *
- * This method can be called on any thread.
- * This method may block briefly if it needs to wake the poll.
- */
- int removeFd(int fd);
-
- /**
- * Enqueues a message to be processed by the specified handler.
- *
- * The handler must not be null.
- * This method can be called on any thread.
- */
- void sendMessage(const sp<MessageHandler>& handler, const Message& message);
-
- /**
- * Enqueues a message to be processed by the specified handler after all pending messages
- * after the specified delay.
- *
- * The time delay is specified in uptime nanoseconds.
- * The handler must not be null.
- * This method can be called on any thread.
- */
- void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
- const Message& message);
-
- /**
- * Enqueues a message to be processed by the specified handler after all pending messages
- * at the specified time.
- *
- * The time is specified in uptime nanoseconds.
- * The handler must not be null.
- * This method can be called on any thread.
- */
- void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
- const Message& message);
-
- /**
- * Removes all messages for the specified handler from the queue.
- *
- * The handler must not be null.
- * This method can be called on any thread.
- */
- void removeMessages(const sp<MessageHandler>& handler);
-
- /**
- * Removes all messages of a particular type for the specified handler from the queue.
- *
- * The handler must not be null.
- * This method can be called on any thread.
- */
- void removeMessages(const sp<MessageHandler>& handler, int what);
-
- /**
- * Return whether this looper's thread is currently idling -- that is, whether it
- * stopped waiting for more work to do. Note that this is intrinsically racy, since
- * its state can change before you get the result back.
- */
- bool isIdling() const;
-
- /**
- * Prepares a looper associated with the calling thread, and returns it.
- * If the thread already has a looper, it is returned. Otherwise, a new
- * one is created, associated with the thread, and returned.
- *
- * The opts may be PREPARE_ALLOW_NON_CALLBACKS or 0.
- */
- static sp<Looper> prepare(int opts);
-
- /**
- * Sets the given looper to be associated with the calling thread.
- * If another looper is already associated with the thread, it is replaced.
- *
- * If "looper" is NULL, removes the currently associated looper.
- */
- static void setForThread(const sp<Looper>& looper);
-
- /**
- * Returns the looper associated with the calling thread, or NULL if
- * there is not one.
- */
- static sp<Looper> getForThread();
-
-private:
- struct Request {
- int fd;
- int ident;
- sp<LooperCallback> callback;
- void* data;
- };
-
- struct Response {
- int events;
- Request request;
- };
-
- struct MessageEnvelope {
- MessageEnvelope() : uptime(0) { }
-
- MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler,
- const Message& message) : uptime(uptime), handler(handler), message(message) {
- }
-
- nsecs_t uptime;
- sp<MessageHandler> handler;
- Message message;
- };
-
- const bool mAllowNonCallbacks; // immutable
-
- int mWakeReadPipeFd; // immutable
- int mWakeWritePipeFd; // immutable
- Mutex mLock;
-
- Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
- bool mSendingMessage; // guarded by mLock
-
- // Whether we are currently waiting for work. Not protected by a lock,
- // any use of it is racy anyway.
- volatile bool mIdling;
-
- int mEpollFd; // immutable
-
- // Locked list of file descriptor monitoring requests.
- KeyedVector<int, Request> mRequests; // guarded by mLock
-
- // This state is only used privately by pollOnce and does not require a lock since
- // it runs on a single thread.
- Vector<Response> mResponses;
- size_t mResponseIndex;
- nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
-
- int pollInner(int timeoutMillis);
- void awoken();
- void pushResponse(int events, const Request& request);
-
- static void initTLSKey();
- static void threadDestructor(void *st);
-};
-
-} // namespace android
-
-#endif // UTILS_LOOPER_H
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
deleted file mode 100644
index cd9d7f9..0000000
--- a/include/utils/LruCache.h
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_UTILS_LRU_CACHE_H
-#define ANDROID_UTILS_LRU_CACHE_H
-
-#include <UniquePtr.h>
-#include <utils/BasicHashtable.h>
-
-namespace android {
-
-/**
- * GenerationCache callback used when an item is removed
- */
-template<typename EntryKey, typename EntryValue>
-class OnEntryRemoved {
-public:
- virtual ~OnEntryRemoved() { };
- virtual void operator()(EntryKey& key, EntryValue& value) = 0;
-}; // class OnEntryRemoved
-
-template <typename TKey, typename TValue>
-class LruCache {
-public:
- explicit LruCache(uint32_t maxCapacity);
-
- enum Capacity {
- kUnlimitedCapacity,
- };
-
- void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener);
- size_t size() const;
- const TValue& get(const TKey& key);
- bool put(const TKey& key, const TValue& value);
- bool remove(const TKey& key);
- bool removeOldest();
- void clear();
- const TValue& peekOldestValue();
-
- class Iterator {
- public:
- Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIndex(-1) {
- }
-
- bool next() {
- mIndex = mCache.mTable->next(mIndex);
- return (ssize_t)mIndex != -1;
- }
-
- size_t index() const {
- return mIndex;
- }
-
- const TValue& value() const {
- return mCache.mTable->entryAt(mIndex).value;
- }
-
- const TKey& key() const {
- return mCache.mTable->entryAt(mIndex).key;
- }
- private:
- const LruCache<TKey, TValue>& mCache;
- size_t mIndex;
- };
-
-private:
- LruCache(const LruCache& that); // disallow copy constructor
-
- struct Entry {
- TKey key;
- TValue value;
- Entry* parent;
- Entry* child;
-
- Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) {
- }
- const TKey& getKey() const { return key; }
- };
-
- void attachToCache(Entry& entry);
- void detachFromCache(Entry& entry);
- void rehash(size_t newCapacity);
-
- UniquePtr<BasicHashtable<TKey, Entry> > mTable;
- OnEntryRemoved<TKey, TValue>* mListener;
- Entry* mOldest;
- Entry* mYoungest;
- uint32_t mMaxCapacity;
- TValue mNullValue;
-};
-
-// Implementation is here, because it's fully templated
-template <typename TKey, typename TValue>
-LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
- : mTable(new BasicHashtable<TKey, Entry>)
- , mListener(NULL)
- , mOldest(NULL)
- , mYoungest(NULL)
- , mMaxCapacity(maxCapacity)
- , mNullValue(NULL) {
-};
-
-template<typename K, typename V>
-void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
- mListener = listener;
-}
-
-template <typename TKey, typename TValue>
-size_t LruCache<TKey, TValue>::size() const {
- return mTable->size();
-}
-
-template <typename TKey, typename TValue>
-const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
- hash_t hash = hash_type(key);
- ssize_t index = mTable->find(-1, hash, key);
- if (index == -1) {
- return mNullValue;
- }
- Entry& entry = mTable->editEntryAt(index);
- detachFromCache(entry);
- attachToCache(entry);
- return entry.value;
-}
-
-template <typename TKey, typename TValue>
-bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
- if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
- removeOldest();
- }
-
- hash_t hash = hash_type(key);
- ssize_t index = mTable->find(-1, hash, key);
- if (index >= 0) {
- return false;
- }
- if (!mTable->hasMoreRoom()) {
- rehash(mTable->capacity() * 2);
- }
-
- // Would it be better to initialize a blank entry and assign key, value?
- Entry initEntry(key, value);
- index = mTable->add(hash, initEntry);
- Entry& entry = mTable->editEntryAt(index);
- attachToCache(entry);
- return true;
-}
-
-template <typename TKey, typename TValue>
-bool LruCache<TKey, TValue>::remove(const TKey& key) {
- hash_t hash = hash_type(key);
- ssize_t index = mTable->find(-1, hash, key);
- if (index < 0) {
- return false;
- }
- Entry& entry = mTable->editEntryAt(index);
- if (mListener) {
- (*mListener)(entry.key, entry.value);
- }
- detachFromCache(entry);
- mTable->removeAt(index);
- return true;
-}
-
-template <typename TKey, typename TValue>
-bool LruCache<TKey, TValue>::removeOldest() {
- if (mOldest != NULL) {
- return remove(mOldest->key);
- // TODO: should probably abort if false
- }
- return false;
-}
-
-template <typename TKey, typename TValue>
-const TValue& LruCache<TKey, TValue>::peekOldestValue() {
- if (mOldest) {
- return mOldest->value;
- }
- return mNullValue;
-}
-
-template <typename TKey, typename TValue>
-void LruCache<TKey, TValue>::clear() {
- if (mListener) {
- for (Entry* p = mOldest; p != NULL; p = p->child) {
- (*mListener)(p->key, p->value);
- }
- }
- mYoungest = NULL;
- mOldest = NULL;
- mTable->clear();
-}
-
-template <typename TKey, typename TValue>
-void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
- if (mYoungest == NULL) {
- mYoungest = mOldest = &entry;
- } else {
- entry.parent = mYoungest;
- mYoungest->child = &entry;
- mYoungest = &entry;
- }
-}
-
-template <typename TKey, typename TValue>
-void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
- if (entry.parent != NULL) {
- entry.parent->child = entry.child;
- } else {
- mOldest = entry.child;
- }
- if (entry.child != NULL) {
- entry.child->parent = entry.parent;
- } else {
- mYoungest = entry.parent;
- }
-
- entry.parent = NULL;
- entry.child = NULL;
-}
-
-template <typename TKey, typename TValue>
-void LruCache<TKey, TValue>::rehash(size_t newCapacity) {
- UniquePtr<BasicHashtable<TKey, Entry> > oldTable(mTable.release());
- Entry* oldest = mOldest;
-
- mOldest = NULL;
- mYoungest = NULL;
- mTable.reset(new BasicHashtable<TKey, Entry>(newCapacity));
- for (Entry* p = oldest; p != NULL; p = p->child) {
- put(p->key, p->value);
- }
-}
-
-}
-
-#endif // ANDROID_UTILS_LRU_CACHE_H
diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h
deleted file mode 100644
index f027c79..0000000
--- a/include/utils/Mutex.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_UTILS_MUTEX_H
-#define _LIBS_UTILS_MUTEX_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <time.h>
-
-#if !defined(_WIN32)
-# include <pthread.h>
-#endif
-
-#include <utils/Errors.h>
-#include <utils/Timers.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class Condition;
-
-/*
- * Simple mutex class. The implementation is system-dependent.
- *
- * The mutex must be unlocked by the thread that locked it. They are not
- * recursive, i.e. the same thread can't lock it multiple times.
- */
-class Mutex {
-public:
- enum {
- PRIVATE = 0,
- SHARED = 1
- };
-
- Mutex();
- Mutex(const char* name);
- Mutex(int type, const char* name = NULL);
- ~Mutex();
-
- // lock or unlock the mutex
- status_t lock();
- void unlock();
-
- // lock if possible; returns 0 on success, error otherwise
- status_t tryLock();
-
-#if defined(__ANDROID__)
- // lock the mutex, but don't wait longer than timeoutMilliseconds.
- // Returns 0 on success, TIMED_OUT for failure due to timeout expiration.
- //
- // OSX doesn't have pthread_mutex_timedlock() or equivalent. To keep
- // capabilities consistent across host OSes, this method is only available
- // when building Android binaries.
- status_t timedLock(nsecs_t timeoutMilliseconds);
-#endif
-
- // Manages the mutex automatically. It'll be locked when Autolock is
- // constructed and released when Autolock goes out of scope.
- class Autolock {
- public:
- inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
- inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
- inline ~Autolock() { mLock.unlock(); }
- private:
- Mutex& mLock;
- };
-
-private:
- friend class Condition;
-
- // A mutex cannot be copied
- Mutex(const Mutex&);
- Mutex& operator = (const Mutex&);
-
-#if !defined(_WIN32)
- pthread_mutex_t mMutex;
-#else
- void _init();
- void* mState;
-#endif
-};
-
-// ---------------------------------------------------------------------------
-
-#if !defined(_WIN32)
-
-inline Mutex::Mutex() {
- pthread_mutex_init(&mMutex, NULL);
-}
-inline Mutex::Mutex(__attribute__((unused)) const char* name) {
- pthread_mutex_init(&mMutex, NULL);
-}
-inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
- if (type == SHARED) {
- pthread_mutexattr_t attr;
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
- pthread_mutex_init(&mMutex, &attr);
- pthread_mutexattr_destroy(&attr);
- } else {
- pthread_mutex_init(&mMutex, NULL);
- }
-}
-inline Mutex::~Mutex() {
- pthread_mutex_destroy(&mMutex);
-}
-inline status_t Mutex::lock() {
- return -pthread_mutex_lock(&mMutex);
-}
-inline void Mutex::unlock() {
- pthread_mutex_unlock(&mMutex);
-}
-inline status_t Mutex::tryLock() {
- return -pthread_mutex_trylock(&mMutex);
-}
-#if defined(__ANDROID__)
-inline status_t Mutex::timedLock(nsecs_t timeoutNs) {
- const struct timespec ts = {
- /* .tv_sec = */ static_cast<time_t>(timeoutNs / 1000000000),
- /* .tv_nsec = */ static_cast<long>(timeoutNs % 1000000000),
- };
- return -pthread_mutex_timedlock(&mMutex, &ts);
-}
-#endif
-
-#endif // !defined(_WIN32)
-
-// ---------------------------------------------------------------------------
-
-/*
- * Automatic mutex. Declare one of these at the top of a function.
- * When the function returns, it will go out of scope, and release the
- * mutex.
- */
-
-typedef Mutex::Autolock AutoMutex;
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // _LIBS_UTILS_MUTEX_H
diff --git a/include/utils/RWLock.h b/include/utils/RWLock.h
deleted file mode 100644
index e743b1c..0000000
--- a/include/utils/RWLock.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_UTILS_RWLOCK_H
-#define _LIBS_UTILS_RWLOCK_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#if !defined(_WIN32)
-# include <pthread.h>
-#endif
-
-#include <utils/Errors.h>
-#include <utils/ThreadDefs.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-#if !defined(_WIN32)
-
-/*
- * Simple mutex class. The implementation is system-dependent.
- *
- * The mutex must be unlocked by the thread that locked it. They are not
- * recursive, i.e. the same thread can't lock it multiple times.
- */
-class RWLock {
-public:
- enum {
- PRIVATE = 0,
- SHARED = 1
- };
-
- RWLock();
- RWLock(const char* name);
- RWLock(int type, const char* name = NULL);
- ~RWLock();
-
- status_t readLock();
- status_t tryReadLock();
- status_t writeLock();
- status_t tryWriteLock();
- void unlock();
-
- class AutoRLock {
- public:
- inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); }
- inline ~AutoRLock() { mLock.unlock(); }
- private:
- RWLock& mLock;
- };
-
- class AutoWLock {
- public:
- inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); }
- inline ~AutoWLock() { mLock.unlock(); }
- private:
- RWLock& mLock;
- };
-
-private:
- // A RWLock cannot be copied
- RWLock(const RWLock&);
- RWLock& operator = (const RWLock&);
-
- pthread_rwlock_t mRWLock;
-};
-
-inline RWLock::RWLock() {
- pthread_rwlock_init(&mRWLock, NULL);
-}
-inline RWLock::RWLock(__attribute__((unused)) const char* name) {
- pthread_rwlock_init(&mRWLock, NULL);
-}
-inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) {
- if (type == SHARED) {
- pthread_rwlockattr_t attr;
- pthread_rwlockattr_init(&attr);
- pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
- pthread_rwlock_init(&mRWLock, &attr);
- pthread_rwlockattr_destroy(&attr);
- } else {
- pthread_rwlock_init(&mRWLock, NULL);
- }
-}
-inline RWLock::~RWLock() {
- pthread_rwlock_destroy(&mRWLock);
-}
-inline status_t RWLock::readLock() {
- return -pthread_rwlock_rdlock(&mRWLock);
-}
-inline status_t RWLock::tryReadLock() {
- return -pthread_rwlock_tryrdlock(&mRWLock);
-}
-inline status_t RWLock::writeLock() {
- return -pthread_rwlock_wrlock(&mRWLock);
-}
-inline status_t RWLock::tryWriteLock() {
- return -pthread_rwlock_trywrlock(&mRWLock);
-}
-inline void RWLock::unlock() {
- pthread_rwlock_unlock(&mRWLock);
-}
-
-#endif // !defined(_WIN32)
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // _LIBS_UTILS_RWLOCK_H
diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h
deleted file mode 100644
index eac6a78..0000000
--- a/include/utils/RefBase.h
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_REF_BASE_H
-#define ANDROID_REF_BASE_H
-
-#include <cutils/atomic.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <utils/StrongPointer.h>
-#include <utils/TypeHelpers.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class TextOutput;
-TextOutput& printWeakPointer(TextOutput& to, const void* val);
-
-// ---------------------------------------------------------------------------
-
-#define COMPARE_WEAK(_op_) \
-inline bool operator _op_ (const sp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-} \
-template<typename U> \
-inline bool operator _op_ (const sp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-template<typename U> \
-inline bool operator _op_ (const U* o) const { \
- return m_ptr _op_ o; \
-}
-
-// ---------------------------------------------------------------------------
-
-class ReferenceRenamer {
-protected:
- // destructor is purposedly not virtual so we avoid code overhead from
- // subclasses; we have to make it protected to guarantee that it
- // cannot be called from this base class (and to make strict compilers
- // happy).
- ~ReferenceRenamer() { }
-public:
- virtual void operator()(size_t i) const = 0;
-};
-
-// ---------------------------------------------------------------------------
-
-class RefBase
-{
-public:
- void incStrong(const void* id) const;
- void decStrong(const void* id) const;
-
- void forceIncStrong(const void* id) const;
-
- //! DEBUGGING ONLY: Get current strong ref count.
- int32_t getStrongCount() const;
-
- class weakref_type
- {
- public:
- RefBase* refBase() const;
-
- void incWeak(const void* id);
- void decWeak(const void* id);
-
- // acquires a strong reference if there is already one.
- bool attemptIncStrong(const void* id);
-
- // acquires a weak reference if there is already one.
- // This is not always safe. see ProcessState.cpp and BpBinder.cpp
- // for proper use.
- bool attemptIncWeak(const void* id);
-
- //! DEBUGGING ONLY: Get current weak ref count.
- int32_t getWeakCount() const;
-
- //! DEBUGGING ONLY: Print references held on object.
- void printRefs() const;
-
- //! DEBUGGING ONLY: Enable tracking for this object.
- // enable -- enable/disable tracking
- // retain -- when tracking is enable, if true, then we save a stack trace
- // for each reference and dereference; when retain == false, we
- // match up references and dereferences and keep only the
- // outstanding ones.
-
- void trackMe(bool enable, bool retain);
- };
-
- weakref_type* createWeak(const void* id) const;
-
- weakref_type* getWeakRefs() const;
-
- //! DEBUGGING ONLY: Print references held on object.
- inline void printRefs() const { getWeakRefs()->printRefs(); }
-
- //! DEBUGGING ONLY: Enable tracking of object.
- inline void trackMe(bool enable, bool retain)
- {
- getWeakRefs()->trackMe(enable, retain);
- }
-
- typedef RefBase basetype;
-
-protected:
- RefBase();
- virtual ~RefBase();
-
- //! Flags for extendObjectLifetime()
- enum {
- OBJECT_LIFETIME_STRONG = 0x0000,
- OBJECT_LIFETIME_WEAK = 0x0001,
- OBJECT_LIFETIME_MASK = 0x0001
- };
-
- void extendObjectLifetime(int32_t mode);
-
- //! Flags for onIncStrongAttempted()
- enum {
- FIRST_INC_STRONG = 0x0001
- };
-
- virtual void onFirstRef();
- virtual void onLastStrongRef(const void* id);
- virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
- virtual void onLastWeakRef(const void* id);
-
-private:
- friend class weakref_type;
- class weakref_impl;
-
- RefBase(const RefBase& o);
- RefBase& operator=(const RefBase& o);
-
-private:
- friend class ReferenceMover;
-
- static void renameRefs(size_t n, const ReferenceRenamer& renamer);
-
- static void renameRefId(weakref_type* ref,
- const void* old_id, const void* new_id);
-
- static void renameRefId(RefBase* ref,
- const void* old_id, const void* new_id);
-
- weakref_impl* const mRefs;
-};
-
-// ---------------------------------------------------------------------------
-
-template <class T>
-class LightRefBase
-{
-public:
- inline LightRefBase() : mCount(0) { }
- inline void incStrong(__attribute__((unused)) const void* id) const {
- android_atomic_inc(&mCount);
- }
- inline void decStrong(__attribute__((unused)) const void* id) const {
- if (android_atomic_dec(&mCount) == 1) {
- delete static_cast<const T*>(this);
- }
- }
- //! DEBUGGING ONLY: Get current strong ref count.
- inline int32_t getStrongCount() const {
- return mCount;
- }
-
- typedef LightRefBase<T> basetype;
-
-protected:
- inline ~LightRefBase() { }
-
-private:
- friend class ReferenceMover;
- inline static void renameRefs(size_t n, const ReferenceRenamer& renamer) { }
- inline static void renameRefId(T* ref,
- const void* old_id, const void* new_id) { }
-
-private:
- mutable volatile int32_t mCount;
-};
-
-// This is a wrapper around LightRefBase that simply enforces a virtual
-// destructor to eliminate the template requirement of LightRefBase
-class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
-public:
- virtual ~VirtualLightRefBase() {}
-};
-
-// ---------------------------------------------------------------------------
-
-template <typename T>
-class wp
-{
-public:
- typedef typename RefBase::weakref_type weakref_type;
-
- inline wp() : m_ptr(0) { }
-
- wp(T* other);
- wp(const wp<T>& other);
- wp(const sp<T>& other);
- template<typename U> wp(U* other);
- template<typename U> wp(const sp<U>& other);
- template<typename U> wp(const wp<U>& other);
-
- ~wp();
-
- // Assignment
-
- wp& operator = (T* other);
- wp& operator = (const wp<T>& other);
- wp& operator = (const sp<T>& other);
-
- template<typename U> wp& operator = (U* other);
- template<typename U> wp& operator = (const wp<U>& other);
- template<typename U> wp& operator = (const sp<U>& other);
-
- void set_object_and_refs(T* other, weakref_type* refs);
-
- // promotion to sp
-
- sp<T> promote() const;
-
- // Reset
-
- void clear();
-
- // Accessors
-
- inline weakref_type* get_refs() const { return m_refs; }
-
- inline T* unsafe_get() const { return m_ptr; }
-
- // Operators
-
- COMPARE_WEAK(==)
- COMPARE_WEAK(!=)
- COMPARE_WEAK(>)
- COMPARE_WEAK(<)
- COMPARE_WEAK(<=)
- COMPARE_WEAK(>=)
-
- inline bool operator == (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
- }
- template<typename U>
- inline bool operator == (const wp<U>& o) const {
- return m_ptr == o.m_ptr;
- }
-
- inline bool operator > (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
- }
- template<typename U>
- inline bool operator > (const wp<U>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
- }
-
- inline bool operator < (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
- }
- template<typename U>
- inline bool operator < (const wp<U>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
- }
- inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
- template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
- inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
- template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
- inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
- template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
-
-private:
- template<typename Y> friend class sp;
- template<typename Y> friend class wp;
-
- T* m_ptr;
- weakref_type* m_refs;
-};
-
-template <typename T>
-TextOutput& operator<<(TextOutput& to, const wp<T>& val);
-
-#undef COMPARE_WEAK
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts below here.
-
-template<typename T>
-wp<T>::wp(T* other)
- : m_ptr(other)
-{
- if (other) m_refs = other->createWeak(this);
-}
-
-template<typename T>
-wp<T>::wp(const wp<T>& other)
- : m_ptr(other.m_ptr), m_refs(other.m_refs)
-{
- if (m_ptr) m_refs->incWeak(this);
-}
-
-template<typename T>
-wp<T>::wp(const sp<T>& other)
- : m_ptr(other.m_ptr)
-{
- if (m_ptr) {
- m_refs = m_ptr->createWeak(this);
- }
-}
-
-template<typename T> template<typename U>
-wp<T>::wp(U* other)
- : m_ptr(other)
-{
- if (other) m_refs = other->createWeak(this);
-}
-
-template<typename T> template<typename U>
-wp<T>::wp(const wp<U>& other)
- : m_ptr(other.m_ptr)
-{
- if (m_ptr) {
- m_refs = other.m_refs;
- m_refs->incWeak(this);
- }
-}
-
-template<typename T> template<typename U>
-wp<T>::wp(const sp<U>& other)
- : m_ptr(other.m_ptr)
-{
- if (m_ptr) {
- m_refs = m_ptr->createWeak(this);
- }
-}
-
-template<typename T>
-wp<T>::~wp()
-{
- if (m_ptr) m_refs->decWeak(this);
-}
-
-template<typename T>
-wp<T>& wp<T>::operator = (T* other)
-{
- weakref_type* newRefs =
- other ? other->createWeak(this) : 0;
- if (m_ptr) m_refs->decWeak(this);
- m_ptr = other;
- m_refs = newRefs;
- return *this;
-}
-
-template<typename T>
-wp<T>& wp<T>::operator = (const wp<T>& other)
-{
- weakref_type* otherRefs(other.m_refs);
- T* otherPtr(other.m_ptr);
- if (otherPtr) otherRefs->incWeak(this);
- if (m_ptr) m_refs->decWeak(this);
- m_ptr = otherPtr;
- m_refs = otherRefs;
- return *this;
-}
-
-template<typename T>
-wp<T>& wp<T>::operator = (const sp<T>& other)
-{
- weakref_type* newRefs =
- other != NULL ? other->createWeak(this) : 0;
- T* otherPtr(other.m_ptr);
- if (m_ptr) m_refs->decWeak(this);
- m_ptr = otherPtr;
- m_refs = newRefs;
- return *this;
-}
-
-template<typename T> template<typename U>
-wp<T>& wp<T>::operator = (U* other)
-{
- weakref_type* newRefs =
- other ? other->createWeak(this) : 0;
- if (m_ptr) m_refs->decWeak(this);
- m_ptr = other;
- m_refs = newRefs;
- return *this;
-}
-
-template<typename T> template<typename U>
-wp<T>& wp<T>::operator = (const wp<U>& other)
-{
- weakref_type* otherRefs(other.m_refs);
- U* otherPtr(other.m_ptr);
- if (otherPtr) otherRefs->incWeak(this);
- if (m_ptr) m_refs->decWeak(this);
- m_ptr = otherPtr;
- m_refs = otherRefs;
- return *this;
-}
-
-template<typename T> template<typename U>
-wp<T>& wp<T>::operator = (const sp<U>& other)
-{
- weakref_type* newRefs =
- other != NULL ? other->createWeak(this) : 0;
- U* otherPtr(other.m_ptr);
- if (m_ptr) m_refs->decWeak(this);
- m_ptr = otherPtr;
- m_refs = newRefs;
- return *this;
-}
-
-template<typename T>
-void wp<T>::set_object_and_refs(T* other, weakref_type* refs)
-{
- if (other) refs->incWeak(this);
- if (m_ptr) m_refs->decWeak(this);
- m_ptr = other;
- m_refs = refs;
-}
-
-template<typename T>
-sp<T> wp<T>::promote() const
-{
- sp<T> result;
- if (m_ptr && m_refs->attemptIncStrong(&result)) {
- result.set_pointer(m_ptr);
- }
- return result;
-}
-
-template<typename T>
-void wp<T>::clear()
-{
- if (m_ptr) {
- m_refs->decWeak(this);
- m_ptr = 0;
- }
-}
-
-template <typename T>
-inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
-{
- return printWeakPointer(to, val.unsafe_get());
-}
-
-// ---------------------------------------------------------------------------
-
-// this class just serves as a namespace so TYPE::moveReferences can stay
-// private.
-class ReferenceMover {
-public:
- // it would be nice if we could make sure no extra code is generated
- // for sp<TYPE> or wp<TYPE> when TYPE is a descendant of RefBase:
- // Using a sp<RefBase> override doesn't work; it's a bit like we wanted
- // a template<typename TYPE inherits RefBase> template...
-
- template<typename TYPE> static inline
- void move_references(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
-
- class Renamer : public ReferenceRenamer {
- sp<TYPE>* d;
- sp<TYPE> const* s;
- virtual void operator()(size_t i) const {
- // The id are known to be the sp<>'s this pointer
- TYPE::renameRefId(d[i].get(), &s[i], &d[i]);
- }
- public:
- Renamer(sp<TYPE>* d, sp<TYPE> const* s) : d(d), s(s) { }
- virtual ~Renamer() { }
- };
-
- memmove(d, s, n*sizeof(sp<TYPE>));
- TYPE::renameRefs(n, Renamer(d, s));
- }
-
-
- template<typename TYPE> static inline
- void move_references(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
-
- class Renamer : public ReferenceRenamer {
- wp<TYPE>* d;
- wp<TYPE> const* s;
- virtual void operator()(size_t i) const {
- // The id are known to be the wp<>'s this pointer
- TYPE::renameRefId(d[i].get_refs(), &s[i], &d[i]);
- }
- public:
- Renamer(wp<TYPE>* d, wp<TYPE> const* s) : d(d), s(s) { }
- virtual ~Renamer() { }
- };
-
- memmove(d, s, n*sizeof(wp<TYPE>));
- TYPE::renameRefs(n, Renamer(d, s));
- }
-};
-
-// specialization for moving sp<> and wp<> types.
-// these are used by the [Sorted|Keyed]Vector<> implementations
-// sp<> and wp<> need to be handled specially, because they do not
-// have trivial copy operation in the general case (see RefBase.cpp
-// when DEBUG ops are enabled), but can be implemented very
-// efficiently in most cases.
-
-template<typename TYPE> inline
-void move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
- ReferenceMover::move_references(d, s, n);
-}
-
-template<typename TYPE> inline
-void move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
- ReferenceMover::move_references(d, s, n);
-}
-
-template<typename TYPE> inline
-void move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
- ReferenceMover::move_references(d, s, n);
-}
-
-template<typename TYPE> inline
-void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
- ReferenceMover::move_references(d, s, n);
-}
-
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_REF_BASE_H
diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h
deleted file mode 100644
index b670953..0000000
--- a/include/utils/SharedBuffer.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_SHARED_BUFFER_H
-#define ANDROID_SHARED_BUFFER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-class SharedBuffer
-{
-public:
-
- /* flags to use with release() */
- enum {
- eKeepStorage = 0x00000001
- };
-
- /*! allocate a buffer of size 'size' and acquire() it.
- * call release() to free it.
- */
- static SharedBuffer* alloc(size_t size);
-
- /*! free the memory associated with the SharedBuffer.
- * Fails if there are any users associated with this SharedBuffer.
- * In other words, the buffer must have been release by all its
- * users.
- */
- static ssize_t dealloc(const SharedBuffer* released);
-
- //! access the data for read
- inline const void* data() const;
-
- //! access the data for read/write
- inline void* data();
-
- //! get size of the buffer
- inline size_t size() const;
-
- //! get back a SharedBuffer object from its data
- static inline SharedBuffer* bufferFromData(void* data);
-
- //! get back a SharedBuffer object from its data
- static inline const SharedBuffer* bufferFromData(const void* data);
-
- //! get the size of a SharedBuffer object from its data
- static inline size_t sizeFromData(const void* data);
-
- //! edit the buffer (get a writtable, or non-const, version of it)
- SharedBuffer* edit() const;
-
- //! edit the buffer, resizing if needed
- SharedBuffer* editResize(size_t size) const;
-
- //! like edit() but fails if a copy is required
- SharedBuffer* attemptEdit() const;
-
- //! resize and edit the buffer, loose it's content.
- SharedBuffer* reset(size_t size) const;
-
- //! acquire/release a reference on this buffer
- void acquire() const;
-
- /*! release a reference on this buffer, with the option of not
- * freeing the memory associated with it if it was the last reference
- * returns the previous reference count
- */
- int32_t release(uint32_t flags = 0) const;
-
- //! returns wether or not we're the only owner
- inline bool onlyOwner() const;
-
-
-private:
- inline SharedBuffer() { }
- inline ~SharedBuffer() { }
- SharedBuffer(const SharedBuffer&);
- SharedBuffer& operator = (const SharedBuffer&);
-
- // 16 bytes. must be sized to preserve correct alignment.
- mutable int32_t mRefs;
- size_t mSize;
- uint32_t mReserved[2];
-};
-
-// ---------------------------------------------------------------------------
-
-const void* SharedBuffer::data() const {
- return this + 1;
-}
-
-void* SharedBuffer::data() {
- return this + 1;
-}
-
-size_t SharedBuffer::size() const {
- return mSize;
-}
-
-SharedBuffer* SharedBuffer::bufferFromData(void* data) {
- return data ? static_cast<SharedBuffer *>(data)-1 : 0;
-}
-
-const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
- return data ? static_cast<const SharedBuffer *>(data)-1 : 0;
-}
-
-size_t SharedBuffer::sizeFromData(const void* data) {
- return data ? bufferFromData(data)->mSize : 0;
-}
-
-bool SharedBuffer::onlyOwner() const {
- return (mRefs == 1);
-}
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_VECTOR_H
diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h
deleted file mode 100644
index ffc03cb..0000000
--- a/include/utils/Singleton.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_UTILS_SINGLETON_H
-#define ANDROID_UTILS_SINGLETON_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/threads.h>
-#include <cutils/compiler.h>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-template <typename TYPE>
-class ANDROID_API Singleton
-{
-public:
- static TYPE& getInstance() {
- Mutex::Autolock _l(sLock);
- TYPE* instance = sInstance;
- if (instance == 0) {
- instance = new TYPE();
- sInstance = instance;
- }
- return *instance;
- }
-
- static bool hasInstance() {
- Mutex::Autolock _l(sLock);
- return sInstance != 0;
- }
-
-protected:
- ~Singleton() { };
- Singleton() { };
-
-private:
- Singleton(const Singleton&);
- Singleton& operator = (const Singleton&);
- static Mutex sLock;
- static TYPE* sInstance;
-};
-
-/*
- * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file
- * (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes,
- * and avoid to have a copy of them in each compilation units Singleton<TYPE>
- * is used.
- * NOTE: we use a version of Mutex ctor that takes a parameter, because
- * for some unknown reason using the default ctor doesn't emit the variable!
- */
-
-#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \
- template<> ::android::Mutex \
- (::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE); \
- template<> TYPE* ::android::Singleton< TYPE >::sInstance(0); \
- template class ::android::Singleton< TYPE >;
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_UTILS_SINGLETON_H
-
diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h
deleted file mode 100644
index 2d3e82a..0000000
--- a/include/utils/SortedVector.h
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_SORTED_VECTOR_H
-#define ANDROID_SORTED_VECTOR_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <cutils/log.h>
-
-#include <utils/Vector.h>
-#include <utils/VectorImpl.h>
-#include <utils/TypeHelpers.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-template <class TYPE>
-class SortedVector : private SortedVectorImpl
-{
- friend class Vector<TYPE>;
-
-public:
- typedef TYPE value_type;
-
- /*!
- * Constructors and destructors
- */
-
- SortedVector();
- SortedVector(const SortedVector<TYPE>& rhs);
- virtual ~SortedVector();
-
- /*! copy operator */
- const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
- SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
-
- /*
- * empty the vector
- */
-
- inline void clear() { VectorImpl::clear(); }
-
- /*!
- * vector stats
- */
-
- //! returns number of items in the vector
- inline size_t size() const { return VectorImpl::size(); }
- //! returns whether or not the vector is empty
- inline bool isEmpty() const { return VectorImpl::isEmpty(); }
- //! returns how many items can be stored without reallocating the backing store
- inline size_t capacity() const { return VectorImpl::capacity(); }
- //! sets the capacity. capacity can never be reduced less than size()
- inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
-
- /*!
- * C-style array access
- */
-
- //! read-only C-style access
- inline const TYPE* array() const;
-
- //! read-write C-style access. BE VERY CAREFUL when modifying the array
- //! you must keep it sorted! You usually don't use this function.
- TYPE* editArray();
-
- //! finds the index of an item
- ssize_t indexOf(const TYPE& item) const;
-
- //! finds where this item should be inserted
- size_t orderOf(const TYPE& item) const;
-
-
- /*!
- * accessors
- */
-
- //! read-only access to an item at a given index
- inline const TYPE& operator [] (size_t index) const;
- //! alternate name for operator []
- inline const TYPE& itemAt(size_t index) const;
- //! stack-usage of the vector. returns the top of the stack (last element)
- const TYPE& top() const;
-
- /*!
- * modifying the array
- */
-
- //! add an item in the right place (and replace the one that is there)
- ssize_t add(const TYPE& item);
-
- //! editItemAt() MUST NOT change the order of this item
- TYPE& editItemAt(size_t index) {
- return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
- }
-
- //! merges a vector into this one
- ssize_t merge(const Vector<TYPE>& vector);
- ssize_t merge(const SortedVector<TYPE>& vector);
-
- //! removes an item
- ssize_t remove(const TYPE&);
-
- //! remove several items
- inline ssize_t removeItemsAt(size_t index, size_t count = 1);
- //! remove one item
- inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
-
-protected:
- virtual void do_construct(void* storage, size_t num) const;
- virtual void do_destroy(void* storage, size_t num) const;
- virtual void do_copy(void* dest, const void* from, size_t num) const;
- virtual void do_splat(void* dest, const void* item, size_t num) const;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const;
- virtual int do_compare(const void* lhs, const void* rhs) const;
-};
-
-// SortedVector<T> can be trivially moved using memcpy() because moving does not
-// require any change to the underlying SharedBuffer contents or reference count.
-template<typename T> struct trait_trivial_move<SortedVector<T> > { enum { value = true }; };
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts from here...
-// ---------------------------------------------------------------------------
-
-template<class TYPE> inline
-SortedVector<TYPE>::SortedVector()
- : SortedVectorImpl(sizeof(TYPE),
- ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
- |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
- |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
- )
-{
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
- : SortedVectorImpl(rhs) {
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>::~SortedVector() {
- finish_vector();
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
- SortedVectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
- SortedVectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const TYPE* SortedVector<TYPE>::array() const {
- return static_cast<const TYPE *>(arrayImpl());
-}
-
-template<class TYPE> inline
-TYPE* SortedVector<TYPE>::editArray() {
- return static_cast<TYPE *>(editArrayImpl());
-}
-
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
- LOG_FATAL_IF(index>=size(),
- "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__,
- int(index), int(size()));
- return *(array() + index);
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
- return operator[](index);
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::top() const {
- return *(array() + size() - 1);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::add(const TYPE& item) {
- return SortedVectorImpl::add(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
- return SortedVectorImpl::indexOf(&item);
-}
-
-template<class TYPE> inline
-size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
- return SortedVectorImpl::orderOf(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
- return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
- return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
- return SortedVectorImpl::remove(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
- return VectorImpl::removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<class TYPE>
-void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
- construct_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
- destroy_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
- copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
- splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
- move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
- move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
- return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
-}
-
-}; // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_SORTED_VECTOR_H
diff --git a/include/utils/String16.h b/include/utils/String16.h
deleted file mode 100644
index d131bfc..0000000
--- a/include/utils/String16.h
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_STRING16_H
-#define ANDROID_STRING16_H
-
-#include <utils/Errors.h>
-#include <utils/SharedBuffer.h>
-#include <utils/Unicode.h>
-#include <utils/TypeHelpers.h>
-
-// ---------------------------------------------------------------------------
-
-extern "C" {
-
-}
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class String8;
-class TextOutput;
-
-//! This is a string holding UTF-16 characters.
-class String16
-{
-public:
- /* use String16(StaticLinkage) if you're statically linking against
- * libutils and declaring an empty static String16, e.g.:
- *
- * static String16 sAStaticEmptyString(String16::kEmptyString);
- * static String16 sAnotherStaticEmptyString(sAStaticEmptyString);
- */
- enum StaticLinkage { kEmptyString };
-
- String16();
- explicit String16(StaticLinkage);
- String16(const String16& o);
- String16(const String16& o,
- size_t len,
- size_t begin=0);
- explicit String16(const char16_t* o);
- explicit String16(const char16_t* o, size_t len);
- explicit String16(const String8& o);
- explicit String16(const char* o);
- explicit String16(const char* o, size_t len);
-
- ~String16();
-
- inline const char16_t* string() const;
- inline size_t size() const;
-
- inline const SharedBuffer* sharedBuffer() const;
-
- void setTo(const String16& other);
- status_t setTo(const char16_t* other);
- status_t setTo(const char16_t* other, size_t len);
- status_t setTo(const String16& other,
- size_t len,
- size_t begin=0);
-
- status_t append(const String16& other);
- status_t append(const char16_t* other, size_t len);
-
- inline String16& operator=(const String16& other);
-
- inline String16& operator+=(const String16& other);
- inline String16 operator+(const String16& other) const;
-
- status_t insert(size_t pos, const char16_t* chrs);
- status_t insert(size_t pos,
- const char16_t* chrs, size_t len);
-
- ssize_t findFirst(char16_t c) const;
- ssize_t findLast(char16_t c) const;
-
- bool startsWith(const String16& prefix) const;
- bool startsWith(const char16_t* prefix) const;
-
- status_t makeLower();
-
- status_t replaceAll(char16_t replaceThis,
- char16_t withThis);
-
- status_t remove(size_t len, size_t begin=0);
-
- inline int compare(const String16& other) const;
-
- inline bool operator<(const String16& other) const;
- inline bool operator<=(const String16& other) const;
- inline bool operator==(const String16& other) const;
- inline bool operator!=(const String16& other) const;
- inline bool operator>=(const String16& other) const;
- inline bool operator>(const String16& other) const;
-
- inline bool operator<(const char16_t* other) const;
- inline bool operator<=(const char16_t* other) const;
- inline bool operator==(const char16_t* other) const;
- inline bool operator!=(const char16_t* other) const;
- inline bool operator>=(const char16_t* other) const;
- inline bool operator>(const char16_t* other) const;
-
- inline operator const char16_t*() const;
-
-private:
- const char16_t* mString;
-};
-
-// String16 can be trivially moved using memcpy() because moving does not
-// require any change to the underlying SharedBuffer contents or reference count.
-ANDROID_TRIVIAL_MOVE_TRAIT(String16)
-
-// ---------------------------------------------------------------------------
-// No user servicable parts below.
-
-inline int compare_type(const String16& lhs, const String16& rhs)
-{
- return lhs.compare(rhs);
-}
-
-inline int strictly_order_type(const String16& lhs, const String16& rhs)
-{
- return compare_type(lhs, rhs) < 0;
-}
-
-inline const char16_t* String16::string() const
-{
- return mString;
-}
-
-inline size_t String16::size() const
-{
- return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
-}
-
-inline const SharedBuffer* String16::sharedBuffer() const
-{
- return SharedBuffer::bufferFromData(mString);
-}
-
-inline String16& String16::operator=(const String16& other)
-{
- setTo(other);
- return *this;
-}
-
-inline String16& String16::operator+=(const String16& other)
-{
- append(other);
- return *this;
-}
-
-inline String16 String16::operator+(const String16& other) const
-{
- String16 tmp(*this);
- tmp += other;
- return tmp;
-}
-
-inline int String16::compare(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size());
-}
-
-inline bool String16::operator<(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) < 0;
-}
-
-inline bool String16::operator<=(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) <= 0;
-}
-
-inline bool String16::operator==(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) == 0;
-}
-
-inline bool String16::operator!=(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) != 0;
-}
-
-inline bool String16::operator>=(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) >= 0;
-}
-
-inline bool String16::operator>(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) > 0;
-}
-
-inline bool String16::operator<(const char16_t* other) const
-{
- return strcmp16(mString, other) < 0;
-}
-
-inline bool String16::operator<=(const char16_t* other) const
-{
- return strcmp16(mString, other) <= 0;
-}
-
-inline bool String16::operator==(const char16_t* other) const
-{
- return strcmp16(mString, other) == 0;
-}
-
-inline bool String16::operator!=(const char16_t* other) const
-{
- return strcmp16(mString, other) != 0;
-}
-
-inline bool String16::operator>=(const char16_t* other) const
-{
- return strcmp16(mString, other) >= 0;
-}
-
-inline bool String16::operator>(const char16_t* other) const
-{
- return strcmp16(mString, other) > 0;
-}
-
-inline String16::operator const char16_t*() const
-{
- return mString;
-}
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_STRING16_H
diff --git a/include/utils/String8.h b/include/utils/String8.h
deleted file mode 100644
index ecfcf10..0000000
--- a/include/utils/String8.h
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_STRING8_H
-#define ANDROID_STRING8_H
-
-#include <utils/Errors.h>
-#include <utils/SharedBuffer.h>
-#include <utils/Unicode.h>
-#include <utils/TypeHelpers.h>
-
-#include <string.h> // for strcmp
-#include <stdarg.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-class String16;
-class TextOutput;
-
-//! This is a string holding UTF-8 characters. Does not allow the value more
-// than 0x10FFFF, which is not valid unicode codepoint.
-class String8
-{
-public:
- /* use String8(StaticLinkage) if you're statically linking against
- * libutils and declaring an empty static String8, e.g.:
- *
- * static String8 sAStaticEmptyString(String8::kEmptyString);
- * static String8 sAnotherStaticEmptyString(sAStaticEmptyString);
- */
- enum StaticLinkage { kEmptyString };
-
- String8();
- explicit String8(StaticLinkage);
- String8(const String8& o);
- explicit String8(const char* o);
- explicit String8(const char* o, size_t numChars);
-
- explicit String8(const String16& o);
- explicit String8(const char16_t* o);
- explicit String8(const char16_t* o, size_t numChars);
- explicit String8(const char32_t* o);
- explicit String8(const char32_t* o, size_t numChars);
- ~String8();
-
- static inline const String8 empty();
-
- static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
- static String8 formatV(const char* fmt, va_list args);
-
- inline const char* string() const;
- inline size_t size() const;
- inline size_t length() const;
- inline size_t bytes() const;
- inline bool isEmpty() const;
-
- inline const SharedBuffer* sharedBuffer() const;
-
- void clear();
-
- void setTo(const String8& other);
- status_t setTo(const char* other);
- status_t setTo(const char* other, size_t numChars);
- status_t setTo(const char16_t* other, size_t numChars);
- status_t setTo(const char32_t* other,
- size_t length);
-
- status_t append(const String8& other);
- status_t append(const char* other);
- status_t append(const char* other, size_t numChars);
-
- status_t appendFormat(const char* fmt, ...)
- __attribute__((format (printf, 2, 3)));
- status_t appendFormatV(const char* fmt, va_list args);
-
- // Note that this function takes O(N) time to calculate the value.
- // No cache value is stored.
- size_t getUtf32Length() const;
- int32_t getUtf32At(size_t index,
- size_t *next_index) const;
- void getUtf32(char32_t* dst) const;
-
- inline String8& operator=(const String8& other);
- inline String8& operator=(const char* other);
-
- inline String8& operator+=(const String8& other);
- inline String8 operator+(const String8& other) const;
-
- inline String8& operator+=(const char* other);
- inline String8 operator+(const char* other) const;
-
- inline int compare(const String8& other) const;
-
- inline bool operator<(const String8& other) const;
- inline bool operator<=(const String8& other) const;
- inline bool operator==(const String8& other) const;
- inline bool operator!=(const String8& other) const;
- inline bool operator>=(const String8& other) const;
- inline bool operator>(const String8& other) const;
-
- inline bool operator<(const char* other) const;
- inline bool operator<=(const char* other) const;
- inline bool operator==(const char* other) const;
- inline bool operator!=(const char* other) const;
- inline bool operator>=(const char* other) const;
- inline bool operator>(const char* other) const;
-
- inline operator const char*() const;
-
- char* lockBuffer(size_t size);
- void unlockBuffer();
- status_t unlockBuffer(size_t size);
-
- // return the index of the first byte of other in this at or after
- // start, or -1 if not found
- ssize_t find(const char* other, size_t start = 0) const;
-
- // return true if this string contains the specified substring
- inline bool contains(const char* other) const;
-
- // removes all occurrence of the specified substring
- // returns true if any were found and removed
- bool removeAll(const char* other);
-
- void toLower();
- void toLower(size_t start, size_t numChars);
- void toUpper();
- void toUpper(size_t start, size_t numChars);
-
-
- /*
- * These methods operate on the string as if it were a path name.
- */
-
- /*
- * Set the filename field to a specific value.
- *
- * Normalizes the filename, removing a trailing '/' if present.
- */
- void setPathName(const char* name);
- void setPathName(const char* name, size_t numChars);
-
- /*
- * Get just the filename component.
- *
- * "/tmp/foo/bar.c" --> "bar.c"
- */
- String8 getPathLeaf(void) const;
-
- /*
- * Remove the last (file name) component, leaving just the directory
- * name.
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo"
- * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
- * "bar.c" --> ""
- */
- String8 getPathDir(void) const;
-
- /*
- * Retrieve the front (root dir) component. Optionally also return the
- * remaining components.
- *
- * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
- * "/tmp" --> "tmp" (remain = "")
- * "bar.c" --> "bar.c" (remain = "")
- */
- String8 walkPath(String8* outRemains = NULL) const;
-
- /*
- * Return the filename extension. This is the last '.' and any number
- * of characters that follow it. The '.' is included in case we
- * decide to expand our definition of what constitutes an extension.
- *
- * "/tmp/foo/bar.c" --> ".c"
- * "/tmp" --> ""
- * "/tmp/foo.bar/baz" --> ""
- * "foo.jpeg" --> ".jpeg"
- * "foo." --> ""
- */
- String8 getPathExtension(void) const;
-
- /*
- * Return the path without the extension. Rules for what constitutes
- * an extension are described in the comment for getPathExtension().
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
- */
- String8 getBasePath(void) const;
-
- /*
- * Add a component to the pathname. We guarantee that there is
- * exactly one path separator between the old path and the new.
- * If there is no existing name, we just copy the new name in.
- *
- * If leaf is a fully qualified path (i.e. starts with '/', it
- * replaces whatever was there before.
- */
- String8& appendPath(const char* leaf);
- String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); }
-
- /*
- * Like appendPath(), but does not affect this string. Returns a new one instead.
- */
- String8 appendPathCopy(const char* leaf) const
- { String8 p(*this); p.appendPath(leaf); return p; }
- String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); }
-
- /*
- * Converts all separators in this string to /, the default path separator.
- *
- * If the default OS separator is backslash, this converts all
- * backslashes to slashes, in-place. Otherwise it does nothing.
- * Returns self.
- */
- String8& convertToResPath();
-
-private:
- status_t real_append(const char* other, size_t numChars);
- char* find_extension(void) const;
-
- const char* mString;
-};
-
-// String8 can be trivially moved using memcpy() because moving does not
-// require any change to the underlying SharedBuffer contents or reference count.
-ANDROID_TRIVIAL_MOVE_TRAIT(String8)
-
-// ---------------------------------------------------------------------------
-// No user servicable parts below.
-
-inline int compare_type(const String8& lhs, const String8& rhs)
-{
- return lhs.compare(rhs);
-}
-
-inline int strictly_order_type(const String8& lhs, const String8& rhs)
-{
- return compare_type(lhs, rhs) < 0;
-}
-
-inline const String8 String8::empty() {
- return String8();
-}
-
-inline const char* String8::string() const
-{
- return mString;
-}
-
-inline size_t String8::length() const
-{
- return SharedBuffer::sizeFromData(mString)-1;
-}
-
-inline size_t String8::size() const
-{
- return length();
-}
-
-inline bool String8::isEmpty() const
-{
- return length() == 0;
-}
-
-inline size_t String8::bytes() const
-{
- return SharedBuffer::sizeFromData(mString)-1;
-}
-
-inline const SharedBuffer* String8::sharedBuffer() const
-{
- return SharedBuffer::bufferFromData(mString);
-}
-
-inline bool String8::contains(const char* other) const
-{
- return find(other) >= 0;
-}
-
-inline String8& String8::operator=(const String8& other)
-{
- setTo(other);
- return *this;
-}
-
-inline String8& String8::operator=(const char* other)
-{
- setTo(other);
- return *this;
-}
-
-inline String8& String8::operator+=(const String8& other)
-{
- append(other);
- return *this;
-}
-
-inline String8 String8::operator+(const String8& other) const
-{
- String8 tmp(*this);
- tmp += other;
- return tmp;
-}
-
-inline String8& String8::operator+=(const char* other)
-{
- append(other);
- return *this;
-}
-
-inline String8 String8::operator+(const char* other) const
-{
- String8 tmp(*this);
- tmp += other;
- return tmp;
-}
-
-inline int String8::compare(const String8& other) const
-{
- return strcmp(mString, other.mString);
-}
-
-inline bool String8::operator<(const String8& other) const
-{
- return strcmp(mString, other.mString) < 0;
-}
-
-inline bool String8::operator<=(const String8& other) const
-{
- return strcmp(mString, other.mString) <= 0;
-}
-
-inline bool String8::operator==(const String8& other) const
-{
- return strcmp(mString, other.mString) == 0;
-}
-
-inline bool String8::operator!=(const String8& other) const
-{
- return strcmp(mString, other.mString) != 0;
-}
-
-inline bool String8::operator>=(const String8& other) const
-{
- return strcmp(mString, other.mString) >= 0;
-}
-
-inline bool String8::operator>(const String8& other) const
-{
- return strcmp(mString, other.mString) > 0;
-}
-
-inline bool String8::operator<(const char* other) const
-{
- return strcmp(mString, other) < 0;
-}
-
-inline bool String8::operator<=(const char* other) const
-{
- return strcmp(mString, other) <= 0;
-}
-
-inline bool String8::operator==(const char* other) const
-{
- return strcmp(mString, other) == 0;
-}
-
-inline bool String8::operator!=(const char* other) const
-{
- return strcmp(mString, other) != 0;
-}
-
-inline bool String8::operator>=(const char* other) const
-{
- return strcmp(mString, other) >= 0;
-}
-
-inline bool String8::operator>(const char* other) const
-{
- return strcmp(mString, other) > 0;
-}
-
-inline String8::operator const char*() const
-{
- return mString;
-}
-
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_STRING8_H
diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h
deleted file mode 100644
index aba9577..0000000
--- a/include/utils/StrongPointer.h
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_STRONG_POINTER_H
-#define ANDROID_STRONG_POINTER_H
-
-#include <cutils/atomic.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <stdlib.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-template<typename T> class wp;
-
-// ---------------------------------------------------------------------------
-
-#define COMPARE(_op_) \
-inline bool operator _op_ (const sp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-} \
-template<typename U> \
-inline bool operator _op_ (const sp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-template<typename U> \
-inline bool operator _op_ (const U* o) const { \
- return m_ptr _op_ o; \
-} \
-inline bool operator _op_ (const wp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-template<typename U> \
-inline bool operator _op_ (const wp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-}
-
-// ---------------------------------------------------------------------------
-
-template<typename T>
-class sp {
-public:
- inline sp() : m_ptr(0) { }
-
- sp(T* other);
- sp(const sp<T>& other);
- template<typename U> sp(U* other);
- template<typename U> sp(const sp<U>& other);
-
- ~sp();
-
- // Assignment
-
- sp& operator = (T* other);
- sp& operator = (const sp<T>& other);
-
- template<typename U> sp& operator = (const sp<U>& other);
- template<typename U> sp& operator = (U* other);
-
- //! Special optimization for use by ProcessState (and nobody else).
- void force_set(T* other);
-
- // Reset
-
- void clear();
-
- // Accessors
-
- inline T& operator* () const { return *m_ptr; }
- inline T* operator-> () const { return m_ptr; }
- inline T* get() const { return m_ptr; }
-
- // Operators
-
- COMPARE(==)
- COMPARE(!=)
- COMPARE(>)
- COMPARE(<)
- COMPARE(<=)
- COMPARE(>=)
-
-private:
- template<typename Y> friend class sp;
- template<typename Y> friend class wp;
- void set_pointer(T* ptr);
- T* m_ptr;
-};
-
-#undef COMPARE
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts below here.
-
-template<typename T>
-sp<T>::sp(T* other)
- : m_ptr(other) {
- if (other)
- other->incStrong(this);
-}
-
-template<typename T>
-sp<T>::sp(const sp<T>& other)
- : m_ptr(other.m_ptr) {
- if (m_ptr)
- m_ptr->incStrong(this);
-}
-
-template<typename T> template<typename U>
-sp<T>::sp(U* other)
- : m_ptr(other) {
- if (other)
- ((T*) other)->incStrong(this);
-}
-
-template<typename T> template<typename U>
-sp<T>::sp(const sp<U>& other)
- : m_ptr(other.m_ptr) {
- if (m_ptr)
- m_ptr->incStrong(this);
-}
-
-template<typename T>
-sp<T>::~sp() {
- if (m_ptr)
- m_ptr->decStrong(this);
-}
-
-template<typename T>
-sp<T>& sp<T>::operator =(const sp<T>& other) {
- T* otherPtr(other.m_ptr);
- if (otherPtr)
- otherPtr->incStrong(this);
- if (m_ptr)
- m_ptr->decStrong(this);
- m_ptr = otherPtr;
- return *this;
-}
-
-template<typename T>
-sp<T>& sp<T>::operator =(T* other) {
- if (other)
- other->incStrong(this);
- if (m_ptr)
- m_ptr->decStrong(this);
- m_ptr = other;
- return *this;
-}
-
-template<typename T> template<typename U>
-sp<T>& sp<T>::operator =(const sp<U>& other) {
- T* otherPtr(other.m_ptr);
- if (otherPtr)
- otherPtr->incStrong(this);
- if (m_ptr)
- m_ptr->decStrong(this);
- m_ptr = otherPtr;
- return *this;
-}
-
-template<typename T> template<typename U>
-sp<T>& sp<T>::operator =(U* other) {
- if (other)
- ((T*) other)->incStrong(this);
- if (m_ptr)
- m_ptr->decStrong(this);
- m_ptr = other;
- return *this;
-}
-
-template<typename T>
-void sp<T>::force_set(T* other) {
- other->forceIncStrong(this);
- m_ptr = other;
-}
-
-template<typename T>
-void sp<T>::clear() {
- if (m_ptr) {
- m_ptr->decStrong(this);
- m_ptr = 0;
- }
-}
-
-template<typename T>
-void sp<T>::set_pointer(T* ptr) {
- m_ptr = ptr;
-}
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_STRONG_POINTER_H
diff --git a/include/utils/Thread.h b/include/utils/Thread.h
deleted file mode 100644
index 1532b7e..0000000
--- a/include/utils/Thread.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_UTILS_THREAD_H
-#define _LIBS_UTILS_THREAD_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <time.h>
-
-#if !defined(_WIN32)
-# include <pthread.h>
-#endif
-
-#include <utils/Condition.h>
-#include <utils/Errors.h>
-#include <utils/Mutex.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-#include <utils/ThreadDefs.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class Thread : virtual public RefBase
-{
-public:
- // Create a Thread object, but doesn't create or start the associated
- // thread. See the run() method.
- Thread(bool canCallJava = true);
- virtual ~Thread();
-
- // Start the thread in threadLoop() which needs to be implemented.
- virtual status_t run( const char* name = 0,
- int32_t priority = PRIORITY_DEFAULT,
- size_t stack = 0);
-
- // Ask this object's thread to exit. This function is asynchronous, when the
- // function returns the thread might still be running. Of course, this
- // function can be called from a different thread.
- virtual void requestExit();
-
- // Good place to do one-time initializations
- virtual status_t readyToRun();
-
- // Call requestExit() and wait until this object's thread exits.
- // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call
- // this function from this object's thread. Will return WOULD_BLOCK in
- // that case.
- status_t requestExitAndWait();
-
- // Wait until this object's thread exits. Returns immediately if not yet running.
- // Do not call from this object's thread; will return WOULD_BLOCK in that case.
- status_t join();
-
- // Indicates whether this thread is running or not.
- bool isRunning() const;
-
-#if defined(__ANDROID__)
- // Return the thread's kernel ID, same as the thread itself calling gettid(),
- // or -1 if the thread is not running.
- pid_t getTid() const;
-#endif
-
-protected:
- // exitPending() returns true if requestExit() has been called.
- bool exitPending() const;
-
-private:
- // Derived class must implement threadLoop(). The thread starts its life
- // here. There are two ways of using the Thread object:
- // 1) loop: if threadLoop() returns true, it will be called again if
- // requestExit() wasn't called.
- // 2) once: if threadLoop() returns false, the thread will exit upon return.
- virtual bool threadLoop() = 0;
-
-private:
- Thread& operator=(const Thread&);
- static int _threadLoop(void* user);
- const bool mCanCallJava;
- // always hold mLock when reading or writing
- thread_id_t mThread;
- mutable Mutex mLock;
- Condition mThreadExitedCondition;
- status_t mStatus;
- // note that all accesses of mExitPending and mRunning need to hold mLock
- volatile bool mExitPending;
- volatile bool mRunning;
- sp<Thread> mHoldSelf;
-#if defined(__ANDROID__)
- // legacy for debugging, not used by getTid() as it is set by the child thread
- // and so is not initialized until the child reaches that point
- pid_t mTid;
-#endif
-};
-
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-#endif // _LIBS_UTILS_THREAD_H
-// ---------------------------------------------------------------------------
diff --git a/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h
deleted file mode 100644
index 9711c13..0000000
--- a/include/utils/ThreadDefs.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_UTILS_THREAD_DEFS_H
-#define _LIBS_UTILS_THREAD_DEFS_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <system/graphics.h>
-#include <system/thread_defs.h>
-
-// ---------------------------------------------------------------------------
-// C API
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef void* android_thread_id_t;
-
-typedef int (*android_thread_func_t)(void*);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-// ---------------------------------------------------------------------------
-// C++ API
-#ifdef __cplusplus
-namespace android {
-// ---------------------------------------------------------------------------
-
-typedef android_thread_id_t thread_id_t;
-typedef android_thread_func_t thread_func_t;
-
-enum {
- PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST,
- PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND,
- PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL,
- PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND,
- PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY,
- PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY,
- PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO,
- PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO,
- PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST,
- PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT,
- PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE,
- PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE,
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-#endif // __cplusplus
-// ---------------------------------------------------------------------------
-
-
-#endif // _LIBS_UTILS_THREAD_DEFS_H
diff --git a/include/utils/Trace.h b/include/utils/Trace.h
deleted file mode 100644
index 6ba68f6..0000000
--- a/include/utils/Trace.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_TRACE_H
-#define ANDROID_TRACE_H
-
-#if defined(__ANDROID__)
-
-#include <fcntl.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/compiler.h>
-#include <utils/threads.h>
-#include <cutils/trace.h>
-
-// See <cutils/trace.h> for more ATRACE_* macros.
-
-// ATRACE_NAME traces the beginning and end of the current scope. To trace
-// the correct start and end times this macro should be declared first in the
-// scope body.
-#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name)
-// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
-#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
-
-namespace android {
-
-class ScopedTrace {
-public:
-inline ScopedTrace(uint64_t tag, const char* name)
- : mTag(tag) {
- atrace_begin(mTag,name);
-}
-
-inline ~ScopedTrace() {
- atrace_end(mTag);
-}
-
-private:
- uint64_t mTag;
-};
-
-}; // namespace android
-
-#else // !__ANDROID__
-
-#define ATRACE_NAME(...)
-#define ATRACE_CALL()
-
-#endif // __ANDROID__
-
-#endif // ANDROID_TRACE_H
diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h
deleted file mode 100644
index 61d618e..0000000
--- a/include/utils/TypeHelpers.h
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_TYPE_HELPERS_H
-#define ANDROID_TYPE_HELPERS_H
-
-#include <new>
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-/*
- * Types traits
- */
-
-template <typename T> struct trait_trivial_ctor { enum { value = false }; };
-template <typename T> struct trait_trivial_dtor { enum { value = false }; };
-template <typename T> struct trait_trivial_copy { enum { value = false }; };
-template <typename T> struct trait_trivial_move { enum { value = false }; };
-template <typename T> struct trait_pointer { enum { value = false }; };
-template <typename T> struct trait_pointer<T*> { enum { value = true }; };
-
-template <typename TYPE>
-struct traits {
- enum {
- // whether this type is a pointer
- is_pointer = trait_pointer<TYPE>::value,
- // whether this type's constructor is a no-op
- has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
- // whether this type's destructor is a no-op
- has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
- // whether this type type can be copy-constructed with memcpy
- has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
- // whether this type can be moved with memmove
- has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value
- };
-};
-
-template <typename T, typename U>
-struct aggregate_traits {
- enum {
- is_pointer = false,
- has_trivial_ctor =
- traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
- has_trivial_dtor =
- traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
- has_trivial_copy =
- traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
- has_trivial_move =
- traits<T>::has_trivial_move && traits<U>::has_trivial_move
- };
-};
-
-#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
- template<> struct trait_trivial_ctor< T > { enum { value = true }; };
-
-#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
- template<> struct trait_trivial_dtor< T > { enum { value = true }; };
-
-#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \
- template<> struct trait_trivial_copy< T > { enum { value = true }; };
-
-#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \
- template<> struct trait_trivial_move< T > { enum { value = true }; };
-
-#define ANDROID_BASIC_TYPES_TRAITS( T ) \
- ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
- ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
- ANDROID_TRIVIAL_COPY_TRAIT( T ) \
- ANDROID_TRIVIAL_MOVE_TRAIT( T )
-
-// ---------------------------------------------------------------------------
-
-/*
- * basic types traits
- */
-
-ANDROID_BASIC_TYPES_TRAITS( void )
-ANDROID_BASIC_TYPES_TRAITS( bool )
-ANDROID_BASIC_TYPES_TRAITS( char )
-ANDROID_BASIC_TYPES_TRAITS( unsigned char )
-ANDROID_BASIC_TYPES_TRAITS( short )
-ANDROID_BASIC_TYPES_TRAITS( unsigned short )
-ANDROID_BASIC_TYPES_TRAITS( int )
-ANDROID_BASIC_TYPES_TRAITS( unsigned int )
-ANDROID_BASIC_TYPES_TRAITS( long )
-ANDROID_BASIC_TYPES_TRAITS( unsigned long )
-ANDROID_BASIC_TYPES_TRAITS( long long )
-ANDROID_BASIC_TYPES_TRAITS( unsigned long long )
-ANDROID_BASIC_TYPES_TRAITS( float )
-ANDROID_BASIC_TYPES_TRAITS( double )
-
-// ---------------------------------------------------------------------------
-
-
-/*
- * compare and order types
- */
-
-template<typename TYPE> inline
-int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
- return (lhs < rhs) ? 1 : 0;
-}
-
-template<typename TYPE> inline
-int compare_type(const TYPE& lhs, const TYPE& rhs) {
- return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
-}
-
-/*
- * create, destroy, copy and move types...
- */
-
-template<typename TYPE> inline
-void construct_type(TYPE* p, size_t n) {
- if (!traits<TYPE>::has_trivial_ctor) {
- while (n > 0) {
- n--;
- new(p++) TYPE;
- }
- }
-}
-
-template<typename TYPE> inline
-void destroy_type(TYPE* p, size_t n) {
- if (!traits<TYPE>::has_trivial_dtor) {
- while (n > 0) {
- n--;
- p->~TYPE();
- p++;
- }
- }
-}
-
-template<typename TYPE> inline
-void copy_type(TYPE* d, const TYPE* s, size_t n) {
- if (!traits<TYPE>::has_trivial_copy) {
- while (n > 0) {
- n--;
- new(d) TYPE(*s);
- d++, s++;
- }
- } else {
- memcpy(d,s,n*sizeof(TYPE));
- }
-}
-
-template<typename TYPE> inline
-void splat_type(TYPE* where, const TYPE* what, size_t n) {
- if (!traits<TYPE>::has_trivial_copy) {
- while (n > 0) {
- n--;
- new(where) TYPE(*what);
- where++;
- }
- } else {
- while (n > 0) {
- n--;
- *where++ = *what;
- }
- }
-}
-
-template<typename TYPE> inline
-void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
- || traits<TYPE>::has_trivial_move)
- {
- memmove(d,s,n*sizeof(TYPE));
- } else {
- d += n;
- s += n;
- while (n > 0) {
- n--;
- --d, --s;
- if (!traits<TYPE>::has_trivial_copy) {
- new(d) TYPE(*s);
- } else {
- *d = *s;
- }
- if (!traits<TYPE>::has_trivial_dtor) {
- s->~TYPE();
- }
- }
- }
-}
-
-template<typename TYPE> inline
-void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
- || traits<TYPE>::has_trivial_move)
- {
- memmove(d,s,n*sizeof(TYPE));
- } else {
- while (n > 0) {
- n--;
- if (!traits<TYPE>::has_trivial_copy) {
- new(d) TYPE(*s);
- } else {
- *d = *s;
- }
- if (!traits<TYPE>::has_trivial_dtor) {
- s->~TYPE();
- }
- d++, s++;
- }
- }
-}
-
-// ---------------------------------------------------------------------------
-
-/*
- * a key/value pair
- */
-
-template <typename KEY, typename VALUE>
-struct key_value_pair_t {
- typedef KEY key_t;
- typedef VALUE value_t;
-
- KEY key;
- VALUE value;
- key_value_pair_t() { }
- key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
- key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
- key_value_pair_t(const KEY& k) : key(k) { }
- inline bool operator < (const key_value_pair_t& o) const {
- return strictly_order_type(key, o.key);
- }
- inline const KEY& getKey() const {
- return key;
- }
- inline const VALUE& getValue() const {
- return value;
- }
-};
-
-template <typename K, typename V>
-struct trait_trivial_ctor< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
-template <typename K, typename V>
-struct trait_trivial_dtor< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
-template <typename K, typename V>
-struct trait_trivial_copy< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
-template <typename K, typename V>
-struct trait_trivial_move< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_move }; };
-
-// ---------------------------------------------------------------------------
-
-/*
- * Hash codes.
- */
-typedef uint32_t hash_t;
-
-template <typename TKey>
-hash_t hash_type(const TKey& key);
-
-/* Built-in hash code specializations.
- * Assumes pointers are 32bit. */
-#define ANDROID_INT32_HASH(T) \
- template <> inline hash_t hash_type(const T& value) { return hash_t(value); }
-#define ANDROID_INT64_HASH(T) \
- template <> inline hash_t hash_type(const T& value) { \
- return hash_t((value >> 32) ^ value); }
-#define ANDROID_REINTERPRET_HASH(T, R) \
- template <> inline hash_t hash_type(const T& value) { \
- return hash_type(*reinterpret_cast<const R*>(&value)); }
-
-ANDROID_INT32_HASH(bool)
-ANDROID_INT32_HASH(int8_t)
-ANDROID_INT32_HASH(uint8_t)
-ANDROID_INT32_HASH(int16_t)
-ANDROID_INT32_HASH(uint16_t)
-ANDROID_INT32_HASH(int32_t)
-ANDROID_INT32_HASH(uint32_t)
-ANDROID_INT64_HASH(int64_t)
-ANDROID_INT64_HASH(uint64_t)
-ANDROID_REINTERPRET_HASH(float, uint32_t)
-ANDROID_REINTERPRET_HASH(double, uint64_t)
-
-template <typename T> inline hash_t hash_type(T* const & value) {
- return hash_type(uintptr_t(value));
-}
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_TYPE_HELPERS_H
diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h
deleted file mode 100644
index b76a5e2..0000000
--- a/include/utils/Unicode.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_UNICODE_H
-#define ANDROID_UNICODE_H
-
-#include <sys/types.h>
-#include <stdint.h>
-
-extern "C" {
-
-// Standard string functions on char16_t strings.
-int strcmp16(const char16_t *, const char16_t *);
-int strncmp16(const char16_t *s1, const char16_t *s2, size_t n);
-size_t strlen16(const char16_t *);
-size_t strnlen16(const char16_t *, size_t);
-char16_t *strcpy16(char16_t *, const char16_t *);
-char16_t *strncpy16(char16_t *, const char16_t *, size_t);
-
-// Version of comparison that supports embedded nulls.
-// This is different than strncmp() because we don't stop
-// at a nul character and consider the strings to be different
-// if the lengths are different (thus we need to supply the
-// lengths of both strings). This can also be used when
-// your string is not nul-terminated as it will have the
-// equivalent result as strcmp16 (unlike strncmp16).
-int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
-
-// Version of strzcmp16 for comparing strings in different endianness.
-int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
-
-// Standard string functions on char32_t strings.
-size_t strlen32(const char32_t *);
-size_t strnlen32(const char32_t *, size_t);
-
-/**
- * Measure the length of a UTF-32 string in UTF-8. If the string is invalid
- * such as containing a surrogate character, -1 will be returned.
- */
-ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len);
-
-/**
- * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not
- * large enough to store the string, the part of the "src" string is stored
- * into "dst" as much as possible. See the examples for more detail.
- * Returns the size actually used for storing the string.
- * dst" is not null-terminated when dst_len is fully used (like strncpy).
- *
- * Example 1
- * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
- * "src_len" == 2
- * "dst_len" >= 7
- * ->
- * Returned value == 6
- * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0
- * (note that "dst" is null-terminated)
- *
- * Example 2
- * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
- * "src_len" == 2
- * "dst_len" == 5
- * ->
- * Returned value == 3
- * "dst" becomes \xE3\x81\x82\0
- * (note that "dst" is null-terminated, but \u3044 is not stored in "dst"
- * since "dst" does not have enough size to store the character)
- *
- * Example 3
- * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
- * "src_len" == 2
- * "dst_len" == 6
- * ->
- * Returned value == 6
- * "dst" becomes \xE3\x81\x82\xE3\x81\x84
- * (note that "dst" is NOT null-terminated, like strncpy)
- */
-void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst);
-
-/**
- * Returns the unicode value at "index".
- * Returns -1 when the index is invalid (equals to or more than "src_len").
- * If returned value is positive, it is able to be converted to char32_t, which
- * is unsigned. Then, if "next_index" is not NULL, the next index to be used is
- * stored in "next_index". "next_index" can be NULL.
- */
-int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index);
-
-
-/**
- * Returns the UTF-8 length of UTF-16 string "src".
- */
-ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len);
-
-/**
- * Converts a UTF-16 string to UTF-8. The destination buffer must be large
- * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added
- * NULL terminator.
- */
-void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst);
-
-/**
- * Returns the length of "src" when "src" is valid UTF-8 string.
- * Returns 0 if src is NULL or 0-length string. Returns -1 when the source
- * is an invalid string.
- *
- * This function should be used to determine whether "src" is valid UTF-8
- * characters with valid unicode codepoints. "src" must be null-terminated.
- *
- * If you are going to use other utf8_to_... functions defined in this header
- * with string which may not be valid UTF-8 with valid codepoint (form 0 to
- * 0x10FFFF), you should use this function before calling others, since the
- * other functions do not check whether the string is valid UTF-8 or not.
- *
- * If you do not care whether "src" is valid UTF-8 or not, you should use
- * strlen() as usual, which should be much faster.
- */
-ssize_t utf8_length(const char *src);
-
-/**
- * Measure the length of a UTF-32 string.
- */
-size_t utf8_to_utf32_length(const char *src, size_t src_len);
-
-/**
- * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large
- * enough to store the entire converted string as measured by
- * utf8_to_utf32_length plus space for a NULL terminator.
- */
-void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst);
-
-/**
- * Returns the UTF-16 length of UTF-8 string "src".
- */
-ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen);
-
-/**
- * Convert UTF-8 to UTF-16 including surrogate pairs.
- * Returns a pointer to the end of the string (where a null terminator might go
- * if you wanted to add one).
- */
-char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst);
-
-/**
- * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer
- * must be large enough to hold the result as measured by utf8_to_utf16_length
- * plus an added NULL terminator.
- */
-void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst);
-
-/**
- * Like utf8_to_utf16_no_null_terminator, but you can supply a maximum length of the
- * decoded string. The decoded string will fill up to that length; if it is longer
- * the returned pointer will be to the character after dstLen.
- */
-char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);
-
-}
-
-#endif
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
deleted file mode 100644
index ed7b725..0000000
--- a/include/utils/Vector.h
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_VECTOR_H
-#define ANDROID_VECTOR_H
-
-#include <new>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <cutils/log.h>
-
-#include <utils/VectorImpl.h>
-#include <utils/TypeHelpers.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-template <typename TYPE>
-class SortedVector;
-
-/*!
- * The main templated vector class ensuring type safety
- * while making use of VectorImpl.
- * This is the class users want to use.
- */
-
-template <class TYPE>
-class Vector : private VectorImpl
-{
-public:
- typedef TYPE value_type;
-
- /*!
- * Constructors and destructors
- */
-
- Vector();
- Vector(const Vector<TYPE>& rhs);
- explicit Vector(const SortedVector<TYPE>& rhs);
- virtual ~Vector();
-
- /*! copy operator */
- const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
- Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
-
- const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
- Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
-
- /*
- * empty the vector
- */
-
- inline void clear() { VectorImpl::clear(); }
-
- /*!
- * vector stats
- */
-
- //! returns number of items in the vector
- inline size_t size() const { return VectorImpl::size(); }
- //! returns whether or not the vector is empty
- inline bool isEmpty() const { return VectorImpl::isEmpty(); }
- //! returns how many items can be stored without reallocating the backing store
- inline size_t capacity() const { return VectorImpl::capacity(); }
- //! sets the capacity. capacity can never be reduced less than size()
- inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
-
- /*!
- * set the size of the vector. items are appended with the default
- * constructor, or removed from the end as needed.
- */
- inline ssize_t resize(size_t size) { return VectorImpl::resize(size); }
-
- /*!
- * C-style array access
- */
-
- //! read-only C-style access
- inline const TYPE* array() const;
- //! read-write C-style access
- TYPE* editArray();
-
- /*!
- * accessors
- */
-
- //! read-only access to an item at a given index
- inline const TYPE& operator [] (size_t index) const;
- //! alternate name for operator []
- inline const TYPE& itemAt(size_t index) const;
- //! stack-usage of the vector. returns the top of the stack (last element)
- const TYPE& top() const;
-
- /*!
- * modifying the array
- */
-
- //! copy-on write support, grants write access to an item
- TYPE& editItemAt(size_t index);
- //! grants right access to the top of the stack (last element)
- TYPE& editTop();
-
- /*!
- * append/insert another vector
- */
-
- //! insert another vector at a given index
- ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index);
-
- //! append another vector at the end of this one
- ssize_t appendVector(const Vector<TYPE>& vector);
-
-
- //! insert an array at a given index
- ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length);
-
- //! append an array at the end of this vector
- ssize_t appendArray(const TYPE* array, size_t length);
-
- /*!
- * add/insert/replace items
- */
-
- //! insert one or several items initialized with their default constructor
- inline ssize_t insertAt(size_t index, size_t numItems = 1);
- //! insert one or several items initialized from a prototype item
- ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
- //! pop the top of the stack (removes the last element). No-op if the stack's empty
- inline void pop();
- //! pushes an item initialized with its default constructor
- inline void push();
- //! pushes an item on the top of the stack
- void push(const TYPE& item);
- //! same as push() but returns the index the item was added at (or an error)
- inline ssize_t add();
- //! same as push() but returns the index the item was added at (or an error)
- ssize_t add(const TYPE& item);
- //! replace an item with a new one initialized with its default constructor
- inline ssize_t replaceAt(size_t index);
- //! replace an item with a new one
- ssize_t replaceAt(const TYPE& item, size_t index);
-
- /*!
- * remove items
- */
-
- //! remove several items
- inline ssize_t removeItemsAt(size_t index, size_t count = 1);
- //! remove one item
- inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
-
- /*!
- * sort (stable) the array
- */
-
- typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
- typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
-
- inline status_t sort(compar_t cmp);
- inline status_t sort(compar_r_t cmp, void* state);
-
- // for debugging only
- inline size_t getItemSize() const { return itemSize(); }
-
-
- /*
- * these inlines add some level of compatibility with STL. eventually
- * we should probably turn things around.
- */
- typedef TYPE* iterator;
- typedef TYPE const* const_iterator;
-
- inline iterator begin() { return editArray(); }
- inline iterator end() { return editArray() + size(); }
- inline const_iterator begin() const { return array(); }
- inline const_iterator end() const { return array() + size(); }
- inline void reserve(size_t n) { setCapacity(n); }
- inline bool empty() const{ return isEmpty(); }
- inline void push_back(const TYPE& item) { insertAt(item, size(), 1); }
- inline void push_front(const TYPE& item) { insertAt(item, 0, 1); }
- inline iterator erase(iterator pos) {
- ssize_t index = removeItemsAt(pos-array());
- return begin() + index;
- }
-
-protected:
- virtual void do_construct(void* storage, size_t num) const;
- virtual void do_destroy(void* storage, size_t num) const;
- virtual void do_copy(void* dest, const void* from, size_t num) const;
- virtual void do_splat(void* dest, const void* item, size_t num) const;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const;
-};
-
-// Vector<T> can be trivially moved using memcpy() because moving does not
-// require any change to the underlying SharedBuffer contents or reference count.
-template<typename T> struct trait_trivial_move<Vector<T> > { enum { value = true }; };
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts from here...
-// ---------------------------------------------------------------------------
-
-template<class TYPE> inline
-Vector<TYPE>::Vector()
- : VectorImpl(sizeof(TYPE),
- ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
- |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
- |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
- )
-{
-}
-
-template<class TYPE> inline
-Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
- : VectorImpl(rhs) {
-}
-
-template<class TYPE> inline
-Vector<TYPE>::Vector(const SortedVector<TYPE>& rhs)
- : VectorImpl(static_cast<const VectorImpl&>(rhs)) {
-}
-
-template<class TYPE> inline
-Vector<TYPE>::~Vector() {
- finish_vector();
-}
-
-template<class TYPE> inline
-Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
- VectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
- VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
- return *this;
-}
-
-template<class TYPE> inline
-Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
- VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
- return *this;
-}
-
-template<class TYPE> inline
-const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
- VectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const TYPE* Vector<TYPE>::array() const {
- return static_cast<const TYPE *>(arrayImpl());
-}
-
-template<class TYPE> inline
-TYPE* Vector<TYPE>::editArray() {
- return static_cast<TYPE *>(editArrayImpl());
-}
-
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::operator[](size_t index) const {
- LOG_FATAL_IF(index>=size(),
- "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__,
- int(index), int(size()));
- return *(array() + index);
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::itemAt(size_t index) const {
- return operator[](index);
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::top() const {
- return *(array() + size() - 1);
-}
-
-template<class TYPE> inline
-TYPE& Vector<TYPE>::editItemAt(size_t index) {
- return *( static_cast<TYPE *>(editItemLocation(index)) );
-}
-
-template<class TYPE> inline
-TYPE& Vector<TYPE>::editTop() {
- return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
- return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
- return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) {
- return VectorImpl::insertArrayAt(array, index, length);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) {
- return VectorImpl::appendArray(array, length);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
- return VectorImpl::insertAt(&item, index, numItems);
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::push(const TYPE& item) {
- return VectorImpl::push(&item);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::add(const TYPE& item) {
- return VectorImpl::add(&item);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
- return VectorImpl::replaceAt(&item, index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
- return VectorImpl::insertAt(index, numItems);
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::pop() {
- VectorImpl::pop();
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::push() {
- VectorImpl::push();
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::add() {
- return VectorImpl::add();
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::replaceAt(size_t index) {
- return VectorImpl::replaceAt(index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
- return VectorImpl::removeItemsAt(index, count);
-}
-
-template<class TYPE> inline
-status_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) {
- return VectorImpl::sort((VectorImpl::compar_t)cmp);
-}
-
-template<class TYPE> inline
-status_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) {
- return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state);
-}
-
-// ---------------------------------------------------------------------------
-
-template<class TYPE>
-void Vector<TYPE>::do_construct(void* storage, size_t num) const {
- construct_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
- destroy_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
- copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
- splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
- move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
- move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-}; // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_VECTOR_H
diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h
deleted file mode 100644
index 21ad71c..0000000
--- a/include/utils/VectorImpl.h
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_VECTOR_IMPL_H
-#define ANDROID_VECTOR_IMPL_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Errors.h>
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts in here...
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-/*!
- * Implementation of the guts of the vector<> class
- * this ensures backward binary compatibility and
- * reduces code size.
- * For performance reasons, we expose mStorage and mCount
- * so these fields are set in stone.
- *
- */
-
-class VectorImpl
-{
-public:
- enum { // flags passed to the ctor
- HAS_TRIVIAL_CTOR = 0x00000001,
- HAS_TRIVIAL_DTOR = 0x00000002,
- HAS_TRIVIAL_COPY = 0x00000004,
- };
-
- VectorImpl(size_t itemSize, uint32_t flags);
- VectorImpl(const VectorImpl& rhs);
- virtual ~VectorImpl();
-
- /*! must be called from subclasses destructor */
- void finish_vector();
-
- VectorImpl& operator = (const VectorImpl& rhs);
-
- /*! C-style array access */
- inline const void* arrayImpl() const { return mStorage; }
- void* editArrayImpl();
-
- /*! vector stats */
- inline size_t size() const { return mCount; }
- inline bool isEmpty() const { return mCount == 0; }
- size_t capacity() const;
- ssize_t setCapacity(size_t size);
- ssize_t resize(size_t size);
-
- /*! append/insert another vector or array */
- ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
- ssize_t appendVector(const VectorImpl& vector);
- ssize_t insertArrayAt(const void* array, size_t index, size_t length);
- ssize_t appendArray(const void* array, size_t length);
-
- /*! add/insert/replace items */
- ssize_t insertAt(size_t where, size_t numItems = 1);
- ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
- void pop();
- void push();
- void push(const void* item);
- ssize_t add();
- ssize_t add(const void* item);
- ssize_t replaceAt(size_t index);
- ssize_t replaceAt(const void* item, size_t index);
-
- /*! remove items */
- ssize_t removeItemsAt(size_t index, size_t count = 1);
- void clear();
-
- const void* itemLocation(size_t index) const;
- void* editItemLocation(size_t index);
-
- typedef int (*compar_t)(const void* lhs, const void* rhs);
- typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state);
- status_t sort(compar_t cmp);
- status_t sort(compar_r_t cmp, void* state);
-
-protected:
- size_t itemSize() const;
- void release_storage();
-
- virtual void do_construct(void* storage, size_t num) const = 0;
- virtual void do_destroy(void* storage, size_t num) const = 0;
- virtual void do_copy(void* dest, const void* from, size_t num) const = 0;
- virtual void do_splat(void* dest, const void* item, size_t num) const = 0;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0;
-
-private:
- void* _grow(size_t where, size_t amount);
- void _shrink(size_t where, size_t amount);
-
- inline void _do_construct(void* storage, size_t num) const;
- inline void _do_destroy(void* storage, size_t num) const;
- inline void _do_copy(void* dest, const void* from, size_t num) const;
- inline void _do_splat(void* dest, const void* item, size_t num) const;
- inline void _do_move_forward(void* dest, const void* from, size_t num) const;
- inline void _do_move_backward(void* dest, const void* from, size_t num) const;
-
- // These 2 fields are exposed in the inlines below,
- // so they're set in stone.
- void * mStorage; // base address of the vector
- size_t mCount; // number of items
-
- const uint32_t mFlags;
- const size_t mItemSize;
-};
-
-
-
-class SortedVectorImpl : public VectorImpl
-{
-public:
- SortedVectorImpl(size_t itemSize, uint32_t flags);
- SortedVectorImpl(const VectorImpl& rhs);
- virtual ~SortedVectorImpl();
-
- SortedVectorImpl& operator = (const SortedVectorImpl& rhs);
-
- //! finds the index of an item
- ssize_t indexOf(const void* item) const;
-
- //! finds where this item should be inserted
- size_t orderOf(const void* item) const;
-
- //! add an item in the right place (or replaces it if there is one)
- ssize_t add(const void* item);
-
- //! merges a vector into this one
- ssize_t merge(const VectorImpl& vector);
- ssize_t merge(const SortedVectorImpl& vector);
-
- //! removes an item
- ssize_t remove(const void* item);
-
-protected:
- virtual int do_compare(const void* lhs, const void* rhs) const = 0;
-
-private:
- ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
-
- // these are made private, because they can't be used on a SortedVector
- // (they don't have an implementation either)
- ssize_t add();
- void pop();
- void push();
- void push(const void* item);
- ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
- ssize_t appendVector(const VectorImpl& vector);
- ssize_t insertArrayAt(const void* array, size_t index, size_t length);
- ssize_t appendArray(const void* array, size_t length);
- ssize_t insertAt(size_t where, size_t numItems = 1);
- ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
- ssize_t replaceAt(size_t index);
- ssize_t replaceAt(const void* item, size_t index);
-};
-
-}; // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_VECTOR_IMPL_H
diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h
deleted file mode 100644
index 0854775..0000000
--- a/include/utils/ashmem.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* utils/ashmem.h
- **
- ** Copyright 2008 The Android Open Source Project
- **
- ** This file is dual licensed. It may be redistributed and/or modified
- ** under the terms of the Apache 2.0 License OR version 2 of the GNU
- ** General Public License.
- */
-
-#ifndef _UTILS_ASHMEM_H
-#define _UTILS_ASHMEM_H
-
-#include <linux/limits.h>
-#include <linux/ioctl.h>
-
-#define ASHMEM_NAME_LEN 256
-
-#define ASHMEM_NAME_DEF "dev/ashmem"
-
-/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
-#define ASHMEM_NOT_REAPED 0
-#define ASHMEM_WAS_REAPED 1
-
-/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
-#define ASHMEM_NOW_UNPINNED 0
-#define ASHMEM_NOW_PINNED 1
-
-#define __ASHMEMIOC 0x77
-
-#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
-#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
-#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
-#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
-#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long)
-#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6)
-#define ASHMEM_PIN _IO(__ASHMEMIOC, 7)
-#define ASHMEM_UNPIN _IO(__ASHMEMIOC, 8)
-#define ASHMEM_ISPINNED _IO(__ASHMEMIOC, 9)
-#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)
-
-#endif /* _UTILS_ASHMEM_H */
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 3591a6b..31fc2df 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -26,8 +26,6 @@
#include <sys/types.h>
#include <utils/Compat.h>
-__BEGIN_DECLS
-
/* Zip compression methods we support */
enum {
kCompressStored = 0, // no compression
@@ -43,8 +41,7 @@
/*
* entry_name has to be an c-style string with only ASCII characters.
*/
- explicit ZipString(const char* entry_name)
- : name(reinterpret_cast<const uint8_t*>(entry_name)), name_length(strlen(entry_name)) {}
+ explicit ZipString(const char* entry_name);
bool operator==(const ZipString& rhs) const {
return name && (name_length == rhs.name_length) &&
@@ -131,6 +128,8 @@
int32_t OpenArchiveFd(const int fd, const char* debugFileName,
ZipArchiveHandle *handle, bool assume_ownership = true);
+int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName,
+ ZipArchiveHandle *handle);
/*
* Close archive, releasing resources associated with it. This will
* unmap the central directory of the zipfile and free all internal
@@ -152,6 +151,9 @@
* if this file entry contains a data descriptor footer. To verify crc32s
* and length, a call to VerifyCrcAndLengths must be made after entry data
* has been processed.
+ *
+ * On non-Windows platforms this method does not modify internal state and
+ * can be called concurrently.
*/
int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
ZipEntry* data);
@@ -191,7 +193,8 @@
* Uncompress and write an entry to an open file identified by |fd|.
* |entry->uncompressed_length| bytes will be written to the file at
* its current offset, and the file will be truncated at the end of
- * the uncompressed data.
+ * the uncompressed data (no truncation if |fd| references a block
+ * device).
*
* Returns 0 on success and negative values on failure.
*/
@@ -212,6 +215,15 @@
const char* ErrorCodeString(int32_t error_code);
-__END_DECLS
+#if !defined(_WIN32)
+typedef bool (*ProcessZipEntryFunction)(const uint8_t* buf, size_t buf_size, void* cookie);
+
+/*
+ * Stream the uncompressed data through the supplied function,
+ * passing cookie to it each time it gets called.
+*/
+int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+ ProcessZipEntryFunction func, void* cookie);
+#endif
#endif // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/include/ziparchive/zip_archive_stream_entry.h b/include/ziparchive/zip_archive_stream_entry.h
new file mode 100644
index 0000000..a40b799
--- /dev/null
+++ b/include/ziparchive/zip_archive_stream_entry.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+// Read-only stream access to Zip archives entries.
+#ifndef LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
+#define LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
+
+#include <vector>
+
+#include <ziparchive/zip_archive.h>
+
+class ZipArchiveStreamEntry {
+ public:
+ virtual ~ZipArchiveStreamEntry() {}
+
+ virtual const std::vector<uint8_t>* Read() = 0;
+
+ virtual bool Verify() = 0;
+
+ static ZipArchiveStreamEntry* Create(ZipArchiveHandle handle, const ZipEntry& entry);
+ static ZipArchiveStreamEntry* CreateRaw(ZipArchiveHandle handle, const ZipEntry& entry);
+
+ protected:
+ ZipArchiveStreamEntry(ZipArchiveHandle handle) : handle_(handle) {}
+
+ virtual bool Init(const ZipEntry& entry);
+
+ ZipArchiveHandle handle_;
+
+ uint32_t crc32_;
+};
+
+#endif // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
new file mode 100644
index 0000000..0b6ede4
--- /dev/null
+++ b/include/ziparchive/zip_writer.h
@@ -0,0 +1,166 @@
+/*
+ * 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 LIBZIPARCHIVE_ZIPWRITER_H_
+#define LIBZIPARCHIVE_ZIPWRITER_H_
+
+#include "android-base/macros.h"
+#include <utils/Compat.h>
+
+#include <cstdio>
+#include <ctime>
+#include <memory>
+#include <string>
+#include <vector>
+#include <zlib.h>
+
+/**
+ * Writes a Zip file via a stateful interface.
+ *
+ * Example:
+ *
+ * FILE* file = fopen("path/to/zip.zip", "wb");
+ *
+ * ZipWriter writer(file);
+ *
+ * writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
+ * writer.WriteBytes(buffer, bufferLen);
+ * writer.WriteBytes(buffer2, bufferLen2);
+ * writer.FinishEntry();
+ *
+ * writer.StartEntry("empty.txt", 0);
+ * writer.FinishEntry();
+ *
+ * writer.Finish();
+ *
+ * fclose(file);
+ */
+class ZipWriter {
+public:
+ enum {
+ /**
+ * Flag to compress the zip entry using deflate.
+ */
+ kCompress = 0x01,
+
+ /**
+ * Flag to align the zip entry data on a 32bit boundary. Useful for
+ * mmapping the data at runtime.
+ */
+ kAlign32 = 0x02,
+ };
+
+ static const char* ErrorCodeString(int32_t error_code);
+
+ /**
+ * Create a ZipWriter that will write into a FILE stream. The file should be opened with
+ * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
+ * caller is responsible for closing the file.
+ */
+ explicit ZipWriter(FILE* f);
+
+ // Move constructor.
+ ZipWriter(ZipWriter&& zipWriter);
+
+ // Move assignment.
+ ZipWriter& operator=(ZipWriter&& zipWriter);
+
+ /**
+ * Starts a new zip entry with the given path and flags.
+ * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
+ * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t StartEntry(const char* path, size_t flags);
+
+ /**
+ * Starts a new zip entry with the given path and flags, where the
+ * entry will be aligned to the given alignment.
+ * Flags can only be ZipWriter::kCompress. Using the flag ZipWriter::kAlign32
+ * will result in an error.
+ * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t StartAlignedEntry(const char* path, size_t flags, uint32_t alignment);
+
+ /**
+ * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
+ */
+ int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
+
+ /**
+ * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
+ */
+ int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+ uint32_t alignment);
+
+ /**
+ * Writes bytes to the zip file for the previously started zip entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t WriteBytes(const void* data, size_t len);
+
+ /**
+ * Finish a zip entry started with StartEntry(const char*, size_t) or
+ * StartEntryWithTime(const char*, size_t, time_t). This must be called before
+ * any new zip entries are started, or before Finish() is called.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t FinishEntry();
+
+ /**
+ * Writes the Central Directory Headers and flushes the zip file stream.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t Finish();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ZipWriter);
+
+ struct FileInfo {
+ std::string path;
+ uint16_t compression_method;
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ uint32_t local_file_header_offset;
+ };
+
+ int32_t HandleError(int32_t error_code);
+ int32_t PrepareDeflate();
+ int32_t StoreBytes(FileInfo* file, const void* data, size_t len);
+ int32_t CompressBytes(FileInfo* file, const void* data, size_t len);
+ int32_t FlushCompressedBytes(FileInfo* file);
+
+ enum class State {
+ kWritingZip,
+ kWritingEntry,
+ kDone,
+ kError,
+ };
+
+ FILE* file_;
+ off64_t current_offset_;
+ State state_;
+ std::vector<FileInfo> files_;
+
+ std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
+ std::vector<uint8_t> buffer_;
+};
+
+#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/init/Android.mk b/init/Android.mk
index 58bff58..9e61fb2 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -45,13 +45,17 @@
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
action.cpp \
+ capabilities.cpp \
+ descriptors.cpp \
+ import_parser.cpp \
init_parser.cpp \
log.cpp \
parser.cpp \
service.cpp \
util.cpp \
-LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup libnl
+LOCAL_WHOLE_STATIC_LIBRARIES := libcap
LOCAL_MODULE := libinit
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
@@ -73,7 +77,6 @@
LOCAL_MODULE:= init
LOCAL_C_INCLUDES += \
- system/extras/ext4_utils \
system/core/mkbootimg
LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -82,21 +85,29 @@
LOCAL_STATIC_LIBRARIES := \
libinit \
+ libbootloader_message \
libfs_mgr \
+ libfec \
+ libfec_rs \
libsquashfs_utils \
liblogwrap \
libcutils \
+ libext4_utils \
libbase \
- libext4_utils_static \
- libutils \
- liblog \
libc \
libselinux \
- libmincrypt \
+ liblog \
+ libcrypto_utils \
+ libcrypto \
libc++_static \
- libdl
+ libdl \
+ libsparse \
+ libz \
+ libprocessgroup \
+ libnl \
+ libavb
-# Create symlinks
+# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
@@ -106,8 +117,8 @@
include $(BUILD_EXECUTABLE)
-
-
+# Unit tests.
+# =========================================================
include $(CLEAR_VARS)
LOCAL_MODULE := init_tests
LOCAL_SRC_FILES := \
@@ -121,4 +132,5 @@
LOCAL_STATIC_LIBRARIES := libinit
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
+LOCAL_CPPFLAGS := -Wall -Wextra -Werror
include $(BUILD_NATIVE_TEST)
diff --git a/init/README.md b/init/README.md
new file mode 100644
index 0000000..c76a33b
--- /dev/null
+++ b/init/README.md
@@ -0,0 +1,566 @@
+Android Init Language
+---------------------
+
+The Android Init Language consists of five broad classes of statements:
+Actions, Commands, Services, Options, and Imports.
+
+All of these are line-oriented, consisting of tokens separated by
+whitespace. The c-style backslash escapes may be used to insert
+whitespace into a token. Double quotes may also be used to prevent
+whitespace from breaking text into multiple tokens. The backslash,
+when it is the last character on a line, may be used for line-folding.
+
+Lines which start with a # (leading whitespace allowed) are comments.
+
+Actions and Services implicitly declare a new section. All commands
+or options belong to the section most recently declared. Commands
+or options before the first section are ignored.
+
+Actions and Services have unique names. If a second Action is defined
+with the same name as an existing one, its commands are appended to
+the commands of the existing action. If a second Service is defined
+with the same name as an existing one, it is ignored and an error
+message is logged.
+
+
+Init .rc Files
+--------------
+The init language is used in plain text files that take the .rc file
+extension. These are typically multiple of these in multiple
+locations on the system, described below.
+
+/init.rc is the primary .rc file and is loaded by the init executable
+at the beginning of its execution. It is responsible for the initial
+set up of the system. It imports /init.${ro.hardware}.rc which is the
+primary vendor supplied .rc file.
+
+During the mount\_all command, the init executable loads all of the
+files contained within the /{system,vendor,odm}/etc/init/ directories.
+These directories are intended for all Actions and Services used after
+file system mounting.
+
+One may specify paths in the mount\_all command line to have it import
+.rc files at the specified paths instead of the default ones listed above.
+This is primarily for supporting factory mode and other non-standard boot
+modes. The three default paths should be used for the normal boot process.
+
+The intention of these directories is:
+
+ 1. /system/etc/init/ is for core system items such as
+ SurfaceFlinger, MediaService, and logcatd.
+ 2. /vendor/etc/init/ is for SoC vendor items such as actions or
+ daemons needed for core SoC functionality.
+ 3. /odm/etc/init/ is for device manufacturer items such as
+ actions or daemons needed for motion sensor or other peripheral
+ functionality.
+
+All services whose binaries reside on the system, vendor, or odm
+partitions should have their service entries placed into a
+corresponding init .rc file, located in the /etc/init/
+directory of the partition where they reside. There is a build
+system macro, LOCAL\_INIT\_RC, that handles this for developers. Each
+init .rc file should additionally contain any actions associated with
+its service.
+
+An example is the logcatd.rc and Android.mk files located in the
+system/core/logcat directory. The LOCAL\_INIT\_RC macro in the
+Android.mk file places logcatd.rc in /system/etc/init/ during the
+build process. Init loads logcatd.rc during the mount\_all command and
+allows the service to be run and the action to be queued when
+appropriate.
+
+This break up of init .rc files according to their daemon is preferred
+to the previously used monolithic init .rc files. This approach
+ensures that the only service entries that init reads and the only
+actions that init performs correspond to services whose binaries are in
+fact present on the file system, which was not the case with the
+monolithic init .rc files. This additionally will aid in merge
+conflict resolution when multiple services are added to the system, as
+each one will go into a separate file.
+
+There are two options "early" and "late" in mount\_all command
+which can be set after optional paths. With "--early" set, the
+init executable will skip mounting entries with "latemount" flag
+and triggering fs encryption state event. With "--late" set,
+init executable will only mount entries with "latemount" flag but skip
+importing rc files. By default, no option is set, and mount\_all will
+process all entries in the given fstab.
+
+Actions
+-------
+Actions are named sequences of commands. Actions have a trigger which
+is used to determine when the action should occur. When an event
+occurs which matches an action's trigger, that action is added to
+the tail of a to-be-executed queue (unless it is already on the
+queue).
+
+Each action in the queue is dequeued in sequence and each command in
+that action is executed in sequence. Init handles other activities
+(device creation/destruction, property setting, process restarting)
+"between" the execution of the commands in activities.
+
+Actions take the form of:
+
+ on <trigger> [&& <trigger>]*
+ <command>
+ <command>
+ <command>
+
+
+Services
+--------
+Services are programs which init launches and (optionally) restarts
+when they exit. Services take the form of:
+
+ service <name> <pathname> [ <argument> ]*
+ <option>
+ <option>
+ ...
+
+
+Options
+-------
+Options are modifiers to services. They affect how and when init
+runs the service.
+
+`console [<console>]`
+> This service needs a console. The optional second parameter chooses a
+ specific console instead of the default. The default "/dev/console" can
+ be changed by setting the "androidboot.console" kernel parameter. In
+ all cases the leading "/dev/" should be omitted, so "/dev/tty0" would be
+ specified as just "console tty0".
+
+`critical`
+> This is a device-critical service. If it exits more than four times in
+ four minutes, the device will reboot into recovery mode.
+
+`disabled`
+> This service will not automatically start with its class.
+ It must be explicitly started by name.
+
+`setenv <name> <value>`
+> Set the environment variable _name_ to _value_ in the launched process.
+
+`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
+> Create a unix domain socket named /dev/socket/_name_ and pass its fd to the
+ launched process. _type_ must be "dgram", "stream" or "seqpacket". User and
+ group default to 0. 'seclabel' is the SELinux security context for the
+ socket. It defaults to the service security context, as specified by
+ seclabel or computed based on the service executable file security context.
+ For native executables see libcutils android\_get\_control\_socket().
+
+`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
+ android\_get\_control\_file().
+
+`user <username>`
+> Change to 'username' before exec'ing this service.
+ Currently defaults to root. (??? probably should default to nobody)
+ As of Android M, processes should use this option even if they
+ require Linux capabilities. Previously, to acquire Linux
+ capabilities, a process would need to run as root, request the
+ capabilities, then drop to its desired uid. There is a new
+ mechanism through fs\_config that allows device manufacturers to add
+ Linux capabilities to specific binaries on a file system that should
+ be used instead. This mechanism is described on
+ <http://source.android.com/devices/tech/config/filesystem.html>. When
+ using this new mechanism, processes can use the user option to
+ select their desired uid without ever running as root.
+ As of Android O, processes can also request capabilities directly in their .rc
+ files. See the "capabilities" option below.
+
+`group <groupname> [ <groupname>\* ]`
+> Change to 'groupname' before exec'ing this service. Additional
+ groupnames beyond the (required) first one are used to set the
+ supplemental groups of the process (via setgroups()).
+ Currently defaults to root. (??? probably should default to nobody)
+
+`capabilities <capability> [ <capability>\* ]`
+> Set capabilities when exec'ing this service. 'capability' should be a Linux
+ capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
+ http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
+ capabilities.
+
+`seclabel <seclabel>`
+> Change to 'seclabel' before exec'ing this service.
+ Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
+ Services on the system partition can instead use policy-defined transitions
+ based on their file security context.
+ If not specified and no transition is defined in policy, defaults to the init context.
+
+`oneshot`
+> Do not restart the service when it exits.
+
+`class <name>`
+> Specify a class name for the service. All services in a
+ named class may be started or stopped together. A service
+ is in the class "default" if one is not specified via the
+ class option.
+
+`onrestart`
+> Execute a Command (see below) when service restarts.
+
+`writepid <file...>`
+> Write the child's pid to the given files when it forks. Meant for
+ cgroup/cpuset usage.
+
+`priority <priority>`
+> Scheduling priority of the service process. This value has to be in range
+ -20 to 19. Default priority is 0. Priority is set via setpriority().
+
+`namespace <pid|mnt>`
+> Enter a new PID or mount namespace when forking the service.
+
+`oom_score_adjust <value>`
+> Sets the child's /proc/self/oom\_score\_adj to the specified value,
+ which must range from -1000 to 1000.
+
+
+Triggers
+--------
+Triggers are strings which can be used to match certain kinds of
+events and used to cause an action to occur.
+
+Triggers are subdivided into event triggers and property triggers.
+
+Event triggers are strings triggered by the 'trigger' command or by
+the QueueEventTrigger() function within the init executable. These
+take the form of a simple string such as 'boot' or 'late-init'.
+
+Property triggers are strings triggered when a named property changes
+value to a given new value or when a named property changes value to
+any new value. These take the form of 'property:<name>=<value>' and
+'property:<name>=\*' respectively. Property triggers are additionally
+evaluated and triggered accordingly during the initial boot phase of
+init.
+
+An Action can have multiple property triggers but may only have one
+event trigger.
+
+For example:
+`on boot && property:a=b` defines an action that is only executed when
+the 'boot' event trigger happens and the property a equals b.
+
+`on property:a=b && property:c=d` defines an action that is executed
+at three times:
+
+ 1. During initial boot if property a=b and property c=d.
+ 2. Any time that property a transitions to value b, while property c already equals d.
+ 3. Any time that property c transitions to value d, while property a already equals b.
+
+
+Commands
+--------
+
+`bootchart [start|stop]`
+> Start/stop bootcharting. These are present in the default init.rc files,
+ but bootcharting is only active if the file /data/bootchart/enabled exists;
+ otherwise bootchart start/stop are no-ops.
+
+`chmod <octal-mode> <path>`
+> Change file access permissions.
+
+`chown <owner> <group> <path>`
+> Change file owner and group.
+
+`class_start <serviceclass>`
+> Start all services of the specified class if they are
+ not already running.
+
+`class_stop <serviceclass>`
+> Stop and disable all services of the specified class if they are
+ currently running.
+
+`class_reset <serviceclass>`
+> Stop all services of the specified class if they are
+ currently running, without disabling them. They can be restarted
+ later using `class_start`.
+
+`copy <src> <dst>`
+> Copies a file. Similar to write, but useful for binary/large
+ amounts of data.
+
+`domainname <name>`
+> Set the domain name.
+
+`enable <servicename>`
+> Turns a disabled service into an enabled one as if the service did not
+ specify disabled.
+ If the service is supposed to be running, it will be started now.
+ Typically used when the bootloader sets a variable that indicates a specific
+ service should be started when needed. E.g.
+
+ on property:ro.boot.myfancyhardware=1
+ enable my_fancy_service_for_my_fancy_hardware
+
+`exec [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ]`
+> Fork and execute command with the given arguments. The command starts
+ after "--" so that an optional security context, user, and supplementary
+ groups can be provided. No other commands will be run until this one
+ finishes. _seclabel_ can be a - to denote default. Properties are expanded
+ within _argument_.
+
+`export <name> <value>`
+> Set the environment variable _name_ equal to _value_ in the
+ global environment (which will be inherited by all processes
+ started after this command is executed)
+
+`hostname <name>`
+> Set the host name.
+
+`ifup <interface>`
+> Bring the network interface _interface_ online.
+
+`insmod [-f] <path> [<options>]`
+> Install the module at _path_ with the specified options.
+ -f: force installation of the module even if the version of the running kernel
+ and the version of the kernel for which the module was compiled do not match.
+
+`load_all_props`
+> Loads properties from /system, /vendor, et cetera.
+ This is included in the default init.rc.
+
+`load_persist_props`
+> Loads persistent properties when /data has been decrypted.
+ This is included in the default init.rc.
+
+`loglevel <level>`
+> Sets the kernel log level to level. Properties are expanded within _level_.
+
+`mkdir <path> [mode] [owner] [group]`
+> Create a directory at _path_, optionally with the given mode, owner, and
+ group. If not provided, the directory is created with permissions 755 and
+ owned by the root user and root group. If provided, the mode, owner and group
+ will be updated if the directory exists already.
+
+`mount_all <fstab> [ <path> ]\* [--<option>]`
+> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab and imports .rc files
+ at the specified paths (e.g., on the partitions just mounted) with optional
+ options "early" and "late".
+ Refer to the section of "Init .rc Files" for detail.
+
+`mount <type> <device> <dir> [ <flag>\* ] [<options>]`
+> Attempt to mount the named device at the directory _dir_
+ _flag_s include "ro", "rw", "remount", "noatime", ...
+ _options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
+ a comma separated string, eg: barrier=1,noauto\_da\_alloc
+
+`powerctl`
+> Internal implementation detail used to respond to changes to the
+ "sys.powerctl" system property, used to implement rebooting.
+
+`restart <service>`
+> Like stop, but doesn't disable the service.
+
+`restorecon <path> [ <path>\* ]`
+> Restore the file named by _path_ to the security context specified
+ in the file\_contexts configuration.
+ Not required for directories created by the init.rc as these are
+ automatically labeled correctly by init.
+
+`restorecon_recursive <path> [ <path>\* ]`
+> Recursively restore the directory tree named by _path_ to the
+ security contexts specified in the file\_contexts configuration.
+
+`rm <path>`
+> Calls unlink(2) on the given path. You might want to
+ use "exec -- rm ..." instead (provided the system partition is
+ already mounted).
+
+`rmdir <path>`
+> Calls rmdir(2) on the given path.
+
+`setprop <name> <value>`
+> Set system property _name_ to _value_. Properties are expanded
+ within _value_.
+
+`setrlimit <resource> <cur> <max>`
+> Set the rlimit for a resource.
+
+`start <service>`
+> Start a service running if it is not already running.
+
+`stop <service>`
+> Stop a service from running if it is currently running.
+
+`swapon_all <fstab>`
+> Calls fs\_mgr\_swapon\_all on the given fstab file.
+
+`symlink <target> <path>`
+> Create a symbolic link at _path_ with the value _target_
+
+`sysclktz <mins_west_of_gmt>`
+> Set the system clock base (0 if system clock ticks in GMT)
+
+`trigger <event>`
+> Trigger an event. Used to queue an action from another
+ action.
+
+`umount <path>`
+> Unmount the filesystem mounted at that path.
+
+`verity_load_state`
+> Internal implementation detail used to load dm-verity state.
+
+`verity_update_state <mount-point>`
+> Internal implementation detail used to update dm-verity state and
+ set the partition._mount-point_.verified properties used by adb remount
+ because fs\_mgr can't set them directly itself.
+
+`wait <path> [ <timeout> ]`
+> Poll for the existence of the given file and return when found,
+ or the timeout has been reached. If timeout is not specified it
+ currently defaults to five seconds.
+
+`wait_for_prop <name> <value>`
+> Wait for system property _name_ to be _value_. Properties are expanded
+ within _value_. If property _name_ is already set to _value_, continue
+ immediately.
+
+`write <path> <content>`
+> Open the file at _path_ and write a string to it with write(2).
+ If the file does not exist, it will be created. If it does exist,
+ it will be truncated. Properties are expanded within _content_.
+
+
+Imports
+-------
+The import keyword is not a command, but rather its own section and is
+handled immediately after the .rc file that contains it has finished
+being parsed. It takes the below form:
+
+`import <path>`
+> Parse an init config file, extending the current configuration.
+ If _path_ is a directory, each file in the directory is parsed as
+ a config file. It is not recursive, nested directories will
+ not be parsed.
+
+There are only two times where the init executable imports .rc files:
+
+ 1. When it imports /init.rc during initial boot
+ 2. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+ paths during mount_all
+
+
+Properties
+----------
+Init provides information about the services that it is responsible
+for via the below properties.
+
+`init.svc.<name>`
+> State of a named service ("stopped", "stopping", "running", "restarting")
+
+
+Boot timing
+-----------
+Init records some boot timing information in system properties.
+
+`ro.boottime.init`
+> Time after boot in ns (via the CLOCK\_BOOTTIME clock) at which the first
+ stage of init started.
+
+`ro.boottime.init.selinux`
+> How long it took the first stage to initialize SELinux.
+
+`ro.boottime.init.cold_boot_wait`
+> How long init waited for ueventd's coldboot phase to end.
+
+`ro.boottime.<service-name>`
+> Time after boot in ns (via the CLOCK\_BOOTTIME clock) that the service was
+ first started.
+
+
+Bootcharting
+------------
+This version of init contains code to perform "bootcharting": generating log
+files that can be later processed by the tools provided by <http://www.bootchart.org/>.
+
+On the emulator, use the -bootchart _timeout_ option to boot with bootcharting
+activated for _timeout_ seconds.
+
+On a device:
+
+ adb shell 'touch /data/bootchart/enabled'
+
+Don't forget to delete this file when you're done collecting data!
+
+The log files are written to /data/bootchart/. A script is provided to
+retrieve them and create a bootchart.tgz file that can be used with the
+bootchart command-line utility:
+
+ sudo apt-get install pybootchartgui
+ # grab-bootchart.sh uses $ANDROID_SERIAL.
+ $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
+
+One thing to watch for is that the bootchart will show init as if it started
+running at 0s. You'll have to look at dmesg to work out when the kernel
+actually started init.
+
+
+Comparing two bootcharts
+------------------------
+A handy script named compare-bootcharts.py can be used to compare the
+start/end time of selected processes. The aforementioned grab-bootchart.sh
+will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
+If two such barballs are preserved on the host machine under different
+directories, the script can list the timestamps differences. For example:
+
+Usage: system/core/init/compare-bootcharts.py _base-bootchart-dir_ _exp-bootchart-dir_
+
+ process: baseline experiment (delta) - Unit is ms (a jiffy is 10 ms on the system)
+ ------------------------------------
+ /init: 50 40 (-10)
+ /system/bin/surfaceflinger: 4320 4470 (+150)
+ /system/bin/bootanimation: 6980 6990 (+10)
+ zygote64: 10410 10640 (+230)
+ zygote: 10410 10640 (+230)
+ system_server: 15350 15150 (-200)
+ bootanimation ends at: 33790 31230 (-2560)
+
+
+Systrace
+--------
+Systrace (<http://developer.android.com/tools/help/systrace.html>) can be
+used for obtaining performance analysis reports during boot
+time on userdebug or eng builds.
+
+Here is an example of trace events of "wm" and "am" categories:
+
+ $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py \
+ wm am --boot
+
+This command will cause the device to reboot. After the device is rebooted and
+the boot sequence has finished, the trace report is obtained from the device
+and written as trace.html on the host by hitting Ctrl+C.
+
+Limitation: recording trace events is started after persistent properties are loaded, so
+the trace events that are emitted before that are not recorded. Several
+services such as vold, surfaceflinger, and servicemanager are affected by this
+limitation since they are started before persistent properties are loaded.
+Zygote initialization and the processes that are forked from the zygote are not
+affected.
+
+
+Debugging init
+--------------
+By default, programs executed by init will drop stdout and stderr into
+/dev/null. To help with debugging, you can execute your program via the
+Android program logwrapper. This will redirect stdout/stderr into the
+Android logging system (accessed via logcat).
+
+For example
+service akmd /system/bin/logwrapper /sbin/akmd
+
+For quicker turnaround when working on init itself, use:
+
+ mm -j &&
+ m ramdisk-nodeps &&
+ m bootimage-nodeps &&
+ adb reboot bootloader &&
+ fastboot boot $ANDROID_PRODUCT_OUT/boot.img
+
+Alternatively, use the emulator:
+
+ emulator -partition-size 1024 \
+ -verbose -show-kernel -no-window
diff --git a/init/action.cpp b/init/action.cpp
index 2eb809e..65bf292 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,50 +18,31 @@
#include <errno.h>
-#include <base/strings.h>
-#include <base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+#include "builtins.h"
#include "error.h"
#include "init_parser.h"
#include "log.h"
#include "property_service.h"
#include "util.h"
-class Action::Command
-{
-public:
- Command(int (*f)(const std::vector<std::string>& args),
- const std::vector<std::string>& args,
- const std::string& filename,
- int line);
+using android::base::Join;
+using android::base::StringPrintf;
- int InvokeFunc() const;
- std::string BuildCommandString() const;
- std::string BuildSourceString() const;
-
-private:
- int (*func_)(const std::vector<std::string>& args);
- const std::vector<std::string> args_;
- const std::string filename_;
- int line_;
-};
-
-Action::Command::Command(int (*f)(const std::vector<std::string>& args),
- const std::vector<std::string>& args,
- const std::string& filename,
- int line) :
- func_(f), args_(args), filename_(filename), line_(line)
-{
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
+ const std::string& filename, int line)
+ : func_(f), args_(args), filename_(filename), line_(line) {
}
-int Action::Command::InvokeFunc() const
-{
+int Command::InvokeFunc() const {
std::vector<std::string> expanded_args;
expanded_args.resize(args_.size());
expanded_args[0] = args_[0];
for (std::size_t i = 1; i < args_.size(); ++i) {
- if (expand_props(args_[i], &expanded_args[i]) == -1) {
- ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
+ if (!expand_props(args_[i], &expanded_args[i])) {
+ LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
return -EINVAL;
}
}
@@ -69,67 +50,91 @@
return func_(expanded_args);
}
-std::string Action::Command::BuildCommandString() const
-{
- return android::base::Join(args_, ' ');
+std::string Command::BuildCommandString() const {
+ return Join(args_, ' ');
}
-std::string Action::Command::BuildSourceString() const
-{
+std::string Command::BuildSourceString() const {
if (!filename_.empty()) {
- return android::base::StringPrintf(" (%s:%d)", filename_.c_str(), line_);
+ return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
} else {
return std::string();
}
}
-Action::Action()
-{
+Action::Action(bool oneshot) : oneshot_(oneshot) {
}
-void Action::AddCommand(int (*f)(const std::vector<std::string>& args),
+const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
+
+bool Action::AddCommand(const std::vector<std::string>& args,
+ const std::string& filename, int line, std::string* err) {
+ if (!function_map_) {
+ *err = "no function map available";
+ return false;
+ }
+
+ if (args.empty()) {
+ *err = "command needed, but not provided";
+ return false;
+ }
+
+ auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+ if (!function) {
+ return false;
+ }
+
+ AddCommand(function, args, filename, line);
+ return true;
+}
+
+void Action::AddCommand(BuiltinFunction f,
const std::vector<std::string>& args,
- const std::string& filename, int line)
-{
- Action::Command* cmd = new Action::Command(f, args, filename, line);
- commands_.push_back(cmd);
+ const std::string& filename, int line) {
+ commands_.emplace_back(f, args, filename, line);
}
-std::size_t Action::NumCommands() const
-{
- return commands_.size();
-}
-
-void Action::ExecuteOneCommand(std::size_t command) const
-{
- ExecuteCommand(*commands_[command]);
-}
-
-void Action::ExecuteAllCommands() const
-{
- for (const auto& c : commands_) {
- ExecuteCommand(*c);
+void Action::CombineAction(const Action& action) {
+ for (const auto& c : action.commands_) {
+ commands_.emplace_back(c);
}
}
-void Action::ExecuteCommand(const Command& command) const
-{
+std::size_t Action::NumCommands() const {
+ return commands_.size();
+}
+
+void Action::ExecuteOneCommand(std::size_t command) const {
+ // We need a copy here since some Command execution may result in
+ // changing commands_ vector by importing .rc files through parser
+ Command cmd = commands_[command];
+ ExecuteCommand(cmd);
+}
+
+void Action::ExecuteAllCommands() const {
+ for (const auto& c : commands_) {
+ ExecuteCommand(c);
+ }
+}
+
+void Action::ExecuteCommand(const Command& command) const {
Timer t;
int result = command.InvokeFunc();
- if (klog_get_level() >= KLOG_INFO_LEVEL) {
+ double duration_ms = t.duration_s() * 1000;
+ // Any action longer than 50ms will be warned to user as slow operation
+ if (duration_ms > 50.0 ||
+ android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
std::string source = command.BuildSourceString();
- INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
- cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
- result, t.duration());
+ LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
+ << " returned " << result << " took " << duration_ms << "ms.";
}
}
-bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err)
-{
+bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
size_t equal_pos = prop_name.find('=');
@@ -149,12 +154,16 @@
return true;
}
-bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err)
-{
+bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
const static std::string prop_str("property:");
for (std::size_t i = 0; i < args.size(); ++i) {
+ if (args[i].empty()) {
+ *err = "empty trigger is not valid";
+ return false;
+ }
+
if (i % 2) {
- if (args[i].compare("&&")) {
+ if (args[i] != "&&") {
*err = "&& is the only symbol allowed to concatenate actions";
return false;
} else {
@@ -179,34 +188,43 @@
return true;
}
-bool Action::InitSingleTrigger(const std::string& trigger)
-{
+bool Action::InitSingleTrigger(const std::string& trigger) {
std::vector<std::string> name_vector{trigger};
std::string err;
- return InitTriggers(name_vector, &err);
+ bool ret = InitTriggers(name_vector, &err);
+ if (!ret) {
+ LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
+ }
+ return ret;
}
+// This function checks that all property triggers are satisfied, that is
+// for each (name, value) in property_triggers_, check that the current
+// value of the property 'name' == value.
+//
+// It takes an optional (name, value) pair, which if provided must
+// be present in property_triggers_; it skips the check of the current
+// property value for this pair.
bool Action::CheckPropertyTriggers(const std::string& name,
- const std::string& value) const
-{
- bool found = !name.compare("");
+ const std::string& value) const {
if (property_triggers_.empty()) {
return true;
}
+ bool found = name.empty();
for (const auto& t : property_triggers_) {
- if (!t.first.compare(name)) {
- if (t.second.compare("*") &&
- t.second.compare(value)) {
+ const auto& trigger_name = t.first;
+ const auto& trigger_value = t.second;
+ if (trigger_name == name) {
+ if (trigger_value != "*" && trigger_value != value) {
return false;
} else {
found = true;
}
} else {
- std::string prop_val = property_get(t.first.c_str());
- if (prop_val.empty() ||
- (t.second.compare("*") &&
- t.second.compare(prop_val))) {
+ std::string prop_val = property_get(trigger_name.c_str());
+ if (prop_val.empty() || (trigger_value != "*" &&
+ trigger_value != prop_val)) {
return false;
}
}
@@ -214,29 +232,23 @@
return found;
}
-bool Action::CheckEventTrigger(const std::string& trigger) const
-{
+bool Action::CheckEventTrigger(const std::string& trigger) const {
return !event_trigger_.empty() &&
- !trigger.compare(event_trigger_) &&
+ trigger == event_trigger_ &&
CheckPropertyTriggers();
}
bool Action::CheckPropertyTrigger(const std::string& name,
- const std::string& value) const
-{
+ const std::string& value) const {
return event_trigger_.empty() && CheckPropertyTriggers(name, value);
}
-bool Action::TriggersEqual(const class Action& other) const
-{
- return property_triggers_.size() == other.property_triggers_.size() &&
- std::equal(property_triggers_.begin(), property_triggers_.end(),
- other.property_triggers_.begin()) &&
- !event_trigger_.compare(other.event_trigger_);
+bool Action::TriggersEqual(const Action& other) const {
+ return property_triggers_ == other.property_triggers_ &&
+ event_trigger_ == other.event_trigger_;
}
-std::string Action::BuildTriggersString() const
-{
+std::string Action::BuildTriggersString() const {
std::string result;
for (const auto& t : property_triggers_) {
@@ -253,22 +265,52 @@
return result;
}
-void Action::DumpState() const
-{
- INFO("on ");
+void Action::DumpState() const {
std::string trigger_name = BuildTriggersString();
- INFO("%s", trigger_name.c_str());
- INFO("\n");
+ LOG(INFO) << "on " << trigger_name;
for (const auto& c : commands_) {
- std::string cmd_str = c->BuildCommandString();
- INFO(" %s", cmd_str.c_str());
+ std::string cmd_str = c.BuildCommandString();
+ LOG(INFO) << " %s" << cmd_str;
}
- INFO("\n");
}
-ActionManager::ActionManager() : cur_command_(0)
-{
+class EventTrigger : public Trigger {
+public:
+ explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
+ }
+ bool CheckTriggers(const Action& action) const override {
+ return action.CheckEventTrigger(trigger_);
+ }
+private:
+ const std::string trigger_;
+};
+
+class PropertyTrigger : public Trigger {
+public:
+ PropertyTrigger(const std::string& name, const std::string& value)
+ : name_(name), value_(value) {
+ }
+ bool CheckTriggers(const Action& action) const override {
+ return action.CheckPropertyTrigger(name_, value_);
+ }
+private:
+ const std::string name_;
+ const std::string value_;
+};
+
+class BuiltinTrigger : public Trigger {
+public:
+ explicit BuiltinTrigger(Action* action) : action_(action) {
+ }
+ bool CheckTriggers(const Action& action) const override {
+ return action_ == &action;
+ }
+private:
+ const Action* action_;
+};
+
+ActionManager::ActionManager() : current_command_(0) {
}
ActionManager& ActionManager::GetInstance() {
@@ -276,103 +318,123 @@
return instance;
}
-void ActionManager::QueueEventTrigger(const std::string& trigger)
-{
- for (const auto& a : action_list_) {
- if (a->CheckEventTrigger(trigger)) {
- action_queue_.push(a);
- }
+void ActionManager::AddAction(std::unique_ptr<Action> action) {
+ auto old_action_it =
+ std::find_if(actions_.begin(), actions_.end(),
+ [&action] (std::unique_ptr<Action>& a) {
+ return action->TriggersEqual(*a);
+ });
+
+ if (old_action_it != actions_.end()) {
+ (*old_action_it)->CombineAction(*action);
+ } else {
+ actions_.emplace_back(std::move(action));
}
}
+void ActionManager::QueueEventTrigger(const std::string& trigger) {
+ trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
+}
+
void ActionManager::QueuePropertyTrigger(const std::string& name,
- const std::string& value)
-{
- for (const auto& a : action_list_) {
- if (a->CheckPropertyTrigger(name, value)) {
- action_queue_.push(a);
+ const std::string& value) {
+ trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
+}
+
+void ActionManager::QueueAllPropertyTriggers() {
+ QueuePropertyTrigger("", "");
+}
+
+void ActionManager::QueueBuiltinAction(BuiltinFunction func,
+ const std::string& name) {
+ auto action = std::make_unique<Action>(true);
+ std::vector<std::string> name_vector{name};
+
+ if (!action->InitSingleTrigger(name)) {
+ return;
+ }
+
+ action->AddCommand(func, name_vector);
+
+ trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+ actions_.emplace_back(std::move(action));
+}
+
+void ActionManager::ExecuteOneCommand() {
+ // Loop through the trigger queue until we have an action to execute
+ while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
+ for (const auto& action : actions_) {
+ if (trigger_queue_.front()->CheckTriggers(*action)) {
+ current_executing_actions_.emplace(action.get());
+ }
+ }
+ trigger_queue_.pop();
+ }
+
+ if (current_executing_actions_.empty()) {
+ return;
+ }
+
+ auto action = current_executing_actions_.front();
+
+ if (current_command_ == 0) {
+ std::string trigger_name = action->BuildTriggersString();
+ LOG(INFO) << "processing action (" << trigger_name << ")";
+ }
+
+ action->ExecuteOneCommand(current_command_);
+
+ // If this was the last command in the current action, then remove
+ // the action from the executing list.
+ // If this action was oneshot, then also remove it from actions_.
+ ++current_command_;
+ if (current_command_ == action->NumCommands()) {
+ current_executing_actions_.pop();
+ current_command_ = 0;
+ if (action->oneshot()) {
+ auto eraser = [&action] (std::unique_ptr<Action>& a) {
+ return a.get() == action;
+ };
+ actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
}
}
}
-void ActionManager::QueueAllPropertyTriggers()
-{
- QueuePropertyTrigger("", "");
+bool ActionManager::HasMoreCommands() const {
+ return !current_executing_actions_.empty() || !trigger_queue_.empty();
}
-void ActionManager::QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
- const std::string& name)
-{
- Action* act = new Action();
- std::vector<std::string> name_vector{name};
-
- if (!act->InitSingleTrigger(name)) {
- return;
- }
-
- act->AddCommand(func, name_vector);
-
- action_queue_.push(act);
-}
-
-void ActionManager::ExecuteOneCommand() {
- if (action_queue_.empty()) {
- return;
- }
-
- Action* action = action_queue_.front();
- if (!action->NumCommands()) {
- action_queue_.pop();
- return;
- }
-
- if (cur_command_ == 0) {
- std::string trigger_name = action->BuildTriggersString();
- INFO("processing action %p (%s)\n", action, trigger_name.c_str());
- }
-
- action->ExecuteOneCommand(cur_command_++);
- if (cur_command_ == action->NumCommands()) {
- cur_command_ = 0;
- action_queue_.pop();
- }
-}
-
-bool ActionManager::HasMoreCommands() const
-{
- return !action_queue_.empty();
-}
-
-Action* ActionManager::AddNewAction(const std::vector<std::string>& triggers,
- std::string* err)
-{
- if (triggers.size() < 1) {
- *err = "actions must have a trigger\n";
- return nullptr;
- }
-
- Action* act = new Action();
- if (!act->InitTriggers(triggers, err)) {
- return nullptr;
- }
-
- auto old_act_it =
- std::find_if(action_list_.begin(), action_list_.end(),
- [&act] (Action* a) { return act->TriggersEqual(*a); });
-
- if (old_act_it != action_list_.end()) {
- delete act;
- return *old_act_it;
- }
-
- action_list_.push_back(act);
- return act;
-}
-
-void ActionManager::DumpState() const
-{
- for (const auto& a : action_list_) {
+void ActionManager::DumpState() const {
+ for (const auto& a : actions_) {
a->DumpState();
}
- INFO("\n");
+}
+
+bool ActionParser::ParseSection(const std::vector<std::string>& args,
+ std::string* err) {
+ std::vector<std::string> triggers(args.begin() + 1, args.end());
+ if (triggers.size() < 1) {
+ *err = "actions must have a trigger";
+ return false;
+ }
+
+ auto action = std::make_unique<Action>(false);
+ if (!action->InitTriggers(triggers, err)) {
+ return false;
+ }
+
+ action_ = std::move(action);
+ return true;
+}
+
+bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
+ const std::string& filename, int line,
+ std::string* err) const {
+ return action_ ? action_->AddCommand(args, filename, line, err) : false;
+}
+
+void ActionParser::EndSection() {
+ if (action_ && action_->NumCommands() > 0) {
+ ActionManager::GetInstance().AddAction(std::move(action_));
+ }
}
diff --git a/init/action.h b/init/action.h
index ae28fe1..0bae9f0 100644
--- a/init/action.h
+++ b/init/action.h
@@ -22,13 +22,36 @@
#include <string>
#include <vector>
+#include "builtins.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+
+class Command {
+public:
+ Command(BuiltinFunction f, const std::vector<std::string>& args,
+ const std::string& filename, int line);
+
+ int InvokeFunc() const;
+ std::string BuildCommandString() const;
+ std::string BuildSourceString() const;
+
+private:
+ BuiltinFunction func_;
+ std::vector<std::string> args_;
+ std::string filename_;
+ int line_;
+};
+
class Action {
public:
- Action();
+ explicit Action(bool oneshot = false);
- void AddCommand(int (*f)(const std::vector<std::string>& args),
+ bool AddCommand(const std::vector<std::string>& args,
+ const std::string& filename, int line, std::string* err);
+ void AddCommand(BuiltinFunction f,
const std::vector<std::string>& args,
const std::string& filename = "", int line = 0);
+ void CombineAction(const Action& action);
bool InitTriggers(const std::vector<std::string>& args, std::string* err);
bool InitSingleTrigger(const std::string& trigger);
std::size_t NumCommands() const;
@@ -37,13 +60,17 @@
bool CheckEventTrigger(const std::string& trigger) const;
bool CheckPropertyTrigger(const std::string& name,
const std::string& value) const;
- bool TriggersEqual(const class Action& other) const;
+ bool TriggersEqual(const Action& other) const;
std::string BuildTriggersString() const;
void DumpState() const;
-private:
- class Command;
+ bool oneshot() const { return oneshot_; }
+ static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
+ function_map_ = function_map;
+ }
+
+private:
void ExecuteCommand(const Command& command) const;
bool CheckPropertyTriggers(const std::string& name = "",
const std::string& value = "") const;
@@ -51,21 +78,28 @@
std::map<std::string, std::string> property_triggers_;
std::string event_trigger_;
- std::vector<Command*> commands_;
+ std::vector<Command> commands_;
+ bool oneshot_;
+ static const KeywordMap<BuiltinFunction>* function_map_;
+};
+
+class Trigger {
+public:
+ virtual ~Trigger() { }
+ virtual bool CheckTriggers(const Action& action) const = 0;
};
class ActionManager {
public:
static ActionManager& GetInstance();
+
+ void AddAction(std::unique_ptr<Action> action);
void QueueEventTrigger(const std::string& trigger);
void QueuePropertyTrigger(const std::string& name, const std::string& value);
void QueueAllPropertyTriggers();
- void QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
- const std::string& name);
+ void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
void ExecuteOneCommand();
bool HasMoreCommands() const;
- Action* AddNewAction(const std::vector<std::string>& triggers,
- std::string* err);
void DumpState() const;
private:
@@ -74,9 +108,26 @@
ActionManager(ActionManager const&) = delete;
void operator=(ActionManager const&) = delete;
- std::vector<Action*> action_list_;
- std::queue<Action*> action_queue_;
- std::size_t cur_command_;
+ std::vector<std::unique_ptr<Action>> actions_;
+ std::queue<std::unique_ptr<Trigger>> trigger_queue_;
+ std::queue<const Action*> current_executing_actions_;
+ std::size_t current_command_;
+};
+
+class ActionParser : public SectionParser {
+public:
+ ActionParser() : action_(nullptr) {
+ }
+ bool ParseSection(const std::vector<std::string>& args,
+ std::string* err) override;
+ bool ParseLineSection(const std::vector<std::string>& args,
+ const std::string& filename, int line,
+ std::string* err) const override;
+ void EndSection() override;
+ void EndFile(const std::string&) override {
+ }
+private:
+ std::unique_ptr<Action> action_;
};
#endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index efaee1c..4a9c32e 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -15,8 +15,7 @@
*/
#include "bootchart.h"
-#include "keywords.h"
-#include "log.h"
+
#include "property_service.h"
#include <dirent.h>
@@ -30,245 +29,170 @@
#include <time.h>
#include <unistd.h>
+#include <chrono>
+#include <condition_variable>
#include <memory>
+#include <mutex>
#include <string>
+#include <thread>
+#include <vector>
-#include <base/file.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
-#define LOG_ROOT "/data/bootchart"
-#define LOG_STAT LOG_ROOT"/proc_stat.log"
-#define LOG_PROCS LOG_ROOT"/proc_ps.log"
-#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
-#define LOG_HEADER LOG_ROOT"/header"
-#define LOG_ACCT LOG_ROOT"/kernel_pacct"
+using android::base::StringPrintf;
+using namespace std::chrono_literals;
-#define LOG_STARTFILE LOG_ROOT"/start"
-#define LOG_STOPFILE LOG_ROOT"/stop"
+static std::thread* g_bootcharting_thread;
-// Polling period in ms.
-static const int BOOTCHART_POLLING_MS = 200;
-
-// Max polling time in seconds.
-static const int BOOTCHART_MAX_TIME_SEC = 10*60;
-
-static long long g_last_bootchart_time;
-static int g_remaining_samples;
-
-static FILE* log_stat;
-static FILE* log_procs;
-static FILE* log_disks;
+static std::mutex g_bootcharting_finished_mutex;
+static std::condition_variable g_bootcharting_finished_cv;
+static bool g_bootcharting_finished;
static long long get_uptime_jiffies() {
- std::string uptime;
- if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
- return 0;
- }
- return 100LL * strtod(uptime.c_str(), NULL);
+ std::string uptime;
+ if (!android::base::ReadFileToString("/proc/uptime", &uptime)) return 0;
+ return 100LL * strtod(uptime.c_str(), NULL);
+}
+
+static std::unique_ptr<FILE, decltype(&fclose)> fopen_unique(const char* filename,
+ const char* mode) {
+ std::unique_ptr<FILE, decltype(&fclose)> result(fopen(filename, mode), fclose);
+ if (!result) PLOG(ERROR) << "bootchart: failed to open " << filename;
+ return result;
}
static void log_header() {
- char date[32];
- time_t now_t = time(NULL);
- struct tm now = *localtime(&now_t);
- strftime(date, sizeof(date), "%F %T", &now);
+ char date[32];
+ time_t now_t = time(NULL);
+ struct tm now = *localtime(&now_t);
+ strftime(date, sizeof(date), "%F %T", &now);
- utsname uts;
- if (uname(&uts) == -1) {
- return;
- }
+ utsname uts;
+ if (uname(&uts) == -1) return;
- std::string fingerprint = property_get("ro.build.fingerprint");
- if (fingerprint.empty()) {
- return;
- }
+ std::string fingerprint = property_get("ro.build.fingerprint");
+ if (fingerprint.empty()) return;
- std::string kernel_cmdline;
- android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline);
+ std::string kernel_cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline);
- FILE* out = fopen(LOG_HEADER, "we");
- if (out == NULL) {
- return;
- }
- fprintf(out, "version = Android init 0.8 " __TIME__ "\n");
- fprintf(out, "title = Boot chart for Android (%s)\n", date);
- fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
- fprintf(out, "system.release = %s\n", fingerprint.c_str());
- // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
- fprintf(out, "system.cpu = %s\n", uts.machine);
- fprintf(out, "system.kernel.options = %s\n", kernel_cmdline.c_str());
- fclose(out);
+ auto fp = fopen_unique("/data/bootchart/header", "we");
+ if (!fp) return;
+ fprintf(&*fp, "version = Android init 0.8\n");
+ fprintf(&*fp, "title = Boot chart for Android (%s)\n", date);
+ fprintf(&*fp, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
+ fprintf(&*fp, "system.release = %s\n", fingerprint.c_str());
+ // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
+ fprintf(&*fp, "system.cpu = %s\n", uts.machine);
+ fprintf(&*fp, "system.kernel.options = %s\n", kernel_cmdline.c_str());
}
-static void do_log_uptime(FILE* log) {
- fprintf(log, "%lld\n", get_uptime_jiffies());
+static void log_uptime(FILE* log) {
+ fprintf(log, "%lld\n", get_uptime_jiffies());
}
-static void do_log_file(FILE* log, const char* procfile) {
- do_log_uptime(log);
+static void log_file(FILE* log, const char* procfile) {
+ log_uptime(log);
- std::string content;
- if (android::base::ReadFileToString(procfile, &content)) {
- fprintf(log, "%s\n", content.c_str());
- }
+ std::string content;
+ if (android::base::ReadFileToString(procfile, &content)) {
+ fprintf(log, "%s\n", content.c_str());
+ }
}
-static void do_log_procs(FILE* log) {
- do_log_uptime(log);
+static void log_processes(FILE* log) {
+ log_uptime(log);
- std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
- struct dirent* entry;
- while ((entry = readdir(dir.get())) != NULL) {
- // Only match numeric values.
- char* end;
- int pid = strtol(entry->d_name, &end, 10);
- if (end != NULL && end > entry->d_name && *end == 0) {
- char filename[32];
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ // Only match numeric values.
+ int pid = atoi(entry->d_name);
+ if (pid == 0) continue;
- // /proc/<pid>/stat only has truncated task names, so get the full
- // name from /proc/<pid>/cmdline.
- snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
- std::string cmdline;
- android::base::ReadFileToString(filename, &cmdline);
- const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
+ // /proc/<pid>/stat only has truncated task names, so get the full
+ // name from /proc/<pid>/cmdline.
+ std::string cmdline;
+ android::base::ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline);
+ const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
- // Read process stat line.
- snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
- std::string stat;
- if (android::base::ReadFileToString(filename, &stat)) {
- if (!cmdline.empty()) {
- // Substitute the process name with its real name.
- size_t open = stat.find('(');
- size_t close = stat.find_last_of(')');
- if (open != std::string::npos && close != std::string::npos) {
- stat.replace(open + 1, close - open - 1, full_name);
- }
- }
- fputs(stat.c_str(), log);
- }
+ // Read process stat line.
+ std::string stat;
+ if (android::base::ReadFileToString(StringPrintf("/proc/%d/stat", pid), &stat)) {
+ if (!cmdline.empty()) {
+ // Substitute the process name with its real name.
+ size_t open = stat.find('(');
+ size_t close = stat.find_last_of(')');
+ if (open != std::string::npos && close != std::string::npos) {
+ stat.replace(open + 1, close - open - 1, full_name);
}
+ }
+ fputs(stat.c_str(), log);
}
+ }
- fputc('\n', log);
+ fputc('\n', log);
}
-static int bootchart_init() {
- int timeout = 0;
+static void bootchart_thread_main() {
+ LOG(INFO) << "Bootcharting started";
- std::string start;
- android::base::ReadFileToString(LOG_STARTFILE, &start);
- if (!start.empty()) {
- timeout = atoi(start.c_str());
- } else {
- // When running with emulator, androidboot.bootchart=<timeout>
- // might be passed by as kernel parameters to specify the bootchart
- // timeout. this is useful when using -wipe-data since the /data
- // partition is fresh.
- std::string cmdline;
- const char* s;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
-#define KERNEL_OPTION "androidboot.bootchart="
- if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
- timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
- }
- }
- if (timeout == 0)
- return 0;
+ // Open log files.
+ auto stat_log = fopen_unique("/data/bootchart/proc_stat.log", "we");
+ if (!stat_log) return;
+ auto proc_log = fopen_unique("/data/bootchart/proc_ps.log", "we");
+ if (!proc_log) return;
+ auto disk_log = fopen_unique("/data/bootchart/proc_diskstats.log", "we");
+ if (!disk_log) return;
- if (timeout > BOOTCHART_MAX_TIME_SEC)
- timeout = BOOTCHART_MAX_TIME_SEC;
+ log_header();
- int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
-
- log_stat = fopen(LOG_STAT, "we");
- if (log_stat == NULL) {
- return -1;
- }
- log_procs = fopen(LOG_PROCS, "we");
- if (log_procs == NULL) {
- fclose(log_stat);
- return -1;
- }
- log_disks = fopen(LOG_DISK, "we");
- if (log_disks == NULL) {
- fclose(log_stat);
- fclose(log_procs);
- return -1;
+ while (true) {
+ {
+ std::unique_lock<std::mutex> lock(g_bootcharting_finished_mutex);
+ g_bootcharting_finished_cv.wait_for(lock, 200ms);
+ if (g_bootcharting_finished) break;
}
- // Create kernel process accounting file.
- close(open(LOG_ACCT, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
- acct(LOG_ACCT);
+ log_file(&*stat_log, "/proc/stat");
+ log_file(&*disk_log, "/proc/diskstats");
+ log_processes(&*proc_log);
+ }
- log_header();
- return count;
+ LOG(INFO) << "Bootcharting finished";
}
-int do_bootchart_init(const std::vector<std::string>& args) {
- g_remaining_samples = bootchart_init();
- if (g_remaining_samples < 0) {
- ERROR("Bootcharting init failure: %s\n", strerror(errno));
- } else if (g_remaining_samples > 0) {
- NOTICE("Bootcharting started (will run for %d s).\n",
- (g_remaining_samples * BOOTCHART_POLLING_MS) / 1000);
- } else {
- NOTICE("Not bootcharting.\n");
- }
+static int do_bootchart_start() {
+ // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+ std::string start;
+ if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+ LOG(VERBOSE) << "Not bootcharting";
return 0;
+ }
+
+ g_bootcharting_thread = new std::thread(bootchart_thread_main);
+ return 0;
}
-static int bootchart_step() {
- do_log_file(log_stat, "/proc/stat");
- do_log_file(log_disks, "/proc/diskstats");
- do_log_procs(log_procs);
+static int do_bootchart_stop() {
+ if (!g_bootcharting_thread) return 0;
- // Stop if /data/bootchart/stop contains 1.
- std::string stop;
- if (android::base::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
- return -1;
- }
+ // Tell the worker thread it's time to quit.
+ {
+ std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
+ g_bootcharting_finished = true;
+ g_bootcharting_finished_cv.notify_one();
+ }
- return 0;
+ g_bootcharting_thread->join();
+ delete g_bootcharting_thread;
+ g_bootcharting_thread = nullptr;
+ return 0;
}
-/* called to get time (in ms) used by bootchart */
-static long long bootchart_gettime() {
- return 10LL*get_uptime_jiffies();
-}
-
-static void bootchart_finish() {
- unlink(LOG_STOPFILE);
- fclose(log_stat);
- fclose(log_disks);
- fclose(log_procs);
- acct(NULL);
-}
-
-void bootchart_sample(int* timeout) {
- // Do we have any more bootcharting to do?
- if (g_remaining_samples <= 0) {
- return;
- }
-
- long long current_time = bootchart_gettime();
- int elapsed_time = current_time - g_last_bootchart_time;
-
- if (elapsed_time >= BOOTCHART_POLLING_MS) {
- /* count missed samples */
- while (elapsed_time >= BOOTCHART_POLLING_MS) {
- elapsed_time -= BOOTCHART_POLLING_MS;
- g_remaining_samples--;
- }
- /* count may be negative, take a sample anyway */
- g_last_bootchart_time = current_time;
- if (bootchart_step() < 0 || g_remaining_samples <= 0) {
- bootchart_finish();
- g_remaining_samples = 0;
- }
- }
- if (g_remaining_samples > 0) {
- int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
- if (*timeout < 0 || *timeout > remaining_time) {
- *timeout = remaining_time;
- }
- }
+int do_bootchart(const std::vector<std::string>& args) {
+ if (args[1] == "start") return do_bootchart_start();
+ return do_bootchart_stop();
}
diff --git a/init/bootchart.h b/init/bootchart.h
index cf61d83..0e3593d 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -17,6 +17,9 @@
#ifndef _BOOTCHART_H
#define _BOOTCHART_H
-void bootchart_sample(int* timeout);
+#include <string>
+#include <vector>
+
+int do_bootchart(const std::vector<std::string>& args);
#endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index e663501..965a81f 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -14,65 +14,82 @@
* limitations under the License.
*/
+#include "builtins.h"
+
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <mntent.h>
#include <net/if.h>
#include <signal.h>
+#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/resource.h>
+#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/loop.h>
-#include <ext4_crypt_init_extensions.h>
+#include <linux/module.h>
+#include <string>
+#include <thread>
+
+#include <selinux/android.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
#include <fs_mgr.h>
-#include <base/stringprintf.h>
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+#include <bootloader_message/bootloader_message.h>
#include <cutils/partition_utils.h>
#include <cutils/android_reboot.h>
+#include <ext4_utils/ext4_crypt.h>
+#include <ext4_utils/ext4_crypt_init_extensions.h>
#include <logwrap/logwrap.h>
-#include <private/android_filesystem_config.h>
#include "action.h"
+#include "bootchart.h"
#include "devices.h"
#include "init.h"
#include "init_parser.h"
-#include "keywords.h"
#include "log.h"
#include "property_service.h"
#include "service.h"
+#include "signal_handler.h"
#include "util.h"
+using namespace std::literals::string_literals;
+
#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
-#define UNMOUNT_CHECK_MS 5000
#define UNMOUNT_CHECK_TIMES 10
-// System call provided by bionic but not in any header file.
-extern "C" int init_module(void *, unsigned long, const char *);
+static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
-static int insmod(const char *filename, const char *options)
-{
- std::string module;
- if (!read_file(filename, &module)) {
+static int insmod(const char *filename, const char *options, int flags) {
+ int fd = open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ PLOG(ERROR) << "insmod: open(\"" << filename << "\") failed";
return -1;
}
-
- // TODO: use finit_module for >= 3.8 kernels.
- return init_module(&module[0], module.size(), options);
+ int rc = syscall(__NR_finit_module, fd, options, flags);
+ if (rc == -1) {
+ PLOG(ERROR) << "finit_module for \"" << filename << "\" failed";
+ }
+ close(fd);
+ return rc;
}
-static int __ifupdown(const char *interface, int up)
-{
+static int __ifupdown(const char *interface, int up) {
struct ifreq ifr;
int s, ret;
@@ -99,8 +116,42 @@
return ret;
}
-static void unmount_and_fsck(const struct mntent *entry)
-{
+// Turn off backlight while we are performing power down cleanup activities.
+static void turnOffBacklight() {
+ static const char off[] = "0";
+
+ android::base::WriteStringToFile(off, "/sys/class/leds/lcd-backlight/brightness");
+
+ static const char backlightDir[] = "/sys/class/backlight";
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(backlightDir), closedir);
+ if (!dir) {
+ return;
+ }
+
+ struct dirent *dp;
+ while ((dp = readdir(dir.get())) != NULL) {
+ if (((dp->d_type != DT_DIR) && (dp->d_type != DT_LNK)) ||
+ (dp->d_name[0] == '.')) {
+ continue;
+ }
+
+ std::string fileName = android::base::StringPrintf("%s/%s/brightness",
+ backlightDir,
+ dp->d_name);
+ android::base::WriteStringToFile(off, fileName);
+ }
+}
+
+static int reboot_into_recovery(const std::vector<std::string>& options) {
+ std::string err;
+ if (!write_bootloader_message(options, &err)) {
+ LOG(ERROR) << "failed to set bootloader message: " << err;
+ return -1;
+ }
+ reboot("recovery");
+}
+
+static void unmount_and_fsck(const struct mntent *entry) {
if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
return;
@@ -124,6 +175,18 @@
ServiceManager::GetInstance().ForEachService([] (Service* s) { s->Stop(); });
TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
+ // Restart Watchdogd to allow us to complete umounting and fsck
+ Service *svc = ServiceManager::GetInstance().FindServiceByName("watchdogd");
+ if (svc) {
+ do {
+ sched_yield(); // do not be so eager, let cleanup have priority
+ ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+ } while (svc->flags() & SVC_RUNNING); // Paranoid Cargo
+ svc->Start();
+ }
+
+ turnOffBacklight();
+
int count = 0;
while (count++ < UNMOUNT_CHECK_TIMES) {
int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL));
@@ -132,11 +195,9 @@
close(fd);
break;
} else if (errno == EBUSY) {
- /* Some processes using |entry->mnt_dir| are still alive. Wait for a
- * while then retry.
- */
- TEMP_FAILURE_RETRY(
- usleep(UNMOUNT_CHECK_MS * 1000 / UNMOUNT_CHECK_TIMES));
+ // Some processes using |entry->mnt_dir| are still alive. Wait for a
+ // while then retry.
+ std::this_thread::sleep_for(5000ms / UNMOUNT_CHECK_TIMES);
continue;
} else {
/* Cannot open the device. Give up. */
@@ -144,24 +205,28 @@
}
}
+ // NB: With watchdog still running, there is no cap on the time it takes
+ // to complete the fsck, from the users perspective the device graphics
+ // and responses are locked-up and they may choose to hold the power
+ // button in frustration if it drags out.
+
int st;
if (!strcmp(entry->mnt_type, "f2fs")) {
const char *f2fs_argv[] = {
"/system/bin/fsck.f2fs", "-f", entry->mnt_fsname,
};
- android_fork_execvp_ext(ARRAY_SIZE(f2fs_argv), (char **)f2fs_argv,
+ android_fork_execvp_ext(arraysize(f2fs_argv), (char **)f2fs_argv,
&st, true, LOG_KLOG, true, NULL, NULL, 0);
} else if (!strcmp(entry->mnt_type, "ext4")) {
const char *ext4_argv[] = {
"/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname,
};
- android_fork_execvp_ext(ARRAY_SIZE(ext4_argv), (char **)ext4_argv,
+ android_fork_execvp_ext(arraysize(ext4_argv), (char **)ext4_argv,
&st, true, LOG_KLOG, true, NULL, NULL, 0);
}
}
-int do_class_start(const std::vector<std::string>& args)
-{
+static int do_class_start(const std::vector<std::string>& args) {
/* Starting a class does not start services
* which are explicitly disabled. They must
* be started individually.
@@ -171,27 +236,23 @@
return 0;
}
-int do_class_stop(const std::vector<std::string>& args)
-{
+static int do_class_stop(const std::vector<std::string>& args) {
ServiceManager::GetInstance().
ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
return 0;
}
-int do_class_reset(const std::vector<std::string>& args)
-{
+static int do_class_reset(const std::vector<std::string>& args) {
ServiceManager::GetInstance().
ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
return 0;
}
-int do_domainname(const std::vector<std::string>& args)
-{
- return write_file("/proc/sys/kernel/domainname", args[1].c_str());
+static int do_domainname(const std::vector<std::string>& args) {
+ return write_file("/proc/sys/kernel/domainname", args[1].c_str()) ? 0 : 1;
}
-int do_enable(const std::vector<std::string>& args)
-{
+static int do_enable(const std::vector<std::string>& args) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
if (!svc) {
return -1;
@@ -199,7 +260,7 @@
return svc->Enable();
}
-int do_exec(const std::vector<std::string>& args) {
+static int do_exec(const std::vector<std::string>& args) {
Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
if (!svc) {
return -1;
@@ -211,45 +272,40 @@
return 0;
}
-int do_export(const std::vector<std::string>& args)
-{
+static int do_export(const std::vector<std::string>& args) {
return add_environment(args[1].c_str(), args[2].c_str());
}
-int do_hostname(const std::vector<std::string>& args)
-{
- return write_file("/proc/sys/kernel/hostname", args[1].c_str());
+static int do_hostname(const std::vector<std::string>& args) {
+ return write_file("/proc/sys/kernel/hostname", args[1].c_str()) ? 0 : 1;
}
-int do_ifup(const std::vector<std::string>& args)
-{
+static int do_ifup(const std::vector<std::string>& args) {
return __ifupdown(args[1].c_str(), 1);
}
-int do_insmod(const std::vector<std::string>& args)
-{
- std::string options;
+static int do_insmod(const std::vector<std::string>& args) {
+ int flags = 0;
+ auto it = args.begin() + 1;
- if (args.size() > 2) {
- options += args[2];
- for (std::size_t i = 3; i < args.size(); ++i) {
- options += ' ';
- options += args[i];
- }
+ if (!(*it).compare("-f")) {
+ flags = MODULE_INIT_IGNORE_VERMAGIC | MODULE_INIT_IGNORE_MODVERSIONS;
+ it++;
}
- return insmod(args[1].c_str(), options.c_str());
+ std::string filename = *it++;
+ std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');
+ return insmod(filename.c_str(), options.c_str(), flags);
}
-int do_mkdir(const std::vector<std::string>& args)
-{
+static int do_mkdir(const std::vector<std::string>& args) {
mode_t mode = 0755;
int ret;
/* mkdir <path> [mode] [owner] [group] */
if (args.size() >= 3) {
- mode = std::stoul(args[2], 0, 8);
+ mode = std::strtoul(args[2].c_str(), 0, 8);
}
ret = make_dir(args[1].c_str(), mode);
@@ -282,7 +338,21 @@
}
}
- return e4crypt_set_directory_policy(args[1].c_str());
+ if (e4crypt_is_native()) {
+ if (e4crypt_set_directory_policy(args[1].c_str())) {
+ const std::vector<std::string> options = {
+ "--prompt_and_wipe_data",
+ "--reason=set_policy_failed:"s + args[1]};
+ reboot_into_recovery(options);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* umount <path> */
+static int do_umount(const std::vector<std::string>& args) {
+ return umount(args[1].c_str());
}
static struct {
@@ -310,8 +380,7 @@
#define DATA_MNT_POINT "/data"
/* mount <type> <device> <path> <flags ...> <options> */
-int do_mount(const std::vector<std::string>& args)
-{
+static int do_mount(const std::vector<std::string>& args) {
char tmp[64];
const char *source, *target, *system;
const char *options = NULL;
@@ -341,22 +410,7 @@
source = args[2].c_str();
target = args[3].c_str();
- if (!strncmp(source, "mtd@", 4)) {
- n = mtd_name_to_number(source + 4);
- if (n < 0) {
- return -1;
- }
-
- snprintf(tmp, sizeof(tmp), "/dev/block/mtdblock%d", n);
-
- if (wait)
- wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
- if (mount(tmp, target, system, flags, options) < 0) {
- return -1;
- }
-
- goto exit_success;
- } else if (!strncmp(source, "loop@", 5)) {
+ if (!strncmp(source, "loop@", 5)) {
int mode, loop, fd;
struct loop_info info;
@@ -395,11 +449,11 @@
}
close(fd);
- ERROR("out of loopback devices");
+ LOG(ERROR) << "out of loopback devices";
return -1;
} else {
if (wait)
- wait_for_file(source, COMMAND_RETRY_TIMEOUT);
+ wait_for_file(source, kCommandRetryTimeout);
if (mount(source, target, system, flags, options) < 0) {
return -1;
}
@@ -411,51 +465,51 @@
}
-static int wipe_data_via_recovery()
-{
- mkdir("/cache/recovery", 0700);
- int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
- if (fd >= 0) {
- write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1);
- write(fd, "--reason=wipe_data_via_recovery\n", strlen("--reason=wipe_data_via_recovery\n") + 1);
- close(fd);
+/* Imports .rc files from the specified paths. Default ones are applied if none is given.
+ *
+ * start_index: index of the first path in the args list
+ */
+static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
+ Parser& parser = Parser::GetInstance();
+ if (end_index <= start_index) {
+ // Use the default set if no path is given
+ static const std::vector<std::string> init_directories = {
+ "/system/etc/init",
+ "/vendor/etc/init",
+ "/odm/etc/init"
+ };
+
+ for (const auto& dir : init_directories) {
+ parser.ParseConfig(dir);
+ }
} else {
- ERROR("could not open /cache/recovery/command\n");
- return -1;
+ for (size_t i = start_index; i < end_index; ++i) {
+ parser.ParseConfig(args[i]);
+ }
}
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- while (1) { pause(); } // never reached
}
-/*
- * This function might request a reboot, in which case it will
- * not return.
+/* mount_fstab
+ *
+ * Call fs_mgr_mount_all() to mount the given fstab
*/
-int do_mount_all(const std::vector<std::string>& args)
-{
- pid_t pid;
+static int mount_fstab(const char* fstabfile, int mount_mode) {
int ret = -1;
- int child_ret = -1;
- int status;
- struct fstab *fstab;
- if (args.size() != 2) {
- return -1;
- }
- const char* fstabfile = args[1].c_str();
/*
* Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
* do the call in the child to provide protection to the main init
* process if anything goes wrong (crash or memory leak), and wait for
* the child to finish in the parent.
*/
- pid = fork();
+ pid_t pid = fork();
if (pid > 0) {
/* Parent. Wait for the child to return */
+ int status;
int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
- if (wp_ret < 0) {
- /* Unexpected error code. We will continue anyway. */
- NOTICE("waitpid failed rc=%d: %s\n", wp_ret, strerror(errno));
+ if (wp_ret == -1) {
+ // Unexpected error code. We will continue anyway.
+ PLOG(WARNING) << "waitpid failed";
}
if (WIFEXITED(status)) {
@@ -465,37 +519,55 @@
}
} else if (pid == 0) {
/* child, call fs_mgr_mount_all() */
- klog_set_level(6); /* So we can see what fs_mgr_mount_all() does */
- fstab = fs_mgr_read_fstab(fstabfile);
- child_ret = fs_mgr_mount_all(fstab);
+
+ // So we can always see what fs_mgr_mount_all() does.
+ // Only needed if someone explicitly changes the default log level in their init.rc.
+ android::base::ScopedLogSeverity info(android::base::INFO);
+
+ struct fstab* fstab = fs_mgr_read_fstab(fstabfile);
+ int child_ret = fs_mgr_mount_all(fstab, mount_mode);
fs_mgr_free_fstab(fstab);
if (child_ret == -1) {
- ERROR("fs_mgr_mount_all returned an error\n");
+ PLOG(ERROR) << "fs_mgr_mount_all returned an error";
}
_exit(child_ret);
} else {
/* fork failed, return an error */
return -1;
}
+ return ret;
+}
- if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
- property_set("vold.decrypt", "trigger_encryption");
- } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
+/* Queue event based on fs_mgr return code.
+ *
+ * code: return code of fs_mgr_mount_all
+ *
+ * This function might request a reboot, in which case it will
+ * not return.
+ *
+ * return code is processed based on input code
+ */
+static int queue_fs_event(int code) {
+ int ret = code;
+ if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
+ ActionManager::GetInstance().QueueEventTrigger("encrypt");
+ } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
- property_set("vold.decrypt", "trigger_default_encryption");
- } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+ ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+ } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
property_set("ro.crypto.state", "unencrypted");
- /* If fs_mgr determined this is an unencrypted device, then trigger
- * that action.
- */
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
+ } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
+ property_set("ro.crypto.state", "unsupported");
+ ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
/* Setup a wipe via recovery, and reboot into recovery */
- ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
- ret = wipe_data_via_recovery();
+ PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
+ const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
+ ret = reboot_into_recovery(options);
/* If reboot worked, there is no return. */
- } else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) {
+ } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
return -1;
}
@@ -505,23 +577,56 @@
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {
- if (e4crypt_install_keyring()) {
- return -1;
- }
- property_set("ro.crypto.state", "encrypted");
- property_set("ro.crypto.type", "file");
- property_set("vold.decrypt", "trigger_restart_min_framework");
- } else if (ret > 0) {
- ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
+ } else if (code > 0) {
+ PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << code;
}
/* else ... < 0: error */
return ret;
}
-int do_swapon_all(const std::vector<std::string>& args)
-{
+/* mount_all <fstab> [ <path> ]* [--<options>]*
+ *
+ * This function might request a reboot, in which case it will
+ * not return.
+ */
+static int do_mount_all(const std::vector<std::string>& args) {
+ std::size_t na = 0;
+ bool import_rc = true;
+ bool queue_event = true;
+ int mount_mode = MOUNT_MODE_DEFAULT;
+ const char* fstabfile = args[1].c_str();
+ std::size_t path_arg_end = args.size();
+
+ for (na = args.size() - 1; na > 1; --na) {
+ if (args[na] == "--early") {
+ path_arg_end = na;
+ queue_event = false;
+ mount_mode = MOUNT_MODE_EARLY;
+ } else if (args[na] == "--late") {
+ path_arg_end = na;
+ import_rc = false;
+ mount_mode = MOUNT_MODE_LATE;
+ }
+ }
+
+ int ret = mount_fstab(fstabfile, mount_mode);
+
+ if (import_rc) {
+ /* Paths of .rc files are specified at the 2nd argument and beyond */
+ import_late(args, 2, path_arg_end);
+ }
+
+ if (queue_event) {
+ /* queue_fs_event will queue event based on mount_fstab return code
+ * and return processed return code*/
+ ret = queue_fs_event(ret);
+ }
+
+ return ret;
+}
+
+static int do_swapon_all(const std::vector<std::string>& args) {
struct fstab *fstab;
int ret;
@@ -532,29 +637,29 @@
return ret;
}
-int do_setprop(const std::vector<std::string>& args)
-{
+static int do_setprop(const std::vector<std::string>& args) {
const char* name = args[1].c_str();
const char* value = args[2].c_str();
property_set(name, value);
return 0;
}
-int do_setrlimit(const std::vector<std::string>& args)
-{
+static int do_setrlimit(const std::vector<std::string>& args) {
struct rlimit limit;
int resource;
- resource = std::stoi(args[1]);
- limit.rlim_cur = std::stoi(args[2]);
- limit.rlim_max = std::stoi(args[3]);
- return setrlimit(resource, &limit);
+ if (android::base::ParseInt(args[1], &resource) &&
+ android::base::ParseUint(args[2], &limit.rlim_cur) &&
+ android::base::ParseUint(args[3], &limit.rlim_max)) {
+ return setrlimit(resource, &limit);
+ }
+ LOG(WARNING) << "ignoring setrlimit " << args[1] << " " << args[2] << " " << args[3];
+ return -1;
}
-int do_start(const std::vector<std::string>& args)
-{
+static int do_start(const std::vector<std::string>& args) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
if (!svc) {
- ERROR("do_start: Service %s not found\n", args[1].c_str());
+ LOG(ERROR) << "do_start: Service " << args[1] << " not found";
return -1;
}
if (!svc->Start())
@@ -562,30 +667,27 @@
return 0;
}
-int do_stop(const std::vector<std::string>& args)
-{
+static int do_stop(const std::vector<std::string>& args) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
if (!svc) {
- ERROR("do_stop: Service %s not found\n", args[1].c_str());
+ LOG(ERROR) << "do_stop: Service " << args[1] << " not found";
return -1;
}
svc->Stop();
return 0;
}
-int do_restart(const std::vector<std::string>& args)
-{
+static int do_restart(const std::vector<std::string>& args) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
if (!svc) {
- ERROR("do_restart: Service %s not found\n", args[1].c_str());
+ LOG(ERROR) << "do_restart: Service " << args[1] << " not found";
return -1;
}
svc->Restart();
return 0;
}
-int do_powerctl(const std::vector<std::string>& args)
-{
+static int do_powerctl(const std::vector<std::string>& args) {
const char* command = args[1].c_str();
int len = 0;
unsigned int cmd = 0;
@@ -599,7 +701,7 @@
cmd = ANDROID_RB_RESTART2;
len = 6;
} else {
- ERROR("powerctl: unrecognized command '%s'\n", command);
+ LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
return -EINVAL;
}
@@ -611,78 +713,111 @@
callback_on_ro_remount = unmount_and_fsck;
} else if (cmd == ANDROID_RB_RESTART2) {
reboot_target = &command[len + 1];
+ // When rebooting to the bootloader notify the bootloader writing
+ // also the BCB.
+ if (strcmp(reboot_target, "bootloader") == 0) {
+ std::string err;
+ if (!write_reboot_bootloader(&err)) {
+ LOG(ERROR) << "reboot-bootloader: Error writing "
+ "bootloader_message: " << err;
+ }
+ }
}
} else if (command[len] != '\0') {
- ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
+ LOG(ERROR) << "powerctl: unrecognized reboot target '" << &command[len] << "'";
return -EINVAL;
}
- return android_reboot_with_callback(cmd, 0, reboot_target,
- callback_on_ro_remount);
+ std::string timeout = property_get("ro.build.shutdown_timeout");
+ unsigned int delay = 0;
+
+ if (android::base::ParseUint(timeout, &delay) && delay > 0) {
+ Timer t;
+ // Ask all services to terminate.
+ ServiceManager::GetInstance().ForEachService(
+ [] (Service* s) { s->Terminate(); });
+
+ while (t.duration_s() < delay) {
+ ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+
+ int service_count = 0;
+ ServiceManager::GetInstance().ForEachService(
+ [&service_count] (Service* s) {
+ // Count the number of services running.
+ // Exclude the console as it will ignore the SIGTERM signal
+ // and not exit.
+ // Note: SVC_CONSOLE actually means "requires console" but
+ // it is only used by the shell.
+ if (s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
+ service_count++;
+ }
+ });
+
+ if (service_count == 0) {
+ // All terminable services terminated. We can exit early.
+ break;
+ }
+
+ // Wait a bit before recounting the number or running services.
+ std::this_thread::sleep_for(50ms);
+ }
+ LOG(VERBOSE) << "Terminating running services took " << t;
+ }
+
+ return android_reboot_with_callback(cmd, 0, reboot_target, callback_on_ro_remount);
}
-int do_trigger(const std::vector<std::string>& args)
-{
+static int do_trigger(const std::vector<std::string>& args) {
ActionManager::GetInstance().QueueEventTrigger(args[1]);
return 0;
}
-int do_symlink(const std::vector<std::string>& args)
-{
+static int do_symlink(const std::vector<std::string>& args) {
return symlink(args[1].c_str(), args[2].c_str());
}
-int do_rm(const std::vector<std::string>& args)
-{
+static int do_rm(const std::vector<std::string>& args) {
return unlink(args[1].c_str());
}
-int do_rmdir(const std::vector<std::string>& args)
-{
+static int do_rmdir(const std::vector<std::string>& args) {
return rmdir(args[1].c_str());
}
-int do_sysclktz(const std::vector<std::string>& args)
-{
- struct timezone tz;
-
- if (args.size() != 2)
- return -1;
-
- memset(&tz, 0, sizeof(tz));
- tz.tz_minuteswest = std::stoi(args[1]);
- if (settimeofday(NULL, &tz))
- return -1;
- return 0;
+static int do_sysclktz(const std::vector<std::string>& args) {
+ struct timezone tz = {};
+ if (android::base::ParseInt(args[1], &tz.tz_minuteswest) && settimeofday(NULL, &tz) != -1) {
+ return 0;
+ }
+ return -1;
}
-int do_verity_load_state(const std::vector<std::string>& args) {
+static int do_verity_load_state(const std::vector<std::string>& args) {
int mode = -1;
int rc = fs_mgr_load_verity_state(&mode);
- if (rc == 0 && mode == VERITY_MODE_LOGGING) {
+ if (rc == 0 && mode != VERITY_MODE_DEFAULT) {
ActionManager::GetInstance().QueueEventTrigger("verity-logging");
}
return rc;
}
-static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) {
+static void verity_update_property(fstab_rec *fstab, const char *mount_point,
+ int mode, int status) {
property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
android::base::StringPrintf("%d", mode).c_str());
}
-int do_verity_update_state(const std::vector<std::string>& args) {
+static int do_verity_update_state(const std::vector<std::string>& args) {
return fs_mgr_update_verity_state(verity_update_property);
}
-int do_write(const std::vector<std::string>& args)
-{
+static int do_write(const std::vector<std::string>& args) {
const char* path = args[1].c_str();
const char* value = args[2].c_str();
- return write_file(path, value);
+ return write_file(path, value) ? 0 : 1;
}
-int do_copy(const std::vector<std::string>& args)
-{
+static int do_copy(const std::vector<std::string>& args) {
char *buffer = NULL;
int rc = 0;
int fd1 = -1, fd2 = -1;
@@ -690,9 +825,6 @@
int brtw, brtr;
char *p;
- if (args.size() != 3)
- return -1;
-
if (stat(args[1].c_str(), &info) < 0)
return -1;
@@ -743,7 +875,7 @@
return rc;
}
-int do_chown(const std::vector<std::string>& args) {
+static int do_chown(const std::vector<std::string>& args) {
/* GID is optional. */
if (args.size() == 3) {
if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
@@ -771,7 +903,7 @@
return mode;
}
-int do_chmod(const std::vector<std::string>& args) {
+static int do_chmod(const std::vector<std::string>& args) {
mode_t mode = get_mode(args[1].c_str());
if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
return -errno;
@@ -779,72 +911,125 @@
return 0;
}
-int do_restorecon(const std::vector<std::string>& args) {
+static int do_restorecon(const std::vector<std::string>& args) {
int ret = 0;
- for (auto it = std::next(args.begin()); it != args.end(); ++it) {
- if (restorecon(it->c_str()) < 0)
- ret = -errno;
+ struct flag_type {const char* name; int value;};
+ static const flag_type flags[] = {
+ {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
+ {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
+ {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
+ {0, 0}
+ };
+
+ int flag = 0;
+
+ bool in_flags = true;
+ for (size_t i = 1; i < args.size(); ++i) {
+ if (android::base::StartsWith(args[i], "--")) {
+ if (!in_flags) {
+ LOG(ERROR) << "restorecon - flags must precede paths";
+ return -1;
+ }
+ bool found = false;
+ for (size_t j = 0; flags[j].name; ++j) {
+ if (args[i] == flags[j].name) {
+ flag |= flags[j].value;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ LOG(ERROR) << "restorecon - bad flag " << args[i];
+ return -1;
+ }
+ } else {
+ in_flags = false;
+ if (restorecon(args[i].c_str(), flag) < 0) {
+ ret = -errno;
+ }
+ }
}
return ret;
}
-int do_restorecon_recursive(const std::vector<std::string>& args) {
- int ret = 0;
-
- for (auto it = std::next(args.begin()); it != args.end(); ++it) {
- if (restorecon_recursive(it->c_str()) < 0)
- ret = -errno;
- }
- return ret;
+static int do_restorecon_recursive(const std::vector<std::string>& args) {
+ std::vector<std::string> non_const_args(args);
+ non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
+ return do_restorecon(non_const_args);
}
-int do_loglevel(const std::vector<std::string>& args) {
- if (args.size() != 2) {
- ERROR("loglevel: missing argument\n");
- return -EINVAL;
+static int do_loglevel(const std::vector<std::string>& args) {
+ // TODO: support names instead/as well?
+ int log_level = -1;
+ android::base::ParseInt(args[1], &log_level);
+ android::base::LogSeverity severity;
+ switch (log_level) {
+ case 7: severity = android::base::DEBUG; break;
+ case 6: severity = android::base::INFO; break;
+ case 5:
+ case 4: severity = android::base::WARNING; break;
+ case 3: severity = android::base::ERROR; break;
+ case 2:
+ case 1:
+ case 0: severity = android::base::FATAL; break;
+ default:
+ LOG(ERROR) << "loglevel: invalid log level " << log_level;
+ return -EINVAL;
}
-
- int log_level = std::stoi(args[1]);
- if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
- ERROR("loglevel: invalid log level'%d'\n", log_level);
- return -EINVAL;
- }
- klog_set_level(log_level);
+ android::base::SetMinimumLogSeverity(severity);
return 0;
}
-int do_load_persist_props(const std::vector<std::string>& args) {
- if (args.size() == 1) {
- load_persist_props();
- return 0;
- }
- return -1;
+static int do_load_persist_props(const std::vector<std::string>& args) {
+ load_persist_props();
+ return 0;
}
-int do_load_all_props(const std::vector<std::string>& args) {
- if (args.size() == 1) {
- load_all_props();
- return 0;
- }
- return -1;
+static int do_load_system_props(const std::vector<std::string>& args) {
+ load_system_props();
+ return 0;
}
-int do_wait(const std::vector<std::string>& args)
-{
+static int do_wait(const std::vector<std::string>& args) {
if (args.size() == 2) {
- return wait_for_file(args[1].c_str(), COMMAND_RETRY_TIMEOUT);
+ return wait_for_file(args[1].c_str(), kCommandRetryTimeout);
} else if (args.size() == 3) {
- return wait_for_file(args[1].c_str(), std::stoi(args[2]));
- } else
+ int timeout;
+ if (android::base::ParseInt(args[2], &timeout)) {
+ return wait_for_file(args[1].c_str(), std::chrono::seconds(timeout));
+ }
+ }
+ return -1;
+}
+
+static int do_wait_for_prop(const std::vector<std::string>& args) {
+ const char* name = args[1].c_str();
+ const char* value = args[2].c_str();
+ size_t value_len = strlen(value);
+
+ if (!is_legal_property_name(name)) {
+ LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
+ << "\") failed: bad name";
return -1;
+ }
+ if (value_len >= PROP_VALUE_MAX) {
+ LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
+ << "\") failed: value too long";
+ return -1;
+ }
+ if (!wait_property(name, value)) {
+ LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
+ << "\") failed: init already in waiting";
+ return -1;
+ }
+ return 0;
}
/*
* Callback to make a directory from the ext4 code
*/
-static int do_installkeys_ensure_dir_exists(const char* dir)
-{
+static int do_installkeys_ensure_dir_exists(const char* dir) {
if (make_dir(dir, 0700) && errno != EEXIST) {
return -1;
}
@@ -852,17 +1037,68 @@
return 0;
}
-int do_installkey(const std::vector<std::string>& args)
-{
- if (args.size() != 2) {
- return -1;
- }
+static bool is_file_crypto() {
+ std::string value = property_get("ro.crypto.type");
+ return value == "file";
+}
- std::string prop_value = property_get("ro.crypto.type");
- if (prop_value != "file") {
+static int do_installkey(const std::vector<std::string>& args) {
+ if (!is_file_crypto()) {
return 0;
}
-
return e4crypt_create_device_key(args[1].c_str(),
do_installkeys_ensure_dir_exists);
}
+
+static int do_init_user0(const std::vector<std::string>& args) {
+ return e4crypt_do_init_user0();
+}
+
+BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+ constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+ static const Map builtin_functions = {
+ {"bootchart", {1, 1, do_bootchart}},
+ {"chmod", {2, 2, do_chmod}},
+ {"chown", {2, 3, do_chown}},
+ {"class_reset", {1, 1, do_class_reset}},
+ {"class_start", {1, 1, do_class_start}},
+ {"class_stop", {1, 1, do_class_stop}},
+ {"copy", {2, 2, do_copy}},
+ {"domainname", {1, 1, do_domainname}},
+ {"enable", {1, 1, do_enable}},
+ {"exec", {1, kMax, do_exec}},
+ {"export", {2, 2, do_export}},
+ {"hostname", {1, 1, do_hostname}},
+ {"ifup", {1, 1, do_ifup}},
+ {"init_user0", {0, 0, do_init_user0}},
+ {"insmod", {1, kMax, do_insmod}},
+ {"installkey", {1, 1, do_installkey}},
+ {"load_persist_props", {0, 0, do_load_persist_props}},
+ {"load_system_props", {0, 0, do_load_system_props}},
+ {"loglevel", {1, 1, do_loglevel}},
+ {"mkdir", {1, 4, do_mkdir}},
+ {"mount_all", {1, kMax, do_mount_all}},
+ {"mount", {3, kMax, do_mount}},
+ {"umount", {1, 1, do_umount}},
+ {"powerctl", {1, 1, do_powerctl}},
+ {"restart", {1, 1, do_restart}},
+ {"restorecon", {1, kMax, do_restorecon}},
+ {"restorecon_recursive", {1, kMax, do_restorecon_recursive}},
+ {"rm", {1, 1, do_rm}},
+ {"rmdir", {1, 1, do_rmdir}},
+ {"setprop", {2, 2, do_setprop}},
+ {"setrlimit", {3, 3, do_setrlimit}},
+ {"start", {1, 1, do_start}},
+ {"stop", {1, 1, do_stop}},
+ {"swapon_all", {1, 1, do_swapon_all}},
+ {"symlink", {2, 2, do_symlink}},
+ {"sysclktz", {1, 1, do_sysclktz}},
+ {"trigger", {1, 1, do_trigger}},
+ {"verity_load_state", {0, 0, do_verity_load_state}},
+ {"verity_update_state", {0, 0, do_verity_update_state}},
+ {"wait", {1, 2, do_wait}},
+ {"wait_for_prop", {2, 2, do_wait_for_prop}},
+ {"write", {2, 2, do_write}},
+ };
+ return builtin_functions;
+}
diff --git a/init/builtins.h b/init/builtins.h
new file mode 100644
index 0000000..53f4a71
--- /dev/null
+++ b/init/builtins.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _INIT_BUILTINS_H
+#define _INIT_BUILTINS_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "keyword_map.h"
+
+using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
+public:
+ BuiltinFunctionMap() {
+ }
+private:
+ Map& map() const override;
+};
+
+#endif
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
new file mode 100644
index 0000000..b8a9ec0
--- /dev/null
+++ b/init/capabilities.cpp
@@ -0,0 +1,194 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "capabilities.h"
+
+#include <sys/capability.h>
+#include <sys/prctl.h>
+
+#include <map>
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+#define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap }
+
+static const std::map<std::string, int> cap_map = {
+ CAP_MAP_ENTRY(CHOWN),
+ CAP_MAP_ENTRY(DAC_OVERRIDE),
+ CAP_MAP_ENTRY(DAC_READ_SEARCH),
+ CAP_MAP_ENTRY(FOWNER),
+ CAP_MAP_ENTRY(FSETID),
+ CAP_MAP_ENTRY(KILL),
+ CAP_MAP_ENTRY(SETGID),
+ CAP_MAP_ENTRY(SETUID),
+ CAP_MAP_ENTRY(SETPCAP),
+ CAP_MAP_ENTRY(LINUX_IMMUTABLE),
+ CAP_MAP_ENTRY(NET_BIND_SERVICE),
+ CAP_MAP_ENTRY(NET_BROADCAST),
+ CAP_MAP_ENTRY(NET_ADMIN),
+ CAP_MAP_ENTRY(NET_RAW),
+ CAP_MAP_ENTRY(IPC_LOCK),
+ CAP_MAP_ENTRY(IPC_OWNER),
+ CAP_MAP_ENTRY(SYS_MODULE),
+ CAP_MAP_ENTRY(SYS_RAWIO),
+ CAP_MAP_ENTRY(SYS_CHROOT),
+ CAP_MAP_ENTRY(SYS_PTRACE),
+ CAP_MAP_ENTRY(SYS_PACCT),
+ CAP_MAP_ENTRY(SYS_ADMIN),
+ CAP_MAP_ENTRY(SYS_BOOT),
+ CAP_MAP_ENTRY(SYS_NICE),
+ CAP_MAP_ENTRY(SYS_RESOURCE),
+ CAP_MAP_ENTRY(SYS_TIME),
+ CAP_MAP_ENTRY(SYS_TTY_CONFIG),
+ CAP_MAP_ENTRY(MKNOD),
+ CAP_MAP_ENTRY(LEASE),
+ CAP_MAP_ENTRY(AUDIT_WRITE),
+ CAP_MAP_ENTRY(AUDIT_CONTROL),
+ CAP_MAP_ENTRY(SETFCAP),
+ CAP_MAP_ENTRY(MAC_OVERRIDE),
+ CAP_MAP_ENTRY(MAC_ADMIN),
+ CAP_MAP_ENTRY(SYSLOG),
+ CAP_MAP_ENTRY(WAKE_ALARM),
+ CAP_MAP_ENTRY(BLOCK_SUSPEND),
+ CAP_MAP_ENTRY(AUDIT_READ),
+};
+
+static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
+
+static bool ComputeCapAmbientSupported() {
+ return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;
+}
+
+static unsigned int ComputeLastValidCap() {
+ // Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see
+ // http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.
+ unsigned int last_valid_cap = CAP_WAKE_ALARM;
+ for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0; ++last_valid_cap);
+
+ // |last_valid_cap| will be the first failing value.
+ return last_valid_cap - 1;
+}
+
+static bool DropBoundingSet(const CapSet& to_keep) {
+ unsigned int last_valid_cap = GetLastValidCap();
+ // When dropping the bounding set, attempt to drop capabilities reported at
+ // run-time, not at compile-time.
+ // If the run-time kernel is older than the compile-time headers, this
+ // avoids dropping an invalid capability. If the run-time kernel is newer
+ // than the headers, this guarantees all capabilities (even those unknown at
+ // compile time) will be dropped.
+ for (size_t cap = 0; cap <= last_valid_cap; ++cap) {
+ if (cap < to_keep.size() && to_keep.test(cap)) {
+ // No need to drop this capability.
+ continue;
+ }
+ if (cap_drop_bound(cap) == -1) {
+ PLOG(ERROR) << "cap_drop_bound(" << cap << ") failed";
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
+ cap_t caps = cap_init();
+ auto deleter = [](cap_t* p) { cap_free(*p); };
+ std::unique_ptr<cap_t, decltype(deleter)> ptr_caps(&caps, deleter);
+
+ cap_clear(caps);
+ cap_value_t value[1];
+ for (size_t cap = 0; cap < to_keep.size(); ++cap) {
+ if (to_keep.test(cap)) {
+ value[0] = cap;
+ if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
+ cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) {
+ PLOG(ERROR) << "cap_set_flag(INHERITABLE|PERMITTED, " << cap << ") failed";
+ return false;
+ }
+ }
+ }
+
+ if (add_setpcap) {
+ value[0] = CAP_SETPCAP;
+ if (cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 ||
+ cap_set_flag(caps, CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) {
+ PLOG(ERROR) << "cap_set_flag(PERMITTED|EFFECTIVE, " << CAP_SETPCAP << ") failed";
+ return false;
+ }
+ }
+
+ if (cap_set_proc(caps) != 0) {
+ PLOG(ERROR) << "cap_set_proc(" << to_keep.to_ulong() << ") failed";
+ return false;
+ }
+ return true;
+}
+
+static bool SetAmbientCaps(const CapSet& to_raise) {
+ for (size_t cap = 0; cap < to_raise.size(); ++cap) {
+ if (to_raise.test(cap)) {
+ if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
+ PLOG(ERROR) << "prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, " << cap << ") failed";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+int LookupCap(const std::string& cap_name) {
+ auto e = cap_map.find(cap_name);
+ if (e != cap_map.end()) {
+ return e->second;
+ } else {
+ return -1;
+ }
+}
+
+bool CapAmbientSupported() {
+ static bool cap_ambient_supported = ComputeCapAmbientSupported();
+ return cap_ambient_supported;
+}
+
+unsigned int GetLastValidCap() {
+ static unsigned int last_valid_cap = ComputeLastValidCap();
+ return last_valid_cap;
+}
+
+bool SetCapsForExec(const CapSet& to_keep) {
+ // Need to keep SETPCAP to drop bounding set below.
+ bool add_setpcap = true;
+ if (!SetProcCaps(to_keep, add_setpcap)) {
+ LOG(ERROR) << "failed to apply initial capset";
+ return false;
+ }
+
+ if (!DropBoundingSet(to_keep)) {
+ return false;
+ }
+
+ // If SETPCAP wasn't specifically requested, drop it now.
+ add_setpcap = false;
+ if (!SetProcCaps(to_keep, add_setpcap)) {
+ LOG(ERROR) << "failed to apply final capset";
+ return false;
+ }
+
+ // Add the capabilities to the ambient set so that they are preserved across
+ // execve(2).
+ // See http://man7.org/linux/man-pages/man7/capabilities.7.html.
+ return SetAmbientCaps(to_keep);
+}
diff --git a/init/capabilities.h b/init/capabilities.h
new file mode 100644
index 0000000..abd7fb2
--- /dev/null
+++ b/init/capabilities.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _INIT_CAPABILITIES_H
+#define _INIT_CAPABILITIES_H
+
+#include <linux/capability.h>
+
+#include <bitset>
+#include <string>
+
+using CapSet = std::bitset<CAP_LAST_CAP + 1>;
+
+int LookupCap(const std::string& cap_name);
+bool CapAmbientSupported();
+unsigned int GetLastValidCap();
+bool SetCapsForExec(const CapSet& to_keep);
+
+#endif // _INIT_CAPABILITIES_H
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
new file mode 100644
index 0000000..6e457cd
--- /dev/null
+++ b/init/descriptors.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "descriptors.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/android_get_control_file.h>
+#include <cutils/sockets.h>
+
+#include "init.h"
+#include "log.h"
+#include "util.h"
+
+DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context)
+ : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
+}
+
+DescriptorInfo::~DescriptorInfo() {
+}
+
+std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) {
+ return os << " descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_;
+}
+
+bool DescriptorInfo::operator==(const DescriptorInfo& other) const {
+ return name_ == other.name_ && type_ == other.type_ && key() == other.key();
+}
+
+void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
+ // Create
+ const std::string& contextStr = context_.empty() ? globalContext : context_;
+ int fd = Create(contextStr);
+ if (fd < 0) return;
+
+ // Publish
+ std::string publishedName = key() + name_;
+ std::for_each(publishedName.begin(), publishedName.end(),
+ [] (char& c) { c = isalnum(c) ? c : '_'; });
+
+ std::string val = android::base::StringPrintf("%d", fd);
+ add_environment(publishedName.c_str(), val.c_str());
+
+ // make sure we don't close on exec
+ fcntl(fd, F_SETFD, 0);
+}
+
+void DescriptorInfo::Clean() const {
+}
+
+SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context)
+ : DescriptorInfo(name, type, uid, gid, perm, context) {
+}
+
+void SocketInfo::Clean() const {
+ unlink(android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s", name().c_str()).c_str());
+}
+
+int SocketInfo::Create(const std::string& context) const {
+ int flags = ((type() == "stream" ? SOCK_STREAM :
+ (type() == "dgram" ? SOCK_DGRAM :
+ SOCK_SEQPACKET)));
+ return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
+}
+
+const std::string SocketInfo::key() const {
+ return ANDROID_SOCKET_ENV_PREFIX;
+}
+
+FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context)
+ // defaults OK for uid,..., they are ignored for this class.
+ : DescriptorInfo(name, type, uid, gid, perm, context) {
+}
+
+int FileInfo::Create(const std::string&) const {
+ int flags = (type() == "r") ? O_RDONLY :
+ (type() == "w") ? O_WRONLY :
+ O_RDWR;
+
+ // Make sure we do not block on open (eg: devices can chose to block on
+ // carrier detect). Our intention is never to delay launch of a service
+ // for such a condition. The service can perform its own blocking on
+ // carrier detect.
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name().c_str(),
+ flags | O_NONBLOCK)));
+
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open file '" << name().c_str() << "'";
+ return -1;
+ }
+
+ // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
+ fcntl(fd, F_SETFL, flags);
+
+ LOG(INFO) << "Opened file '" << name().c_str() << "'"
+ << ", flags " << std::oct << flags << std::dec;
+
+ return fd.release();
+}
+
+const std::string FileInfo::key() const {
+ return ANDROID_FILE_ENV_PREFIX;
+}
diff --git a/init/descriptors.h b/init/descriptors.h
new file mode 100644
index 0000000..ff276fb
--- /dev/null
+++ b/init/descriptors.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef _INIT_DESCRIPTORS_H
+#define _INIT_DESCRIPTORS_H
+
+#include <sys/types.h>
+
+#include <string>
+
+class DescriptorInfo {
+ public:
+ DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context);
+ virtual ~DescriptorInfo();
+
+ friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info);
+ bool operator==(const DescriptorInfo& other) const;
+
+ void CreateAndPublish(const std::string& globalContext) const;
+ virtual void Clean() const;
+
+ protected:
+ const std::string& name() const { return name_; }
+ const std::string& type() const { return type_; }
+ uid_t uid() const { return uid_; }
+ gid_t gid() const { return gid_; }
+ int perm() const { return perm_; }
+ const std::string& context() const { return context_; }
+
+ private:
+ std::string name_;
+ std::string type_;
+ uid_t uid_;
+ gid_t gid_;
+ int perm_;
+ std::string context_;
+
+ virtual int Create(const std::string& globalContext) const = 0;
+ virtual const std::string key() const = 0;
+};
+
+std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info);
+
+class SocketInfo : public DescriptorInfo {
+ public:
+ SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context);
+ void Clean() const override;
+ private:
+ virtual int Create(const std::string& context) const override;
+ virtual const std::string key() const override;
+};
+
+class FileInfo : public DescriptorInfo {
+ public:
+ FileInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context);
+ private:
+ virtual int Create(const std::string& context) const override;
+ virtual const std::string key() const override;
+};
+
+#endif
diff --git a/init/devices.cpp b/init/devices.cpp
index 3652c57..6af237c 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -14,32 +14,39 @@
* limitations under the License.
*/
+#include <dirent.h>
#include <errno.h>
+#include <fcntl.h>
#include <fnmatch.h>
+#include <libgen.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <fcntl.h>
-#include <dirent.h>
-#include <unistd.h>
#include <string.h>
-
+#include <sys/sendfile.h>
#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
#include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
#include <linux/netlink.h>
+#include <memory>
+#include <thread>
+
#include <selinux/selinux.h>
#include <selinux/label.h>
#include <selinux/android.h>
#include <selinux/avc.h>
#include <private/android_filesystem_config.h>
-#include <sys/time.h>
-#include <sys/wait.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <cutils/list.h>
#include <cutils/uevent.h>
@@ -104,13 +111,18 @@
return -ENOMEM;
node->dp.name = strdup(name);
- if (!node->dp.name)
+ if (!node->dp.name) {
+ free(node);
return -ENOMEM;
+ }
if (attr) {
node->dp.attr = strdup(attr);
- if (!node->dp.attr)
+ if (!node->dp.attr) {
+ free(node->dp.name);
+ free(node);
return -ENOMEM;
+ }
}
node->dp.perm = perm;
@@ -127,49 +139,6 @@
return 0;
}
-void fixup_sys_perms(const char *upath)
-{
- char buf[512];
- struct listnode *node;
- struct perms_ *dp;
-
- /* upaths omit the "/sys" that paths in this list
- * contain, so we add 4 when comparing...
- */
- list_for_each(node, &sys_perms) {
- dp = &(node_to_item(node, struct perm_node, plist))->dp;
- if (dp->prefix) {
- if (strncmp(upath, dp->name + 4, strlen(dp->name + 4)))
- continue;
- } else if (dp->wildcard) {
- if (fnmatch(dp->name + 4, upath, FNM_PATHNAME) != 0)
- continue;
- } else {
- if (strcmp(upath, dp->name + 4))
- continue;
- }
-
- if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
- break;
-
- snprintf(buf, sizeof(buf), "/sys%s/%s", upath, dp->attr);
- INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
- chown(buf, dp->uid, dp->gid);
- chmod(buf, dp->perm);
- }
-
- // Now fixup SELinux file labels
- int len = snprintf(buf, sizeof(buf), "/sys%s", upath);
- if ((len < 0) || ((size_t) len >= sizeof(buf))) {
- // Overflow
- return;
- }
- if (access(buf, F_OK) == 0) {
- INFO("restorecon_recursive: %s\n", buf);
- restorecon_recursive(buf);
- }
-}
-
static bool perm_path_matches(const char *path, struct perms_ *dp)
{
if (dp->prefix) {
@@ -186,6 +155,45 @@
return false;
}
+static bool match_subsystem(perms_* dp, const char* pattern,
+ const char* path, const char* subsystem) {
+ if (!pattern || !subsystem || strstr(dp->name, subsystem) == NULL) {
+ return false;
+ }
+
+ std::string subsys_path = android::base::StringPrintf(pattern, subsystem, basename(path));
+ return perm_path_matches(subsys_path.c_str(), dp);
+}
+
+static void fixup_sys_perms(const char* upath, const char* subsystem) {
+ // upaths omit the "/sys" that paths in this list
+ // contain, so we prepend it...
+ std::string path = std::string(SYSFS_PREFIX) + upath;
+
+ listnode* node;
+ list_for_each(node, &sys_perms) {
+ perms_* dp = &(node_to_item(node, perm_node, plist))->dp;
+ if (match_subsystem(dp, SYSFS_PREFIX "/class/%s/%s", path.c_str(), subsystem)) {
+ ; // matched
+ } else if (match_subsystem(dp, SYSFS_PREFIX "/bus/%s/devices/%s", path.c_str(), subsystem)) {
+ ; // matched
+ } else if (!perm_path_matches(path.c_str(), dp)) {
+ continue;
+ }
+
+ std::string attr_file = path + "/" + dp->attr;
+ LOG(INFO) << "fixup " << attr_file
+ << " " << dp->uid << " " << dp->gid << " " << std::oct << dp->perm;
+ chown(attr_file.c_str(), dp->uid, dp->gid);
+ chmod(attr_file.c_str(), dp->perm);
+ }
+
+ if (access(path.c_str(), F_OK) == 0) {
+ LOG(VERBOSE) << "restorecon_recursive: " << path;
+ restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+ }
+}
+
static mode_t get_device_perm(const char *path, const char **links,
unsigned *uid, unsigned *gid)
{
@@ -241,7 +249,10 @@
mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
- selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
+ if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
+ PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+ return;
+ }
setfscreatecon(secontext);
dev = makedev(major, minor);
@@ -251,14 +262,31 @@
* racy. Fixing the gid race at least fixed the issue with system_server
* opening dynamic input devices under the AID_INPUT gid. */
setegid(gid);
- mknod(path, mode, dev);
+ /* If the node already exists update its SELinux label to handle cases when
+ * it was created with the wrong context during coldboot procedure. */
+ if (mknod(path, mode, dev) && (errno == EEXIST)) {
+
+ char* fcon = nullptr;
+ int rc = lgetfilecon(path, &fcon);
+ if (rc < 0) {
+ PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
+ goto out;
+ }
+
+ bool different = strcmp(fcon, secontext) != 0;
+ freecon(fcon);
+
+ if (different && lsetfilecon(path, secontext)) {
+ PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
+ }
+ }
+
+out:
chown(path, uid, -1);
setegid(AID_ROOT);
- if (secontext) {
- freecon(secontext);
- setfscreatecon(NULL);
- }
+ freecon(secontext);
+ setfscreatecon(NULL);
}
static void add_platform_device(const char *path)
@@ -273,7 +301,7 @@
name += 9;
}
- INFO("adding platform device %s (%s)\n", name, path);
+ LOG(VERBOSE) << "adding platform device " << name << " (" << path << ")";
bus = (platform_node*) calloc(1, sizeof(struct platform_node));
bus->path = strdup(path);
@@ -312,7 +340,7 @@
list_for_each_reverse(node, &platform_names) {
bus = node_to_item(node, struct platform_node, list);
if (!strcmp(path, bus->path)) {
- INFO("removing platform device %s\n", bus->name);
+ LOG(INFO) << "removing platform device " << bus->name;
free(bus->path);
list_remove(node);
free(bus);
@@ -401,16 +429,16 @@
}
if (LOG_UEVENTS) {
- INFO("event { '%s', '%s', '%s', '%s', %d, %d }\n",
- uevent->action, uevent->path, uevent->subsystem,
- uevent->firmware, uevent->major, uevent->minor);
+ LOG(INFO) << android::base::StringPrintf("event { '%s', '%s', '%s', '%s', %d, %d }",
+ uevent->action, uevent->path, uevent->subsystem,
+ uevent->firmware, uevent->major, uevent->minor);
}
}
static char **get_character_device_symlinks(struct uevent *uevent)
{
const char *parent;
- char *slash;
+ const char *slash;
char **links;
int link_num = 0;
int width;
@@ -464,7 +492,7 @@
{
const char *device;
struct platform_node *pdev;
- char *slash;
+ const char *slash;
const char *type;
char buf[256];
char link_path[256];
@@ -487,15 +515,16 @@
return NULL;
memset(links, 0, sizeof(char *) * 4);
- INFO("found %s device %s\n", type, device);
+ LOG(INFO) << "found " << type << " device " << device;
snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
if (uevent->partition_name) {
p = strdup(uevent->partition_name);
sanitize(p);
- if (strcmp(uevent->partition_name, p))
- NOTICE("Linking partition '%s' as '%s'\n", uevent->partition_name, p);
+ if (strcmp(uevent->partition_name, p)) {
+ LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
+ }
if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
link_num++;
else
@@ -519,30 +548,49 @@
return links;
}
+static void make_link_init(const char* oldpath, const char* newpath) {
+ const char* slash = strrchr(newpath, '/');
+ if (!slash) return;
+
+ if (mkdir_recursive(dirname(newpath), 0755)) {
+ PLOG(ERROR) << "Failed to create directory " << dirname(newpath);
+ }
+
+ if (symlink(oldpath, newpath) && errno != EEXIST) {
+ PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
+ }
+}
+
+static void remove_link(const char* oldpath, const char* newpath) {
+ std::string path;
+ if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath);
+}
+
static void handle_device(const char *action, const char *devpath,
const char *path, int block, int major, int minor, char **links)
{
- int i;
-
if(!strcmp(action, "add")) {
make_device(devpath, path, block, major, minor, (const char **)links);
if (links) {
- for (i = 0; links[i]; i++)
- make_link(devpath, links[i]);
+ for (int i = 0; links[i]; i++) {
+ make_link_init(devpath, links[i]);
+ }
}
}
if(!strcmp(action, "remove")) {
if (links) {
- for (i = 0; links[i]; i++)
+ for (int i = 0; links[i]; i++) {
remove_link(devpath, links[i]);
+ }
}
unlink(devpath);
}
if (links) {
- for (i = 0; links[i]; i++)
+ for (int i = 0; links[i]; i++) {
free(links[i]);
+ }
free(links);
}
}
@@ -573,22 +621,24 @@
/* too-long names would overrun our buffer */
if(strlen(name) > len) {
- ERROR("DEVPATH=%s exceeds %u-character limit on filename; ignoring event\n",
- name, len);
+ LOG(ERROR) << "DEVPATH=" << name << " exceeds " << len << "-character limit on filename; ignoring event";
return NULL;
}
return name;
}
+#define DEVPATH_LEN 96
+#define MAX_DEV_NAME 64
+
static void handle_block_device_event(struct uevent *uevent)
{
const char *base = "/dev/block/";
const char *name;
- char devpath[96];
+ char devpath[DEVPATH_LEN];
char **links = NULL;
- name = parse_device_name(uevent, 64);
+ name = parse_device_name(uevent, MAX_DEV_NAME);
if (!name)
return;
@@ -602,19 +652,16 @@
uevent->major, uevent->minor, links);
}
-#define DEVPATH_LEN 96
-
static bool assemble_devpath(char *devpath, const char *dirname,
const char *devname)
{
int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname);
if (s < 0) {
- ERROR("failed to assemble device path (%s); ignoring event\n",
- strerror(errno));
+ PLOG(ERROR) << "failed to assemble device path; ignoring event";
return false;
} else if (s >= DEVPATH_LEN) {
- ERROR("%s/%s exceeds %u-character limit on path; ignoring event\n",
- dirname, devname, DEVPATH_LEN);
+ LOG(ERROR) << dirname << "/" << devname
+ << " exceeds " << DEVPATH_LEN << "-character limit on path; ignoring event";
return false;
}
return true;
@@ -638,7 +685,7 @@
char devpath[DEVPATH_LEN] = {0};
char **links = NULL;
- name = parse_device_name(uevent, 64);
+ name = parse_device_name(uevent, MAX_DEV_NAME);
if (!name)
return;
@@ -658,8 +705,7 @@
break;
default:
- ERROR("%s subsystem's devpath option is not set; ignoring event\n",
- uevent->subsystem);
+ LOG(ERROR) << uevent->subsystem << " subsystem's devpath option is not set; ignoring event";
return;
}
@@ -715,9 +761,8 @@
} else if(!strncmp(uevent->subsystem, "sound", 5)) {
base = "/dev/snd/";
make_dir(base, 0755);
- } else if(!strncmp(uevent->subsystem, "misc", 4) &&
- !strncmp(name, "log_", 4)) {
- INFO("kernel logger is deprecated\n");
+ } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) {
+ LOG(INFO) << "kernel logger is deprecated";
base = "/dev/log/";
make_dir(base, 0755);
name += 4;
@@ -735,7 +780,7 @@
static void handle_device_event(struct uevent *uevent)
{
if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
- fixup_sys_perms(uevent->path);
+ fixup_sys_perms(uevent->path, uevent->subsystem);
if (!strncmp(uevent->subsystem, "block", 5)) {
handle_block_device_event(uevent);
@@ -746,152 +791,93 @@
}
}
-static int load_firmware(int fw_fd, int loading_fd, int data_fd)
-{
- struct stat st;
- long len_to_copy;
- int ret = 0;
+static void load_firmware(uevent* uevent, const std::string& root,
+ int fw_fd, size_t fw_size,
+ int loading_fd, int data_fd) {
+ // Start transfer.
+ android::base::WriteFully(loading_fd, "1", 1);
- if(fstat(fw_fd, &st) < 0)
- return -1;
- len_to_copy = st.st_size;
-
- write(loading_fd, "1", 1); /* start transfer */
-
- while (len_to_copy > 0) {
- char buf[PAGE_SIZE];
- ssize_t nr;
-
- nr = read(fw_fd, buf, sizeof(buf));
- if(!nr)
- break;
- if(nr < 0) {
- ret = -1;
- break;
- }
-
- len_to_copy -= nr;
- while (nr > 0) {
- ssize_t nw = 0;
-
- nw = write(data_fd, buf + nw, nr);
- if(nw <= 0) {
- ret = -1;
- goto out;
- }
- nr -= nw;
- }
+ // Copy the firmware.
+ int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
+ if (rc == -1) {
+ PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
}
-out:
- if(!ret)
- write(loading_fd, "0", 1); /* successful end of transfer */
- else
- write(loading_fd, "-1", 2); /* abort transfer */
-
- return ret;
+ // Tell the firmware whether to abort or commit.
+ const char* response = (rc != -1) ? "0" : "-1";
+ android::base::WriteFully(loading_fd, response, strlen(response));
}
-static int is_booting(void)
-{
+static int is_booting() {
return access("/dev/.booting", F_OK) == 0;
}
-static void process_firmware_event(struct uevent *uevent)
-{
- char *root, *loading, *data;
- int l, loading_fd, data_fd, fw_fd;
- size_t i;
+static void process_firmware_event(uevent* uevent) {
int booting = is_booting();
- INFO("firmware: loading '%s' for '%s'\n",
- uevent->firmware, uevent->path);
+ LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
- l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
- if (l == -1)
+ std::string root = android::base::StringPrintf("/sys%s", uevent->path);
+ std::string loading = root + "/loading";
+ std::string data = root + "/data";
+
+ android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
+ if (loading_fd == -1) {
+ PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
return;
+ }
- l = asprintf(&loading, "%sloading", root);
- if (l == -1)
- goto root_free_out;
-
- l = asprintf(&data, "%sdata", root);
- if (l == -1)
- goto loading_free_out;
-
- loading_fd = open(loading, O_WRONLY|O_CLOEXEC);
- if(loading_fd < 0)
- goto data_free_out;
-
- data_fd = open(data, O_WRONLY|O_CLOEXEC);
- if(data_fd < 0)
- goto loading_close_out;
+ android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
+ if (data_fd == -1) {
+ PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
+ return;
+ }
try_loading_again:
- for (i = 0; i < ARRAY_SIZE(firmware_dirs); i++) {
- char *file = NULL;
- l = asprintf(&file, "%s/%s", firmware_dirs[i], uevent->firmware);
- if (l == -1)
- goto data_free_out;
- fw_fd = open(file, O_RDONLY|O_CLOEXEC);
- free(file);
- if (fw_fd >= 0) {
- if(!load_firmware(fw_fd, loading_fd, data_fd))
- INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
- else
- INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
- break;
+ for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
+ std::string file = android::base::StringPrintf("%s/%s", firmware_dirs[i], uevent->firmware);
+ android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
+ struct stat sb;
+ if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
+ load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+ return;
}
}
- if (fw_fd < 0) {
- if (booting) {
- /* If we're not fully booted, we may be missing
- * filesystems needed for firmware, wait and retry.
- */
- usleep(100000);
- booting = is_booting();
- goto try_loading_again;
- }
- INFO("firmware: could not open '%s': %s\n", uevent->firmware, strerror(errno));
- write(loading_fd, "-1", 2);
- goto data_close_out;
- }
- close(fw_fd);
-data_close_out:
- close(data_fd);
-loading_close_out:
- close(loading_fd);
-data_free_out:
- free(data);
-loading_free_out:
- free(loading);
-root_free_out:
- free(root);
+ if (booting) {
+ // If we're not fully booted, we may be missing
+ // filesystems needed for firmware, wait and retry.
+ std::this_thread::sleep_for(100ms);
+ booting = is_booting();
+ goto try_loading_again;
+ }
+
+ LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
+
+ // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
+ write(loading_fd, "-1", 2);
}
-static void handle_firmware_event(struct uevent *uevent)
-{
- pid_t pid;
+static void handle_firmware_event(uevent* uevent) {
+ if (strcmp(uevent->subsystem, "firmware")) return;
+ if (strcmp(uevent->action, "add")) return;
- if(strcmp(uevent->subsystem, "firmware"))
- return;
-
- if(strcmp(uevent->action, "add"))
- return;
-
- /* we fork, to avoid making large memory allocations in init proper */
- pid = fork();
- if (!pid) {
+ // Loading the firmware in a child means we can do that in parallel...
+ // (We ignore SIGCHLD rather than wait for our children.)
+ pid_t pid = fork();
+ if (pid == 0) {
+ Timer t;
process_firmware_event(uevent);
+ LOG(INFO) << "loading " << uevent->path << " took " << t;
_exit(EXIT_SUCCESS);
- } else if (pid < 0) {
- ERROR("could not fork to process firmware event: %s\n", strerror(errno));
+ } else if (pid == -1) {
+ PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
}
}
#define UEVENT_MSG_LEN 2048
-void handle_device_fd()
+
+static inline void handle_device_fd_with(void (handle_uevent)(struct uevent*))
{
char msg[UEVENT_MSG_LEN+2];
int n;
@@ -904,21 +890,28 @@
struct uevent uevent;
parse_event(msg, &uevent);
-
- if (selinux_status_updated() > 0) {
- struct selabel_handle *sehandle2;
- sehandle2 = selinux_android_file_context_handle();
- if (sehandle2) {
- selabel_close(sehandle);
- sehandle = sehandle2;
- }
- }
-
- handle_device_event(&uevent);
- handle_firmware_event(&uevent);
+ handle_uevent(&uevent);
}
}
+void handle_device_fd()
+{
+ handle_device_fd_with(
+ [](struct uevent *uevent) {
+ if (selinux_status_updated() > 0) {
+ struct selabel_handle *sehandle2;
+ sehandle2 = selinux_android_file_context_handle();
+ if (sehandle2) {
+ selabel_close(sehandle);
+ sehandle = sehandle2;
+ }
+ }
+
+ handle_device_event(uevent);
+ handle_firmware_event(uevent);
+ });
+}
+
/* Coldboot walks parts of the /sys tree and pokes the uevent files
** to cause the kernel to regenerate device add events that happened
** before init's device manager was started
@@ -964,13 +957,71 @@
static void coldboot(const char *path)
{
- DIR *d = opendir(path);
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
if(d) {
- do_coldboot(d);
- closedir(d);
+ do_coldboot(d.get());
}
}
+static void early_uevent_handler(struct uevent *uevent, const char *base, bool is_block)
+{
+ const char *name;
+ char devpath[DEVPATH_LEN];
+
+ if (is_block && strncmp(uevent->subsystem, "block", 5))
+ return;
+
+ name = parse_device_name(uevent, MAX_DEV_NAME);
+ if (!name) {
+ LOG(ERROR) << "Failed to parse dev name from uevent: " << uevent->action
+ << " " << uevent->partition_name << " " << uevent->partition_num
+ << " " << uevent->major << ":" << uevent->minor;
+ return;
+ }
+
+ snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+ make_dir(base, 0755);
+
+ dev_t dev = makedev(uevent->major, uevent->minor);
+ mode_t mode = 0600 | (is_block ? S_IFBLK : S_IFCHR);
+ mknod(devpath, mode, dev);
+}
+
+void early_create_dev(const std::string& syspath, early_device_type dev_type)
+{
+ android::base::unique_fd dfd(open(syspath.c_str(), O_RDONLY));
+ if (dfd < 0) {
+ LOG(ERROR) << "Failed to open " << syspath;
+ return;
+ }
+
+ android::base::unique_fd fd(openat(dfd, "uevent", O_WRONLY));
+ if (fd < 0) {
+ LOG(ERROR) << "Failed to open " << syspath << "/uevent";
+ return;
+ }
+
+ fcntl(device_fd, F_SETFL, O_NONBLOCK);
+
+ write(fd, "add\n", 4);
+ handle_device_fd_with(dev_type == EARLY_BLOCK_DEV ?
+ [](struct uevent *uevent) {
+ early_uevent_handler(uevent, "/dev/block/", true);
+ } :
+ [](struct uevent *uevent) {
+ early_uevent_handler(uevent, "/dev/", false);
+ });
+}
+
+int early_device_socket_open() {
+ device_fd = uevent_open_socket(256*1024, true);
+ return device_fd < 0;
+}
+
+void early_device_socket_close() {
+ close(device_fd);
+}
+
void device_init() {
sehandle = selinux_android_file_context_handle();
selinux_status_open(true);
@@ -983,7 +1034,7 @@
fcntl(device_fd, F_SETFL, O_NONBLOCK);
if (access(COLDBOOT_DONE, F_OK) == 0) {
- NOTICE("Skipping coldboot, already done!\n");
+ LOG(VERBOSE) << "Skipping coldboot, already done!";
return;
}
@@ -992,10 +1043,9 @@
coldboot("/sys/block");
coldboot("/sys/devices");
close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
- NOTICE("Coldboot took %.2fs.\n", t.duration());
+ LOG(INFO) << "Coldboot took " << t;
}
-int get_device_fd()
-{
+int get_device_fd() {
return device_fd;
}
diff --git a/init/devices.h b/init/devices.h
index 6cb0a77..8e9ab7d 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,6 +21,13 @@
extern void handle_device_fd();
extern void device_init(void);
+
+enum early_device_type { EARLY_BLOCK_DEV, EARLY_CHAR_DEV };
+
+extern int early_device_socket_open();
+extern void early_device_socket_close();
+extern void early_create_dev(const std::string& syspath, early_device_type dev_type);
+
extern int add_dev_perms(const char *name, const char *attr,
mode_t perm, unsigned int uid,
unsigned int gid, unsigned short prefix,
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
index d6082aa..c4ff6df 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -11,7 +11,7 @@
LOGROOT=/data/bootchart
TARBALL=bootchart.tgz
-FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
+FILES="header proc_stat.log proc_ps.log proc_diskstats.log"
for f in $FILES; do
adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
new file mode 100644
index 0000000..d52247b
--- /dev/null
+++ b/init/import_parser.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "import_parser.h"
+
+#include "errno.h"
+
+#include <string>
+#include <vector>
+
+#include "log.h"
+#include "util.h"
+
+bool ImportParser::ParseSection(const std::vector<std::string>& args,
+ std::string* err) {
+ if (args.size() != 2) {
+ *err = "single argument needed for import\n";
+ return false;
+ }
+
+ std::string conf_file;
+ bool ret = expand_props(args[1], &conf_file);
+ if (!ret) {
+ *err = "error while expanding import";
+ return false;
+ }
+
+ LOG(INFO) << "Added '" << conf_file << "' to import list";
+ imports_.emplace_back(std::move(conf_file));
+ return true;
+}
+
+void ImportParser::EndFile(const std::string& filename) {
+ auto current_imports = std::move(imports_);
+ imports_.clear();
+ for (const auto& s : current_imports) {
+ if (!Parser::GetInstance().ParseConfig(s)) {
+ PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
+ }
+ }
+}
diff --git a/init/import_parser.h b/init/import_parser.h
new file mode 100644
index 0000000..0e91025
--- /dev/null
+++ b/init/import_parser.h
@@ -0,0 +1,43 @@
+/*
+ * 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 _INIT_IMPORT_PARSER_H
+#define _INIT_IMPORT_PARSER_H
+
+#include "init_parser.h"
+
+#include <string>
+#include <vector>
+
+class ImportParser : public SectionParser {
+public:
+ ImportParser() {
+ }
+ bool ParseSection(const std::vector<std::string>& args,
+ std::string* err) override;
+ bool ParseLineSection(const std::vector<std::string>& args,
+ const std::string& filename, int line,
+ std::string* err) const override {
+ return true;
+ }
+ void EndSection() override {
+ }
+ void EndFile(const std::string& filename) override;
+private:
+ std::vector<std::string> imports_;
+};
+
+#endif
diff --git a/init/init.cpp b/init/init.cpp
index c94a6fe..c8c18d2 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,6 +18,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <libgen.h>
#include <paths.h>
#include <signal.h>
@@ -29,32 +30,33 @@
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
-#include <mtd/mtd-user.h>
-
#include <selinux/selinux.h>
#include <selinux/label.h>
#include <selinux/android.h>
-#include <base/file.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
-#include <cutils/android_reboot.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/fs.h>
#include <cutils/iosched_policy.h>
#include <cutils/list.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
+#include <fstream>
#include <memory>
#include "action.h"
#include "bootchart.h"
#include "devices.h"
+#include "fs_mgr.h"
+#include "import_parser.h"
#include "init.h"
#include "init_parser.h"
#include "keychords.h"
@@ -66,6 +68,8 @@
#include "util.h"
#include "watchdogd.h"
+using android::base::StringPrintf;
+
struct selabel_handle *sehandle;
struct selabel_handle *sehandle_prop;
@@ -73,9 +77,8 @@
static char qemu[32];
-int have_console;
-std::string console_name = "/dev/console";
-static time_t process_needs_restart;
+std::string default_console = "/dev/console";
+static time_t process_needs_restart_at;
const char *ENV[32];
@@ -83,12 +86,16 @@
static int epoll_fd = -1;
+static std::unique_ptr<Timer> waiting_for_prop(nullptr);
+static std::string wait_prop_name;
+static std::string wait_prop_value;
+
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) {
- ERROR("epoll_ctl failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "epoll_ctl failed";
}
}
@@ -99,7 +106,7 @@
size_t key_len = strlen(key);
/* The last environment entry is reserved to terminate the list */
- for (n = 0; n < (ARRAY_SIZE(ENV) - 1); n++) {
+ for (n = 0; n < (arraysize(ENV) - 1); n++) {
/* Delete any existing entry for this key */
if (ENV[n] != NULL) {
@@ -119,96 +126,86 @@
}
}
- ERROR("No env. room to store: '%s':'%s'\n", key, val);
+ LOG(ERROR) << "No env. room to store: '" << key << "':'" << val << "'";
return -1;
}
+bool wait_property(const char *name, const char *value)
+{
+ if (waiting_for_prop) {
+ return false;
+ }
+ if (property_get(name) != value) {
+ // Current property value is not equal to expected value
+ wait_prop_name = name;
+ wait_prop_value = value;
+ waiting_for_prop.reset(new Timer());
+ } else {
+ LOG(INFO) << "wait_property(\"" << name << "\", \"" << value << "\"): already set";
+ }
+ return true;
+}
+
void property_changed(const char *name, const char *value)
{
if (property_triggers_enabled)
ActionManager::GetInstance().QueuePropertyTrigger(name, value);
+ if (waiting_for_prop) {
+ if (wait_prop_name == name && wait_prop_value == value) {
+ wait_prop_name.clear();
+ wait_prop_value.clear();
+ LOG(INFO) << "Wait for property took " << *waiting_for_prop;
+ waiting_for_prop.reset();
+ }
+ }
}
static void restart_processes()
{
- process_needs_restart = 0;
- ServiceManager::GetInstance().
- ForEachServiceWithFlags(SVC_RESTARTING, [] (Service* s) {
- s->RestartIfNeeded(process_needs_restart);
- });
+ process_needs_restart_at = 0;
+ ServiceManager::GetInstance().ForEachServiceWithFlags(SVC_RESTARTING, [](Service* s) {
+ s->RestartIfNeeded(&process_needs_restart_at);
+ });
}
-static void msg_start(const std::string& name)
-{
- Service* svc = nullptr;
- std::vector<std::string> vargs;
-
- size_t colon_pos = name.find(':');
- if (colon_pos == std::string::npos) {
- svc = ServiceManager::GetInstance().FindServiceByName(name);
- } else {
- std::string service_name(name.substr(0, colon_pos));
- std::string args(name.substr(colon_pos + 1));
- vargs = android::base::Split(args, " ");
-
- svc = ServiceManager::GetInstance().FindServiceByName(service_name);
- }
-
- if (svc) {
- svc->Start(vargs);
- } else {
- ERROR("no such service '%s'\n", name.c_str());
- }
-}
-
-static void msg_stop(const std::string& name)
-{
+void handle_control_message(const std::string& msg, const std::string& name) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
+ if (svc == nullptr) {
+ LOG(ERROR) << "no such service '" << name << "'";
+ return;
+ }
- if (svc) {
+ if (msg == "start") {
+ svc->Start();
+ } else if (msg == "stop") {
svc->Stop();
- } else {
- ERROR("no such service '%s'\n", name.c_str());
- }
-}
-
-static void msg_restart(const std::string& name)
-{
- Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
-
- if (svc) {
+ } else if (msg == "restart") {
svc->Restart();
} else {
- ERROR("no such service '%s'\n", name.c_str());
- }
-}
-
-void handle_control_message(const std::string& msg, const std::string& arg)
-{
- if (msg == "start") {
- msg_start(arg);
- } else if (msg == "stop") {
- msg_stop(arg);
- } else if (msg == "restart") {
- msg_restart(arg);
- } else {
- ERROR("unknown control msg '%s'\n", msg.c_str());
+ LOG(ERROR) << "unknown control msg '" << msg << "'";
}
}
static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
Timer t;
- NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
- // Any longer than 1s is an unreasonable length of time to delay booting.
- // If you're hitting this timeout, check that you didn't make your
- // sepolicy regular expressions too expensive (http://b/19899875).
- if (wait_for_file(COLDBOOT_DONE, 1)) {
- ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);
+ LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
+
+ // Historically we had a 1s timeout here because we weren't otherwise
+ // tracking boot time, and many OEMs made their sepolicy regular
+ // expressions too expensive (http://b/19899875).
+
+ // Now we're tracking boot time, just log the time taken to a system
+ // property. We still panic if it takes more than a minute though,
+ // because any build that slow isn't likely to boot at all, and we'd
+ // rather any test lab devices fail back to the bootloader.
+ if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
+ LOG(ERROR) << "Timed out waiting for " COLDBOOT_DONE;
+ panic();
}
- NOTICE("Waiting for %s took %.2fs.\n", COLDBOOT_DONE, t.duration());
+ property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration_ms()).c_str());
return 0;
}
@@ -240,11 +237,11 @@
open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
if (hwrandom_fd == -1) {
if (errno == ENOENT) {
- ERROR("/dev/hw_random not found\n");
- /* It's not an error to not have a Hardware RNG. */
- result = 0;
+ LOG(ERROR) << "/dev/hw_random not found";
+ // It's not an error to not have a Hardware RNG.
+ result = 0;
} else {
- ERROR("Failed to open /dev/hw_random: %s\n", strerror(errno));
+ PLOG(ERROR) << "Failed to open /dev/hw_random";
}
goto ret;
}
@@ -252,7 +249,7 @@
urandom_fd = TEMP_FAILURE_RETRY(
open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
if (urandom_fd == -1) {
- ERROR("Failed to open /dev/urandom: %s\n", strerror(errno));
+ PLOG(ERROR) << "Failed to open /dev/urandom";
goto ret;
}
@@ -260,23 +257,22 @@
chunk_size = TEMP_FAILURE_RETRY(
read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
if (chunk_size == -1) {
- ERROR("Failed to read from /dev/hw_random: %s\n", strerror(errno));
+ PLOG(ERROR) << "Failed to read from /dev/hw_random";
goto ret;
} else if (chunk_size == 0) {
- ERROR("Failed to read from /dev/hw_random: EOF\n");
+ LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
goto ret;
}
chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
if (chunk_size == -1) {
- ERROR("Failed to write to /dev/urandom: %s\n", strerror(errno));
+ PLOG(ERROR) << "Failed to write to /dev/urandom";
goto ret;
}
total_bytes_written += chunk_size;
}
- INFO("Mixed %zu bytes from /dev/hw_random into /dev/urandom",
- total_bytes_written);
+ LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
result = 0;
ret:
@@ -289,6 +285,136 @@
return result;
}
+static void security_failure() {
+ LOG(ERROR) << "Security failure...";
+ panic();
+}
+
+static bool set_highest_available_option_value(std::string path, int min, int max)
+{
+ std::ifstream inf(path, std::fstream::in);
+ if (!inf) {
+ LOG(ERROR) << "Cannot open for reading: " << path;
+ return false;
+ }
+
+ int current = max;
+ while (current >= min) {
+ // try to write out new value
+ std::string str_val = std::to_string(current);
+ std::ofstream of(path, std::fstream::out);
+ if (!of) {
+ LOG(ERROR) << "Cannot open for writing: " << path;
+ return false;
+ }
+ of << str_val << std::endl;
+ of.close();
+
+ // check to make sure it was recorded
+ inf.seekg(0);
+ std::string str_rec;
+ inf >> str_rec;
+ if (str_val.compare(str_rec) == 0) {
+ break;
+ }
+ current--;
+ }
+ inf.close();
+
+ if (current < min) {
+ LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
+ return false;
+ }
+ return true;
+}
+
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+/* __attribute__((unused)) due to lack of mips support: see mips block
+ * in set_mmap_rnd_bits_action */
+static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
+ std::string path;
+ if (compat) {
+ path = MMAP_RND_COMPAT_PATH;
+ } else {
+ path = MMAP_RND_PATH;
+ }
+
+ return set_highest_available_option_value(path, min, start);
+}
+
+/*
+ * Set /proc/sys/vm/mmap_rnd_bits and potentially
+ * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
+ * Returns -1 if unable to set these to an acceptable value.
+ *
+ * To support this sysctl, the following upstream commits are needed:
+ *
+ * d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
+ * e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
+ * 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
+ * 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
+ * ec9ee4acd97c drivers: char: random: add get_random_long()
+ * 5ef11c35ce86 mm: ASLR: use get_random_long()
+ */
+static int set_mmap_rnd_bits_action(const std::vector<std::string>& args)
+{
+ int ret = -1;
+
+ /* values are arch-dependent */
+#if defined(__aarch64__)
+ /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
+ if (set_mmap_rnd_bits_min(33, 24, false)
+ && set_mmap_rnd_bits_min(16, 16, true)) {
+ ret = 0;
+ }
+#elif defined(__x86_64__)
+ /* x86_64 supports 28 - 32 bits */
+ if (set_mmap_rnd_bits_min(32, 32, false)
+ && set_mmap_rnd_bits_min(16, 16, true)) {
+ ret = 0;
+ }
+#elif defined(__arm__) || defined(__i386__)
+ /* check to see if we're running on 64-bit kernel */
+ bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
+ /* supported 32-bit architecture must have 16 bits set */
+ if (set_mmap_rnd_bits_min(16, 16, h64)) {
+ ret = 0;
+ }
+#elif defined(__mips__) || defined(__mips64__)
+ // TODO: add mips support b/27788820
+ ret = 0;
+#else
+ LOG(ERROR) << "Unknown architecture";
+#endif
+
+ if (ret == -1) {
+ LOG(ERROR) << "Unable to set adequate mmap entropy value!";
+ security_failure();
+ }
+ return ret;
+}
+
+#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
+#define KPTR_RESTRICT_MINVALUE 2
+#define KPTR_RESTRICT_MAXVALUE 4
+
+/* Set kptr_restrict to the highest available level.
+ *
+ * Aborts if unable to set this to an acceptable value.
+ */
+static int set_kptr_restrict_action(const std::vector<std::string>& args)
+{
+ std::string path = KPTR_RESTRICT_PATH;
+
+ if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
+ LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
+ security_failure();
+ }
+ return 0;
+}
+
static int keychord_init_action(const std::vector<std::string>& args)
{
keychord_init();
@@ -299,36 +425,8 @@
{
std::string console = property_get("ro.boot.console");
if (!console.empty()) {
- console_name = "/dev/" + console;
+ default_console = "/dev/" + console;
}
-
- int fd = open(console_name.c_str(), O_RDWR | O_CLOEXEC);
- if (fd >= 0)
- have_console = 1;
- close(fd);
-
- fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
- if (fd >= 0) {
- const char *msg;
- msg = "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n" // console is 40 cols x 30 lines
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- " A N D R O I D ";
- write(fd, msg, strlen(msg));
- close(fd);
- }
-
return 0;
}
@@ -337,15 +435,26 @@
if (for_emulator) {
// In the emulator, export any kernel option with the "ro.kernel." prefix.
- property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
+ property_set(StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
return;
}
if (key == "qemu") {
strlcpy(qemu, value.c_str(), sizeof(qemu));
} else if (android::base::StartsWith(key, "androidboot.")) {
- property_set(android::base::StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(),
- value.c_str());
+ property_set(StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(), value.c_str());
+ }
+}
+
+static void export_oem_lock_status() {
+ if (property_get("ro.oem_unlock_supported") != "1") {
+ return;
+ }
+
+ std::string value = property_get("ro.boot.verifiedbootstate");
+
+ if (!value.empty()) {
+ property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
}
}
@@ -362,7 +471,7 @@
{ "ro.boot.hardware", "ro.hardware", "unknown", },
{ "ro.boot.revision", "ro.revision", "0", },
};
- for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
+ for (size_t i = 0; i < arraysize(prop_map); i++) {
std::string value = property_get(prop_map[i].src_prop);
property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
}
@@ -371,12 +480,12 @@
static void process_kernel_dt() {
static const char android_dir[] = "/proc/device-tree/firmware/android";
- std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);
+ std::string file_name = StringPrintf("%s/compatible", android_dir);
std::string dt_file;
android::base::ReadFileToString(file_name, &dt_file);
if (!dt_file.compare("android,firmware")) {
- ERROR("firmware/android is not compatible with 'android,firmware'\n");
+ LOG(ERROR) << "firmware/android is not compatible with 'android,firmware'";
return;
}
@@ -385,24 +494,21 @@
struct dirent *dp;
while ((dp = readdir(dir.get())) != NULL) {
- if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) {
+ if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
continue;
}
- file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);
+ file_name = StringPrintf("%s/%s", android_dir, dp->d_name);
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
- std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);
+ std::string property_name = StringPrintf("ro.boot.%s", dp->d_name);
property_set(property_name.c_str(), dt_file.c_str());
}
}
static void process_kernel_cmdline() {
- // Don't expose the raw commandline to unprivileged processes.
- chmod("/proc/cmdline", 0440);
-
// The first pass does the common stuff, and finds if we are in qemu.
// The second pass is only necessary for qemu to export all kernel params
// as properties.
@@ -410,11 +516,17 @@
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
+static int property_enable_triggers_action(const std::vector<std::string>& args)
+{
+ /* Enable property triggers. */
+ property_triggers_enabled = 1;
+ return 0;
+}
+
static int queue_property_triggers_action(const std::vector<std::string>& args)
{
+ ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
ActionManager::GetInstance().QueueAllPropertyTriggers();
- /* enable property triggers */
- property_triggers_enabled = 1;
return 0;
}
@@ -447,35 +559,20 @@
return true;
}
-int selinux_reload_policy(void)
-{
- INFO("SELinux: Attempting to reload policy files\n");
+static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
- if (selinux_android_reload_policy() == -1) {
- return -1;
+ property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
+
+ if (!d || !d->name || !d->cr) {
+ LOG(ERROR) << "audit_callback invoked with null data arguments!";
+ return 0;
}
- if (sehandle)
- selabel_close(sehandle);
-
- if (sehandle_prop)
- selabel_close(sehandle_prop);
-
- selinux_init_all_handles();
+ snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
+ d->cr->pid, d->cr->uid, d->cr->gid);
return 0;
}
-static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
- snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
- return 0;
-}
-
-static void security_failure() {
- ERROR("Security failure; rebooting into recovery mode...\n");
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- while (true) { pause(); } // never reached
-}
-
static void selinux_initialize(bool in_kernel_domain) {
Timer t;
@@ -486,9 +583,9 @@
selinux_set_callback(SELINUX_CB_AUDIT, cb);
if (in_kernel_domain) {
- INFO("Loading SELinux policy...\n");
+ LOG(INFO) << "Loading SELinux policy...";
if (selinux_android_load_policy() < 0) {
- ERROR("failed to load policy: %s\n", strerror(errno));
+ PLOG(ERROR) << "failed to load policy";
security_failure();
}
@@ -496,23 +593,136 @@
bool is_enforcing = selinux_is_enforcing();
if (kernel_enforcing != is_enforcing) {
if (security_setenforce(is_enforcing)) {
- ERROR("security_setenforce(%s) failed: %s\n",
- is_enforcing ? "true" : "false", strerror(errno));
+ PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
security_failure();
}
}
- if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
+ if (!write_file("/sys/fs/selinux/checkreqprot", "0")) {
security_failure();
}
- NOTICE("(Initializing SELinux %s took %.2fs.)\n",
- is_enforcing ? "enforcing" : "non-enforcing", t.duration());
+ // init's first stage can't set properties, so pass the time to the second stage.
+ setenv("INIT_SELINUX_TOOK", std::to_string(t.duration_ms()).c_str(), 1);
} else {
selinux_init_all_handles();
}
}
+// Set the UDC controller for the ConfigFS USB Gadgets.
+// Read the UDC controller in use from "/sys/class/udc".
+// In case of multiple UDC controllers select the first one.
+static void set_usb_controller() {
+ std::unique_ptr<DIR, decltype(&closedir)>dir(opendir("/sys/class/udc"), closedir);
+ if (!dir) return;
+
+ dirent* dp;
+ while ((dp = readdir(dir.get())) != nullptr) {
+ if (dp->d_name[0] == '.') continue;
+
+ property_set("sys.usb.controller", dp->d_name);
+ break;
+ }
+}
+
+/* Returns a new path consisting of base_path and the file name in reference_path. */
+static std::string get_path(const std::string& base_path, const std::string& reference_path) {
+ std::string::size_type pos = reference_path.rfind('/');
+ if (pos == std::string::npos) {
+ return base_path + '/' + reference_path;
+ } else {
+ return base_path + reference_path.substr(pos);
+ }
+}
+
+/* Imports the fstab info from cmdline. */
+static std::string import_cmdline_fstab() {
+ std::string prefix, fstab, fstab_full;
+
+ import_kernel_cmdline(false,
+ [&](const std::string& key, const std::string& value, bool in_qemu __attribute__((__unused__))) {
+ if (key == "android.early.prefix") {
+ prefix = value;
+ } else if (key == "android.early.fstab") {
+ fstab = value;
+ }
+ });
+ if (!fstab.empty()) {
+ // Convert "mmcblk0p09+/odm+ext4+ro+verify" to "mmcblk0p09 /odm ext4 ro verify"
+ std::replace(fstab.begin(), fstab.end(), '+', ' ');
+ for (const auto& entry : android::base::Split(fstab, "\n")) {
+ fstab_full += prefix + entry + '\n';
+ }
+ }
+ return fstab_full;
+}
+
+/* Early mount vendor and ODM partitions. The fstab info is read from kernel cmdline. */
+static void early_mount() {
+ std::string fstab_string = import_cmdline_fstab();
+ if (fstab_string.empty()) {
+ LOG(INFO) << "Failed to load vendor fstab from kernel cmdline";
+ return;
+ }
+ FILE *fstab_file = fmemopen((void *)fstab_string.c_str(), fstab_string.length(), "r");
+ if (!fstab_file) {
+ PLOG(ERROR) << "Failed to open fstab string as FILE";
+ return;
+ }
+ std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_file(fstab_file), fs_mgr_free_fstab);
+ fclose(fstab_file);
+ if (!fstab) {
+ LOG(ERROR) << "Failed to parse fstab string: " << fstab_string;
+ return;
+ }
+ LOG(INFO) << "Loaded vendor fstab from cmdline";
+
+ if (early_device_socket_open()) {
+ LOG(ERROR) << "Failed to open device uevent socket";
+ return;
+ }
+
+ /* Create /dev/device-mapper for dm-verity */
+ early_create_dev("/sys/devices/virtual/misc/device-mapper", EARLY_CHAR_DEV);
+
+ for (int i = 0; i < fstab->num_entries; ++i) {
+ struct fstab_rec *rec = &fstab->recs[i];
+ std::string mount_point = rec->mount_point;
+ std::string syspath = rec->blk_device;
+
+ if (mount_point != "/vendor" && mount_point != "/odm")
+ continue;
+
+ /* Create mount target under /dev/block/ from sysfs via uevent */
+ LOG(INFO) << "Mounting " << mount_point << " from " << syspath << "...";
+ char *devpath = strdup(get_path("/dev/block", syspath).c_str());
+ if (!devpath) {
+ PLOG(ERROR) << "Failed to strdup dev path in early mount " << syspath;
+ continue;
+ }
+ rec->blk_device = devpath;
+ early_create_dev(syspath, EARLY_BLOCK_DEV);
+
+ int rc = fs_mgr_early_setup_verity(rec);
+ if (rc == FS_MGR_EARLY_SETUP_VERITY_SUCCESS) {
+ /* Mount target is changed to /dev/block/dm-<n>; initiate its creation from sysfs counterpart */
+ early_create_dev(get_path("/sys/devices/virtual/block", rec->blk_device), EARLY_BLOCK_DEV);
+ } else if (rc == FS_MGR_EARLY_SETUP_VERITY_FAIL) {
+ LOG(ERROR) << "Failed to set up dm-verity on " << rec->blk_device;
+ continue;
+ } else { /* FS_MGR_EARLY_SETUP_VERITY_NO_VERITY */
+ LOG(INFO) << "dm-verity disabled on debuggable device; mount directly on " << rec->blk_device;
+ }
+
+ mkdir(mount_point.c_str(), 0755);
+ rc = mount(rec->blk_device, mount_point.c_str(), rec->fs_type, rec->flags, rec->fs_options);
+ if (rc) {
+ PLOG(ERROR) << "Failed to mount on " << rec->blk_device;
+ }
+ }
+ early_device_socket_close();
+}
+
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
@@ -522,12 +732,17 @@
return watchdogd_main(argc, argv);
}
+ boot_clock::time_point start_time = boot_clock::now();
+
// Clear the umask.
umask(0);
add_environment("PATH", _PATH_DEFPATH);
- bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
+ bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
+
+ // Don't expose the raw commandline to unprivileged processes.
+ chmod("/proc/cmdline", 0440);
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
@@ -536,21 +751,50 @@
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
- mount("proc", "/proc", "proc", 0, NULL);
+ #define MAKE_STR(x) __STRING(x)
+ mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
+ gid_t groups[] = { AID_READPROC };
+ setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
+ mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+ mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+ mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
+ mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
}
- // We must have some place other than / to create the device nodes for
- // kmsg and null, otherwise we won't be able to remount / read-only
- // later on. Now that tmpfs is mounted on /dev, we can actually talk
- // to the outside world.
- open_devnull_stdio();
- klog_init();
- klog_set_level(KLOG_NOTICE_LEVEL);
+ // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
+ // talk to the outside world...
+ InitKernelLogging(argv);
- NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
+ LOG(INFO) << "init " << (is_first_stage ? "first" : "second") << " stage started!";
- if (!is_first_stage) {
+ if (is_first_stage) {
+ // Mount devices defined in android.early.* kernel commandline
+ early_mount();
+
+ // Set up SELinux, loading the SELinux policy.
+ selinux_initialize(true);
+
+ // We're in the kernel domain, so re-exec init to transition to the init domain now
+ // that the SELinux policy has been loaded.
+ if (restorecon("/init") == -1) {
+ PLOG(ERROR) << "restorecon failed";
+ security_failure();
+ }
+
+ setenv("INIT_SECOND_STAGE", "true", 1);
+
+ static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
+ uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
+ setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ms).c_str(), 1);
+
+ char* path = argv[0];
+ char* args[] = { path, nullptr };
+ if (execv(path, args) == -1) {
+ PLOG(ERROR) << "execv(\"" << path << "\") failed";
+ security_failure();
+ }
+ } else {
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
@@ -564,47 +808,62 @@
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();
- }
- // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
- selinux_initialize(is_first_stage);
+ // Make the time that init started available for bootstat to log.
+ property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
+ property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
- // If we're in the kernel domain, re-exec init to transition to the init domain now
- // that the SELinux policy has been loaded.
- if (is_first_stage) {
- if (restorecon("/init") == -1) {
- ERROR("restorecon failed: %s\n", strerror(errno));
- security_failure();
- }
- char* path = argv[0];
- char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
- if (execv(path, args) == -1) {
- ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
- security_failure();
- }
+ // Clean up our environment.
+ unsetenv("INIT_SECOND_STAGE");
+ unsetenv("INIT_STARTED_AT");
+ unsetenv("INIT_SELINUX_TOOK");
+
+ // Now set up SELinux for second stage.
+ selinux_initialize(false);
}
// These directories were necessarily created before initial policy load
// and therefore need their security context restored to the proper value.
// This must happen before /dev is populated by ueventd.
- NOTICE("Running restorecon...\n");
+ LOG(INFO) << "Running restorecon...";
restorecon("/dev");
+ restorecon("/dev/kmsg");
restorecon("/dev/socket");
+ restorecon("/dev/random");
+ restorecon("/dev/urandom");
restorecon("/dev/__properties__");
- restorecon_recursive("/sys");
+ restorecon("/plat_property_contexts");
+ restorecon("/nonplat_property_contexts");
+ restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+ restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+ restorecon("/dev/device-mapper");
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
- ERROR("epoll_create1 failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "epoll_create1 failed";
exit(1);
}
signal_handler_init();
property_load_boot_defaults();
+ export_oem_lock_status();
start_property_service();
+ set_usb_controller();
- init_parse_config("/init.rc");
+ const BuiltinFunctionMap function_map;
+ Action::set_function_map(&function_map);
+
+ Parser& parser = Parser::GetInstance();
+ parser.AddSectionParser("service",std::make_unique<ServiceParser>());
+ parser.AddSectionParser("on", std::make_unique<ActionParser>());
+ parser.AddSectionParser("import", std::make_unique<ImportParser>());
+ std::string bootscript = property_get("ro.boot.init_rc");
+ if (bootscript.empty()) {
+ parser.ParseConfig("/init.rc");
+ } else {
+ parser.ParseConfig(bootscript);
+ }
ActionManager& am = ActionManager::GetInstance();
@@ -614,6 +873,8 @@
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+ am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
+ am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
@@ -636,28 +897,27 @@
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
while (true) {
- if (!waiting_for_exec) {
+ if (!(waiting_for_exec || waiting_for_prop)) {
am.ExecuteOneCommand();
restart_processes();
}
- int timeout = -1;
- if (process_needs_restart) {
- timeout = (process_needs_restart - gettime()) * 1000;
- if (timeout < 0)
- timeout = 0;
+ // By default, sleep until something happens.
+ int epoll_timeout_ms = -1;
+
+ // If there's a process that needs restarting, wake up in time for that.
+ if (process_needs_restart_at != 0) {
+ epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
+ if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
- if (am.HasMoreCommands()) {
- timeout = 0;
- }
-
- bootchart_sample(&timeout);
+ // If there's more work to do, wake up again immediately.
+ if (am.HasMoreCommands()) epoll_timeout_ms = 0;
epoll_event ev;
- int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
+ int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
- ERROR("epoll_wait failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
diff --git a/init/init.h b/init/init.h
index 345d442..4e4da32 100644
--- a/init/init.h
+++ b/init/init.h
@@ -22,12 +22,9 @@
class Action;
class Service;
-#define COMMAND_RETRY_TIMEOUT 5
-
extern const char *ENV[32];
extern bool waiting_for_exec;
-extern int have_console;
-extern std::string console_name;
+extern std::string default_console;
extern struct selabel_handle *sehandle;
extern struct selabel_handle *sehandle_prop;
@@ -35,10 +32,10 @@
void property_changed(const char *name, const char *value);
-int selinux_reload_policy(void);
-
void register_epoll_handler(int fd, void (*fn)());
int add_environment(const char* key, const char* val);
+bool wait_property(const char *name, const char *value);
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 12f44f7..406b339 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,485 +14,141 @@
* limitations under the License.
*/
-#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
#include "action.h"
-#include "init.h"
#include "init_parser.h"
#include "log.h"
#include "parser.h"
-#include "property_service.h"
#include "service.h"
#include "util.h"
-#include <base/stringprintf.h>
-#include <cutils/iosched_policy.h>
-#include <cutils/list.h>
+#include <android-base/stringprintf.h>
-static list_declare(service_list);
-
-struct import {
- struct listnode list;
- const char *filename;
-};
-
-static void *parse_service(struct parse_state *state, int nargs, char **args);
-static void parse_line_service(struct parse_state *state, int nargs, char **args);
-
-static void *parse_action(struct parse_state *state, int nargs, char **args);
-static void parse_line_action(struct parse_state *state, int nargs, char **args);
-
-#define SECTION 0x01
-#define COMMAND 0x02
-#define OPTION 0x04
-
-#include "keywords.h"
-
-#define KEYWORD(symbol, flags, nargs, func) \
- [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
-
-static struct {
- const char *name;
- int (*func)(const std::vector<std::string>& args);
- size_t nargs;
- unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
- [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
-#include "keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_name(kw) (keyword_info[kw].name)
-#define kw_func(kw) (keyword_info[kw].func)
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-void dump_parser_state() {
- ServiceManager::GetInstance().DumpState();
- ActionManager::GetInstance().DumpState();
+Parser::Parser() {
}
-static int lookup_keyword(const char *s)
-{
- switch (*s++) {
- case 'b':
- if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
- break;
- case 'c':
- if (!strcmp(s, "opy")) return K_copy;
- if (!strcmp(s, "lass")) return K_class;
- if (!strcmp(s, "lass_start")) return K_class_start;
- if (!strcmp(s, "lass_stop")) return K_class_stop;
- if (!strcmp(s, "lass_reset")) return K_class_reset;
- if (!strcmp(s, "onsole")) return K_console;
- if (!strcmp(s, "hown")) return K_chown;
- if (!strcmp(s, "hmod")) return K_chmod;
- if (!strcmp(s, "ritical")) return K_critical;
- break;
- case 'd':
- if (!strcmp(s, "isabled")) return K_disabled;
- if (!strcmp(s, "omainname")) return K_domainname;
- break;
- case 'e':
- if (!strcmp(s, "nable")) return K_enable;
- if (!strcmp(s, "xec")) return K_exec;
- if (!strcmp(s, "xport")) return K_export;
- break;
- case 'g':
- if (!strcmp(s, "roup")) return K_group;
- break;
- case 'h':
- if (!strcmp(s, "ostname")) return K_hostname;
- break;
- case 'i':
- if (!strcmp(s, "oprio")) return K_ioprio;
- if (!strcmp(s, "fup")) return K_ifup;
- if (!strcmp(s, "nsmod")) return K_insmod;
- if (!strcmp(s, "mport")) return K_import;
- if (!strcmp(s, "nstallkey")) return K_installkey;
- break;
- case 'k':
- if (!strcmp(s, "eycodes")) return K_keycodes;
- break;
- case 'l':
- if (!strcmp(s, "oglevel")) return K_loglevel;
- if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
- if (!strcmp(s, "oad_all_props")) return K_load_all_props;
- break;
- case 'm':
- if (!strcmp(s, "kdir")) return K_mkdir;
- if (!strcmp(s, "ount_all")) return K_mount_all;
- if (!strcmp(s, "ount")) return K_mount;
- break;
- case 'o':
- if (!strcmp(s, "n")) return K_on;
- if (!strcmp(s, "neshot")) return K_oneshot;
- if (!strcmp(s, "nrestart")) return K_onrestart;
- break;
- case 'p':
- if (!strcmp(s, "owerctl")) return K_powerctl;
- break;
- case 'r':
- if (!strcmp(s, "estart")) return K_restart;
- if (!strcmp(s, "estorecon")) return K_restorecon;
- if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive;
- if (!strcmp(s, "mdir")) return K_rmdir;
- if (!strcmp(s, "m")) return K_rm;
- break;
- case 's':
- if (!strcmp(s, "eclabel")) return K_seclabel;
- if (!strcmp(s, "ervice")) return K_service;
- if (!strcmp(s, "etenv")) return K_setenv;
- if (!strcmp(s, "etprop")) return K_setprop;
- if (!strcmp(s, "etrlimit")) return K_setrlimit;
- if (!strcmp(s, "ocket")) return K_socket;
- if (!strcmp(s, "tart")) return K_start;
- if (!strcmp(s, "top")) return K_stop;
- if (!strcmp(s, "wapon_all")) return K_swapon_all;
- if (!strcmp(s, "ymlink")) return K_symlink;
- if (!strcmp(s, "ysclktz")) return K_sysclktz;
- break;
- case 't':
- if (!strcmp(s, "rigger")) return K_trigger;
- break;
- case 'u':
- if (!strcmp(s, "ser")) return K_user;
- break;
- case 'v':
- if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
- if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
- break;
- case 'w':
- if (!strcmp(s, "rite")) return K_write;
- if (!strcmp(s, "ritepid")) return K_writepid;
- if (!strcmp(s, "ait")) return K_wait;
- break;
- }
- return K_UNKNOWN;
+Parser& Parser::GetInstance() {
+ static Parser instance;
+ return instance;
}
-static void parse_line_no_op(struct parse_state*, int, char**) {
+void Parser::AddSectionParser(const std::string& name,
+ std::unique_ptr<SectionParser> parser) {
+ section_parsers_[name] = std::move(parser);
}
-int expand_props(const std::string& src, std::string* dst) {
- const char *src_ptr = src.c_str();
-
- if (!dst) {
- return -1;
- }
-
- /* - variables can either be $x.y or ${x.y}, in case they are only part
- * of the string.
- * - will accept $$ as a literal $.
- * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
- * bad things will happen
- */
- while (*src_ptr) {
- const char *c;
-
- c = strchr(src_ptr, '$');
- if (!c) {
- dst->append(src_ptr);
- break;
- }
-
- dst->append(src_ptr, c);
- c++;
-
- if (*c == '$') {
- dst->push_back(*(c++));
- src_ptr = c;
- continue;
- } else if (*c == '\0') {
- break;
- }
-
- std::string prop_name;
- if (*c == '{') {
- c++;
- const char* end = strchr(c, '}');
- if (!end) {
- // failed to find closing brace, abort.
- ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
- goto err;
- }
- prop_name = std::string(c, end);
- c = end + 1;
- } else {
- prop_name = c;
- ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
- c);
- c += prop_name.size();
- }
-
- if (prop_name.empty()) {
- ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
- goto err;
- }
-
- std::string prop_val = property_get(prop_name.c_str());
- if (prop_val.empty()) {
- ERROR("property '%s' doesn't exist while expanding '%s'\n",
- prop_name.c_str(), src.c_str());
- goto err;
- }
-
- dst->append(prop_val);
- src_ptr = c;
- continue;
- }
-
- return 0;
-err:
- return -1;
-}
-
-static void parse_import(struct parse_state *state, int nargs, char **args)
-{
- if (nargs != 2) {
- ERROR("single argument needed for import\n");
- return;
- }
-
- std::string conf_file;
- int ret = expand_props(args[1], &conf_file);
- if (ret) {
- ERROR("error while handling import on line '%d' in '%s'\n",
- state->line, state->filename);
- return;
- }
-
- struct import* import = (struct import*) calloc(1, sizeof(struct import));
- import->filename = strdup(conf_file.c_str());
-
- struct listnode *import_list = (listnode*) state->priv;
- list_add_tail(import_list, &import->list);
- INFO("Added '%s' to import list\n", import->filename);
-}
-
-static void parse_new_section(struct parse_state *state, int kw,
- int nargs, char **args)
-{
- printf("[ %s %s ]\n", args[0],
- nargs > 1 ? args[1] : "");
- switch(kw) {
- case K_service:
- state->context = parse_service(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_service;
- return;
- }
- break;
- case K_on:
- state->context = parse_action(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_action;
- return;
- }
- break;
- case K_import:
- parse_import(state, nargs, args);
- break;
- }
- state->parse_line = parse_line_no_op;
-}
-
-static void parse_config(const char *fn, const std::string& data)
-{
- struct listnode import_list;
- struct listnode *node;
- char *args[INIT_PARSER_MAXARGS];
-
- int nargs = 0;
-
+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');
parse_state state;
- state.filename = fn;
+ state.filename = filename.c_str();
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
- state.parse_line = parse_line_no_op;
- list_init(&import_list);
- state.priv = &import_list;
+ SectionParser* section_parser = nullptr;
+ std::vector<std::string> args;
for (;;) {
switch (next_token(&state)) {
case T_EOF:
- state.parse_line(&state, 0, 0);
- goto parser_done;
+ if (section_parser) {
+ section_parser->EndSection();
+ }
+ return;
case T_NEWLINE:
state.line++;
- if (nargs) {
- int kw = lookup_keyword(args[0]);
- if (kw_is(kw, SECTION)) {
- state.parse_line(&state, 0, 0);
- parse_new_section(&state, kw, nargs, args);
- } else {
- state.parse_line(&state, nargs, args);
- }
- nargs = 0;
+ if (args.empty()) {
+ break;
}
+ if (section_parsers_.count(args[0])) {
+ if (section_parser) {
+ section_parser->EndSection();
+ }
+ section_parser = section_parsers_[args[0]].get();
+ std::string ret_err;
+ if (!section_parser->ParseSection(args, &ret_err)) {
+ parse_error(&state, "%s\n", ret_err.c_str());
+ section_parser = nullptr;
+ }
+ } else if (section_parser) {
+ std::string ret_err;
+ if (!section_parser->ParseLineSection(args, state.filename,
+ state.line, &ret_err)) {
+ parse_error(&state, "%s\n", ret_err.c_str());
+ }
+ }
+ args.clear();
break;
case T_TEXT:
- if (nargs < INIT_PARSER_MAXARGS) {
- args[nargs++] = state.text;
- }
+ args.emplace_back(state.text);
break;
}
}
-
-parser_done:
- list_for_each(node, &import_list) {
- struct import* import = node_to_item(node, struct import, list);
- if (!init_parse_config(import->filename)) {
- ERROR("could not import file '%s' from '%s': %s\n",
- import->filename, fn, strerror(errno));
- }
- }
}
-static bool init_parse_config_file(const char* path) {
- INFO("Parsing file %s...\n", path);
+bool Parser::ParseConfigFile(const std::string& path) {
+ LOG(INFO) << "Parsing file " << path << "...";
Timer t;
std::string data;
- if (!read_file(path, &data)) {
+ if (!read_file(path.c_str(), &data)) {
return false;
}
data.push_back('\n'); // TODO: fix parse_config.
- parse_config(path, data);
- dump_parser_state();
+ ParseData(path, data);
+ for (const auto& sp : section_parsers_) {
+ sp.second->EndFile(path);
+ }
- NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
+ // Turning this on and letting the INFO logging be discarded adds 0.2s to
+ // Nexus 9 boot time, so it's disabled by default.
+ if (false) DumpState();
+
+ LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
}
-static bool init_parse_config_dir(const char* path) {
- INFO("Parsing directory %s...\n", path);
- std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path), closedir);
+bool Parser::ParseConfigDir(const std::string& path) {
+ LOG(INFO) << "Parsing directory " << path << "...";
+ std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir) {
- ERROR("Could not import directory '%s'\n", path);
+ PLOG(ERROR) << "Could not import directory '" << path << "'";
return false;
}
dirent* current_file;
+ std::vector<std::string> files;
while ((current_file = readdir(config_dir.get()))) {
- std::string current_path =
- android::base::StringPrintf("%s/%s", path, current_file->d_name);
// Ignore directories and only process regular files.
if (current_file->d_type == DT_REG) {
- if (!init_parse_config_file(current_path.c_str())) {
- ERROR("could not import file '%s'\n", current_path.c_str());
- }
+ std::string current_path =
+ android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
+ files.emplace_back(current_path);
+ }
+ }
+ // 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)) {
+ LOG(ERROR) << "could not import file '" << file << "'";
}
}
return true;
}
-bool init_parse_config(const char* path) {
- if (is_dir(path)) {
- return init_parse_config_dir(path);
+bool Parser::ParseConfig(const std::string& path) {
+ if (is_dir(path.c_str())) {
+ return ParseConfigDir(path);
}
- return init_parse_config_file(path);
+ return ParseConfigFile(path);
}
-static void *parse_service(struct parse_state *state, int nargs, char **args)
-{
- if (nargs < 3) {
- parse_error(state, "services must have a name and a program\n");
- return nullptr;
- }
- std::vector<std::string> str_args(args + 2, args + nargs);
- std::string ret_err;
- Service* svc = ServiceManager::GetInstance().AddNewService(args[1], "default",
- str_args, &ret_err);
-
- if (!svc) {
- parse_error(state, "%s\n", ret_err.c_str());
- }
-
- return svc;
-}
-
-static void parse_line_service(struct parse_state *state, int nargs, char **args)
-{
- if (nargs == 0) {
- return;
- }
-
- Service* svc = static_cast<Service*>(state->context);
- int kw = lookup_keyword(args[0]);
- std::vector<std::string> str_args(args, args + nargs);
- std::string ret_err;
- bool ret = svc->HandleLine(kw, str_args, &ret_err);
-
- if (!ret) {
- parse_error(state, "%s\n", ret_err.c_str());
- }
-}
-
-static void *parse_action(struct parse_state* state, int nargs, char **args)
-{
- std::string ret_err;
- std::vector<std::string> triggers(args + 1, args + nargs);
- Action* ret = ActionManager::GetInstance().AddNewAction(triggers, &ret_err);
-
- if (!ret) {
- parse_error(state, "%s\n", ret_err.c_str());
- }
-
- return ret;
-}
-
-bool add_command_to_action(Action* action, const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err)
-{
- int kw;
- size_t n;
-
- kw = lookup_keyword(args[0].c_str());
- if (!kw_is(kw, COMMAND)) {
- *err = android::base::StringPrintf("invalid command '%s'\n", args[0].c_str());
- return false;
- }
-
- n = kw_nargs(kw);
- if (args.size() < n) {
- *err = android::base::StringPrintf("%s requires %zu %s\n",
- args[0].c_str(), n - 1,
- n > 2 ? "arguments" : "argument");
- return false;
- }
-
- action->AddCommand(kw_func(kw), args, filename, line);
- return true;
-}
-
-static void parse_line_action(struct parse_state* state, int nargs, char **args)
-{
- if (nargs == 0) {
- return;
- }
-
- Action* action = static_cast<Action*>(state->context);
- std::vector<std::string> str_args(args, args + nargs);
- std::string ret_err;
- bool ret = add_command_to_action(action, str_args, state->filename,
- state->line, &ret_err);
- if (!ret) {
- parse_error(state, "%s\n", ret_err.c_str());
- }
+void Parser::DumpState() const {
+ ServiceManager::GetInstance().DumpState();
+ ActionManager::GetInstance().DumpState();
}
diff --git a/init/init_parser.h b/init/init_parser.h
index 709dca8..5ed30ad 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -17,16 +17,39 @@
#ifndef _INIT_INIT_PARSER_H_
#define _INIT_INIT_PARSER_H_
+#include <map>
#include <string>
#include <vector>
-#define INIT_PARSER_MAXARGS 64
+class SectionParser {
+public:
+ virtual ~SectionParser() {
+ }
+ virtual bool ParseSection(const std::vector<std::string>& args,
+ std::string* err) = 0;
+ virtual bool ParseLineSection(const std::vector<std::string>& args,
+ const std::string& filename, int line,
+ std::string* err) const = 0;
+ virtual void EndSection() = 0;
+ virtual void EndFile(const std::string& filename) = 0;
+};
-class Action;
+class Parser {
+public:
+ static Parser& GetInstance();
+ void DumpState() const;
+ bool ParseConfig(const std::string& path);
+ void AddSectionParser(const std::string& name,
+ std::unique_ptr<SectionParser> parser);
-bool init_parse_config(const char* path);
-int expand_props(const std::string& src, std::string* dst);
-bool add_command_to_action(Action* action, const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err);
+private:
+ Parser();
+
+ 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_;
+};
#endif
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 7a7838d..3dbb2f0 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -43,7 +43,7 @@
size = sizeof(*keychord) + svc->keycodes().size() * sizeof(keychord->keycodes[0]);
keychords = (input_keychord*) realloc(keychords, keychords_length + size);
if (!keychords) {
- ERROR("could not allocate keychords\n");
+ PLOG(ERROR) << "could not allocate keychords";
keychords_length = 0;
keychords_count = 0;
return;
@@ -69,7 +69,7 @@
ret = read(keychord_fd, &id, sizeof(id));
if (ret != sizeof(id)) {
- ERROR("could not read keychord id\n");
+ PLOG(ERROR) << "could not read keychord id";
return;
}
@@ -78,11 +78,13 @@
if (adb_enabled == "running") {
Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
if (svc) {
- INFO("Starting service %s from keychord\n", svc->name().c_str());
+ LOG(INFO) << "Starting service " << svc->name() << " from keychord " << id;
svc->Start();
} else {
- ERROR("service for keychord %d not found\n", id);
+ LOG(ERROR) << "Service for keychord " << id << " not found";
}
+ } else {
+ LOG(WARNING) << "Not starting service for keychord " << id << " because ADB is disabled";
}
}
@@ -96,13 +98,13 @@
keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
if (keychord_fd == -1) {
- ERROR("could not open /dev/keychord: %s\n", strerror(errno));
+ PLOG(ERROR) << "could not open /dev/keychord";
return;
}
int ret = write(keychord_fd, keychords, keychords_length);
if (ret != keychords_length) {
- ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
+ PLOG(ERROR) << "could not configure /dev/keychord " << ret;
close(keychord_fd);
}
diff --git a/init/keyword_map.h b/init/keyword_map.h
new file mode 100644
index 0000000..693d82a
--- /dev/null
+++ b/init/keyword_map.h
@@ -0,0 +1,77 @@
+/*
+ * 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 _INIT_KEYWORD_MAP_H_
+#define _INIT_KEYWORD_MAP_H_
+
+#include <map>
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+template <typename Function>
+class KeywordMap {
+public:
+ using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
+ using Map = const std::map<std::string, FunctionInfo>;
+
+ virtual ~KeywordMap() {
+ }
+
+ const Function FindFunction(const std::string& keyword,
+ size_t num_args,
+ std::string* err) const {
+ using android::base::StringPrintf;
+
+ auto function_info_it = map().find(keyword);
+ if (function_info_it == map().end()) {
+ *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
+ return nullptr;
+ }
+
+ auto function_info = function_info_it->second;
+
+ auto min_args = std::get<0>(function_info);
+ auto max_args = std::get<1>(function_info);
+ if (min_args == max_args && num_args != min_args) {
+ *err = StringPrintf("%s requires %zu argument%s",
+ keyword.c_str(), min_args,
+ (min_args > 1 || min_args == 0) ? "s" : "");
+ return nullptr;
+ }
+
+ if (num_args < min_args || num_args > max_args) {
+ if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
+ *err = StringPrintf("%s requires at least %zu argument%s",
+ keyword.c_str(), min_args,
+ min_args > 1 ? "s" : "");
+ } else {
+ *err = StringPrintf("%s requires between %zu and %zu arguments",
+ keyword.c_str(), min_args, max_args);
+ }
+ return nullptr;
+ }
+
+ return std::get<Function>(function_info);
+ }
+
+private:
+//Map of keyword ->
+//(minimum number of arguments, maximum number of arguments, function pointer)
+ virtual Map& map() const = 0;
+};
+
+#endif
diff --git a/init/keywords.h b/init/keywords.h
deleted file mode 100644
index 922feee..0000000
--- a/init/keywords.h
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef KEYWORD
-#include <string>
-#include <vector>
-int do_bootchart_init(const std::vector<std::string>& args);
-int do_class_start(const std::vector<std::string>& args);
-int do_class_stop(const std::vector<std::string>& args);
-int do_class_reset(const std::vector<std::string>& args);
-int do_domainname(const std::vector<std::string>& args);
-int do_enable(const std::vector<std::string>& args);
-int do_exec(const std::vector<std::string>& args);
-int do_export(const std::vector<std::string>& args);
-int do_hostname(const std::vector<std::string>& args);
-int do_ifup(const std::vector<std::string>& args);
-int do_insmod(const std::vector<std::string>& args);
-int do_installkey(const std::vector<std::string>& args);
-int do_mkdir(const std::vector<std::string>& args);
-int do_mount_all(const std::vector<std::string>& args);
-int do_mount(const std::vector<std::string>& args);
-int do_powerctl(const std::vector<std::string>& args);
-int do_restart(const std::vector<std::string>& args);
-int do_restorecon(const std::vector<std::string>& args);
-int do_restorecon_recursive(const std::vector<std::string>& args);
-int do_rm(const std::vector<std::string>& args);
-int do_rmdir(const std::vector<std::string>& args);
-int do_setprop(const std::vector<std::string>& args);
-int do_setrlimit(const std::vector<std::string>& args);
-int do_start(const std::vector<std::string>& args);
-int do_stop(const std::vector<std::string>& args);
-int do_swapon_all(const std::vector<std::string>& args);
-int do_trigger(const std::vector<std::string>& args);
-int do_symlink(const std::vector<std::string>& args);
-int do_sysclktz(const std::vector<std::string>& args);
-int do_write(const std::vector<std::string>& args);
-int do_copy(const std::vector<std::string>& args);
-int do_chown(const std::vector<std::string>& args);
-int do_chmod(const std::vector<std::string>& args);
-int do_loglevel(const std::vector<std::string>& args);
-int do_load_persist_props(const std::vector<std::string>& args);
-int do_load_all_props(const std::vector<std::string>& args);
-int do_verity_load_state(const std::vector<std::string>& args);
-int do_verity_update_state(const std::vector<std::string>& args);
-int do_wait(const std::vector<std::string>& args);
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
-enum {
- K_UNKNOWN,
-#endif
- KEYWORD(bootchart_init, COMMAND, 0, do_bootchart_init)
- KEYWORD(chmod, COMMAND, 2, do_chmod)
- KEYWORD(chown, COMMAND, 2, do_chown)
- KEYWORD(class, OPTION, 0, 0)
- KEYWORD(class_reset, COMMAND, 1, do_class_reset)
- KEYWORD(class_start, COMMAND, 1, do_class_start)
- KEYWORD(class_stop, COMMAND, 1, do_class_stop)
- KEYWORD(console, OPTION, 0, 0)
- KEYWORD(copy, COMMAND, 2, do_copy)
- KEYWORD(critical, OPTION, 0, 0)
- KEYWORD(disabled, OPTION, 0, 0)
- KEYWORD(domainname, COMMAND, 1, do_domainname)
- KEYWORD(enable, COMMAND, 1, do_enable)
- KEYWORD(exec, COMMAND, 1, do_exec)
- KEYWORD(export, COMMAND, 2, do_export)
- KEYWORD(group, OPTION, 0, 0)
- KEYWORD(hostname, COMMAND, 1, do_hostname)
- KEYWORD(ifup, COMMAND, 1, do_ifup)
- KEYWORD(import, SECTION, 1, 0)
- KEYWORD(insmod, COMMAND, 1, do_insmod)
- KEYWORD(installkey, COMMAND, 1, do_installkey)
- KEYWORD(ioprio, OPTION, 0, 0)
- KEYWORD(keycodes, OPTION, 0, 0)
- KEYWORD(load_all_props, COMMAND, 0, do_load_all_props)
- KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)
- KEYWORD(loglevel, COMMAND, 1, do_loglevel)
- KEYWORD(mkdir, COMMAND, 1, do_mkdir)
- KEYWORD(mount_all, COMMAND, 1, do_mount_all)
- KEYWORD(mount, COMMAND, 3, do_mount)
- KEYWORD(oneshot, OPTION, 0, 0)
- KEYWORD(onrestart, OPTION, 0, 0)
- KEYWORD(on, SECTION, 0, 0)
- KEYWORD(powerctl, COMMAND, 1, do_powerctl)
- KEYWORD(restart, COMMAND, 1, do_restart)
- KEYWORD(restorecon, COMMAND, 1, do_restorecon)
- KEYWORD(restorecon_recursive, COMMAND, 1, do_restorecon_recursive)
- KEYWORD(rm, COMMAND, 1, do_rm)
- KEYWORD(rmdir, COMMAND, 1, do_rmdir)
- KEYWORD(seclabel, OPTION, 0, 0)
- KEYWORD(service, SECTION, 0, 0)
- KEYWORD(setenv, OPTION, 2, 0)
- KEYWORD(setprop, COMMAND, 2, do_setprop)
- KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
- KEYWORD(socket, OPTION, 0, 0)
- KEYWORD(start, COMMAND, 1, do_start)
- KEYWORD(stop, COMMAND, 1, do_stop)
- KEYWORD(swapon_all, COMMAND, 1, do_swapon_all)
- KEYWORD(symlink, COMMAND, 1, do_symlink)
- KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
- KEYWORD(trigger, COMMAND, 1, do_trigger)
- KEYWORD(user, OPTION, 0, 0)
- KEYWORD(verity_load_state, COMMAND, 0, do_verity_load_state)
- KEYWORD(verity_update_state, COMMAND, 0, do_verity_update_state)
- KEYWORD(wait, COMMAND, 1, do_wait)
- KEYWORD(write, COMMAND, 2, do_write)
- KEYWORD(writepid, OPTION, 0, 0)
-#ifdef __MAKE_KEYWORD_ENUM__
- KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
diff --git a/init/log.cpp b/init/log.cpp
index eb5ec42..6b32526 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -16,52 +16,67 @@
#include "log.h"
-#include <stdlib.h>
+#include <fcntl.h>
#include <string.h>
-#include <sys/uio.h>
+#include <linux/audit.h>
+#include <netlink/netlink.h>
#include <selinux/selinux.h>
-#include <base/stringprintf.h>
-
-static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
- static const char* tag = basename(getprogname());
-
- // The kernel's printk buffer is only 1024 bytes.
- // TODO: should we automatically break up long lines into multiple lines?
- // Or we could log but with something like "..." at the end?
- char buf[1024];
- size_t prefix_size = snprintf(buf, sizeof(buf), "<%d>%s: ", level, tag);
- size_t msg_size = vsnprintf(buf + prefix_size, sizeof(buf) - prefix_size, fmt, ap);
- if (msg_size >= sizeof(buf) - prefix_size) {
- msg_size = snprintf(buf + prefix_size, sizeof(buf) - prefix_size,
- "(%zu-byte message too long for printk)\n", msg_size);
+void InitKernelLogging(char* argv[]) {
+ // Make stdin/stdout/stderr all point to /dev/null.
+ int fd = open("/sys/fs/selinux/null", O_RDWR);
+ if (fd == -1) {
+ int saved_errno = errno;
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ errno = saved_errno;
+ PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
}
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2) close(fd);
- iovec iov[1];
- iov[0].iov_base = buf;
- iov[0].iov_len = prefix_size + msg_size;
-
- klog_writev(level, iov, 1);
+ android::base::InitLogging(argv, &android::base::KernelLogger);
}
-void init_klog_write(int level, const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- init_klog_vwrite(level, fmt, ap);
- va_end(ap);
+static void selinux_avc_log(char* buf, size_t buf_len) {
+ size_t str_len = strnlen(buf, buf_len);
+
+ // trim newline at end of string
+ buf[str_len - 1] = '\0';
+
+ struct nl_sock* sk = nl_socket_alloc();
+ if (sk == NULL) {
+ return;
+ }
+ nl_connect(sk, NETLINK_AUDIT);
+ int result;
+ do {
+ result = nl_send_simple(sk, AUDIT_USER_AVC, 0, buf, str_len);
+ } while (result == -NLE_INTR);
+ nl_socket_free(sk);
}
int selinux_klog_callback(int type, const char *fmt, ...) {
- int level = KLOG_ERROR_LEVEL;
+ android::base::LogSeverity severity = android::base::ERROR;
if (type == SELINUX_WARNING) {
- level = KLOG_WARNING_LEVEL;
+ severity = android::base::WARNING;
} else if (type == SELINUX_INFO) {
- level = KLOG_INFO_LEVEL;
+ severity = android::base::INFO;
}
+ char buf[1024];
va_list ap;
va_start(ap, fmt);
- init_klog_vwrite(level, fmt, ap);
+ int res = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
+ if (res <= 0) {
+ return 0;
+ }
+ if (type == SELINUX_AVC) {
+ selinux_avc_log(buf, sizeof(buf));
+ } else {
+ android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+ }
return 0;
}
diff --git a/init/log.h b/init/log.h
index c5c30af..8fa6d74 100644
--- a/init/log.h
+++ b/init/log.h
@@ -17,16 +17,10 @@
#ifndef _INIT_LOG_H_
#define _INIT_LOG_H_
-#include <cutils/klog.h>
+#include <android-base/logging.h>
-#define ERROR(x...) init_klog_write(KLOG_ERROR_LEVEL, x)
-#define WARNING(x...) init_klog_write(KLOG_WARNING_LEVEL, x)
-#define NOTICE(x...) init_klog_write(KLOG_NOTICE_LEVEL, x)
-#define INFO(x...) init_klog_write(KLOG_INFO_LEVEL, x)
-#define DEBUG(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)
-#define VERBOSE(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)
+void InitKernelLogging(char* argv[]);
-void init_klog_write(int level, const char* fmt, ...) __printflike(2, 3);
int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
#endif
diff --git a/init/parser.cpp b/init/parser.cpp
index 8193729..45862b7 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -12,7 +12,7 @@
char buf[128];
int off;
- snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
+ snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
buf[127] = 0;
off = strlen(buf);
@@ -20,7 +20,7 @@
vsnprintf(buf + off, 128 - off, fmt, ap);
va_end(ap);
buf[127] = 0;
- ERROR("%s", buf);
+ LOG(ERROR) << buf;
}
int next_token(struct parse_state *state)
diff --git a/init/parser/tokenizer.h b/init/parser/tokenizer.h
index 8312a08..ade8f73 100644
--- a/init/parser/tokenizer.h
+++ b/init/parser/tokenizer.h
@@ -36,7 +36,7 @@
// a TOK_NEWLINE will not be generated for that line.
class Tokenizer {
public:
- Tokenizer(const std::string& data);
+ explicit Tokenizer(const std::string& data);
~Tokenizer();
enum TokenType { TOK_START, TOK_END, TOK_NEWLINE, TOK_TEXT };
diff --git a/init/perfboot.py b/init/perfboot.py
index 2a17ab6..713290b 100755
--- a/init/perfboot.py
+++ b/init/perfboot.py
@@ -92,7 +92,7 @@
self._interval = interval
self._device = device
self._temp_paths = device.shell(
- ['ls', '/sys/class/thermal/thermal_zone*/temp']).splitlines()
+ ['ls', '/sys/class/thermal/thermal_zone*/temp'])[0].splitlines()
self._product = device.get_prop('ro.build.product')
self._waited = False
@@ -109,7 +109,7 @@
def _get_cpu_temp(self, threshold):
max_temp = 0
for temp_path in self._temp_paths:
- temp = int(self._device.shell(['cat', temp_path]).rstrip())
+ temp = int(self._device.shell(['cat', temp_path])[0].rstrip())
max_temp = max(max_temp, temp)
if temp >= threshold:
return temp
@@ -173,7 +173,7 @@
device.wait()
device.shell(['rm', '-rf', '/system/data/dropbox'])
original_dropbox_max_files = device.shell(
- ['settings', 'get', 'global', 'dropbox_max_files']).rstrip()
+ ['settings', 'get', 'global', 'dropbox_max_files'])[0].rstrip()
device.shell(['settings', 'put', 'global', 'dropbox_max_files', '0'])
return original_dropbox_max_files
@@ -191,9 +191,9 @@
def init_perf(device, output, record_list, tags):
device.wait()
- build_type = device.get_prop('ro.build.type')
+ debuggable = device.get_prop('ro.debuggable')
original_dropbox_max_files = None
- if build_type != 'user':
+ if debuggable == '1':
# Workaround for Dropbox issue (http://b/20890386).
original_dropbox_max_files = disable_dropbox(device)
@@ -244,7 +244,8 @@
"""Drop unknown tags not listed in device's event-log-tags file."""
device.wait()
supported_tags = set()
- for l in device.shell(['cat', '/system/etc/event-log-tags']).splitlines():
+ for l in device.shell(
+ ['cat', '/system/etc/event-log-tags'])[0].splitlines():
tokens = l.split(' ')
if len(tokens) >= 2:
supported_tags.add(tokens[1])
diff --git a/init/property_service.cpp b/init/property_service.cpp
index aa939a5..ce197ee 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -27,6 +27,7 @@
#include <sys/poll.h>
#include <memory>
+#include <vector>
#include <cutils/misc.h>
#include <cutils/sockets.h>
@@ -41,13 +42,14 @@
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/mman.h>
-#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
#include <fs_mgr.h>
-#include <base/file.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
#include "bootimg.h"
#include "property_service.h"
@@ -60,59 +62,43 @@
#define RECOVERY_MOUNT_POINT "/recovery"
static int persistent_properties_loaded = 0;
-static bool property_area_initialized = false;
static int property_set_fd = -1;
-struct workspace {
- size_t size;
- int fd;
-};
-
-static workspace pa_workspace;
-
void property_init() {
- if (property_area_initialized) {
- return;
- }
-
- property_area_initialized = true;
-
if (__system_property_area_init()) {
- return;
- }
-
- pa_workspace.size = 0;
- pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- if (pa_workspace.fd == -1) {
- ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
- return;
+ LOG(ERROR) << "Failed to initialize property area";
+ exit(1);
}
}
-static int check_mac_perms(const char *name, char *sctx)
-{
- char *tctx = NULL;
- int result = 0;
+static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
- if (!sctx)
- goto err;
+ if (!sctx) {
+ return false;
+ }
- if (!sehandle_prop)
- goto err;
+ if (!sehandle_prop) {
+ return false;
+ }
- if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
- goto err;
+ char* tctx = nullptr;
+ if (selabel_lookup(sehandle_prop, &tctx, name.c_str(), 1) != 0) {
+ return false;
+ }
- if (selinux_check_access(sctx, tctx, "property_service", "set", (void*) name) == 0)
- result = 1;
+ property_audit_data audit_data;
+
+ audit_data.name = name.c_str();
+ audit_data.cr = cr;
+
+ bool has_access = (selinux_check_access(sctx, tctx, "property_service", "set", &audit_data) == 0);
freecon(tctx);
- err:
- return result;
+ return has_access;
}
-static int check_control_mac_perms(const char *name, char *sctx)
+static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
{
/*
* Create a name prefix out of ctl.<service name>
@@ -126,19 +112,7 @@
if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
return 0;
- return check_mac_perms(ctl_name, sctx);
-}
-
-/*
- * Checks permissions for setting system properties.
- * Returns 1 if uid allowed, 0 otherwise.
- */
-static int check_perms(const char *name, char *sctx)
-{
- if(!strncmp(name, "ro.", 3))
- name +=3;
-
- return check_mac_perms(name, sctx);
+ return check_mac_perms(ctl_name, sctx, cr);
}
std::string property_get(const char* name) {
@@ -156,7 +130,7 @@
snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
fd = mkstemp(tempPath);
if (fd < 0) {
- ERROR("Unable to write persistent property to temp file %s: %s\n", tempPath, strerror(errno));
+ PLOG(ERROR) << "Unable to write persistent property to temp file " << tempPath;
return;
}
write(fd, value, strlen(value));
@@ -165,28 +139,27 @@
snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
if (rename(tempPath, path)) {
+ PLOG(ERROR) << "Unable to rename persistent property file " << tempPath << " to " << path;
unlink(tempPath);
- ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path);
}
}
-static bool is_legal_property_name(const char* name, size_t namelen)
-{
- size_t i;
- if (namelen >= PROP_NAME_MAX) return false;
+bool is_legal_property_name(const std::string& name) {
+ size_t namelen = name.size();
+
if (namelen < 1) return false;
if (name[0] == '.') return false;
if (name[namelen - 1] == '.') return false;
- /* Only allow alphanumeric, plus '.', '-', or '_' */
+ /* Only allow alphanumeric, plus '.', '-', '@', or '_' */
/* Don't allow ".." to appear in a property name */
- for (i = 0; i < namelen; i++) {
+ for (size_t i = 0; i < namelen; i++) {
if (name[i] == '.') {
// i=0 is guaranteed to never have a dot. See above.
if (name[i-1] == '.') return false;
continue;
}
- if (name[i] == '_' || name[i] == '-') continue;
+ if (name[i] == '_' || name[i] == '-' || name[i] == '@') continue;
if (name[i] >= 'a' && name[i] <= 'z') continue;
if (name[i] >= 'A' && name[i] <= 'Z') continue;
if (name[i] >= '0' && name[i] <= '9') continue;
@@ -196,158 +169,280 @@
return true;
}
-static int property_set_impl(const char* name, const char* value) {
- size_t namelen = strlen(name);
- size_t valuelen = strlen(value);
+uint32_t property_set(const std::string& name, const std::string& value) {
+ size_t valuelen = value.size();
- if (!is_legal_property_name(name, namelen)) return -1;
- if (valuelen >= PROP_VALUE_MAX) return -1;
+ if (!is_legal_property_name(name)) {
+ LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
+ return PROP_ERROR_INVALID_NAME;
+ }
- prop_info* pi = (prop_info*) __system_property_find(name);
+ if (valuelen >= PROP_VALUE_MAX) {
+ LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
+ << "value too long";
+ return PROP_ERROR_INVALID_VALUE;
+ }
- if(pi != 0) {
- /* ro.* properties may NEVER be modified once set */
- if(!strncmp(name, "ro.", 3)) return -1;
+ if (name == "selinux.restorecon_recursive" && valuelen > 0) {
+ if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ LOG(ERROR) << "Failed to restorecon_recursive " << value;
+ }
+ }
- __system_property_update(pi, value, valuelen);
+ prop_info* pi = (prop_info*) __system_property_find(name.c_str());
+ if (pi != nullptr) {
+ // ro.* properties are actually "write-once".
+ if (android::base::StartsWith(name, "ro.")) {
+ LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
+ << "property already set";
+ return PROP_ERROR_READ_ONLY_PROPERTY;
+ }
+
+ __system_property_update(pi, value.c_str(), valuelen);
} else {
- int rc = __system_property_add(name, namelen, value, valuelen);
+ int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
- return rc;
+ LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
+ << "__system_property_add failed";
+ return PROP_ERROR_SET_FAILED;
}
}
- /* If name starts with "net." treat as a DNS property. */
- if (strncmp("net.", name, strlen("net.")) == 0) {
- if (strcmp("net.change", name) == 0) {
- return 0;
- }
- /*
- * The 'net.change' property is a special property used track when any
- * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
- * contains the last updated 'net.*' property.
- */
- property_set("net.change", name);
- } else if (persistent_properties_loaded &&
- strncmp("persist.", name, strlen("persist.")) == 0) {
- /*
- * Don't write properties to disk until after we have read all default properties
- * to prevent them from being overwritten by default values.
- */
- write_persistent_property(name, value);
- } else if (strcmp("selinux.reload_policy", name) == 0 &&
- strcmp("1", value) == 0) {
- selinux_reload_policy();
+
+ // Don't write properties to disk until after we have read all default
+ // properties to prevent them from being overwritten by default values.
+ if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
+ write_persistent_property(name.c_str(), value.c_str());
}
- property_changed(name, value);
- return 0;
+ property_changed(name.c_str(), value.c_str());
+ return PROP_SUCCESS;
}
-int property_set(const char* name, const char* value) {
- int rc = property_set_impl(name, value);
- if (rc == -1) {
- ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
- }
- return rc;
-}
+class SocketConnection {
+ public:
+ SocketConnection(int socket, const struct ucred& cred)
+ : socket_(socket), cred_(cred) {}
-static void handle_property_set_fd()
-{
- prop_msg msg;
- int s;
- int r;
- struct ucred cr;
- struct sockaddr_un addr;
- socklen_t addr_size = sizeof(addr);
- socklen_t cr_size = sizeof(cr);
- char * source_ctx = NULL;
+ ~SocketConnection() {
+ close(socket_);
+ }
+
+ bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
+ return RecvFully(value, sizeof(*value), timeout_ms);
+ }
+
+ bool RecvChars(char* chars, size_t size, uint32_t* timeout_ms) {
+ return RecvFully(chars, size, timeout_ms);
+ }
+
+ bool RecvString(std::string* value, uint32_t* timeout_ms) {
+ uint32_t len = 0;
+ if (!RecvUint32(&len, timeout_ms)) {
+ return false;
+ }
+
+ if (len == 0) {
+ *value = "";
+ return true;
+ }
+
+ std::vector<char> chars(len);
+ if (!RecvChars(&chars[0], len, timeout_ms)) {
+ return false;
+ }
+
+ *value = std::string(&chars[0], len);
+ return true;
+ }
+
+ bool SendUint32(uint32_t value) {
+ int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
+ return result == sizeof(value);
+ }
+
+ int socket() {
+ return socket_;
+ }
+
+ const struct ucred& cred() {
+ return cred_;
+ }
+
+ private:
+ bool PollIn(uint32_t* timeout_ms) {
struct pollfd ufds[1];
- const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
- int nr;
+ ufds[0].fd = socket_;
+ ufds[0].events = POLLIN;
+ ufds[0].revents = 0;
+ while (*timeout_ms > 0) {
+ Timer timer;
+ int nr = poll(ufds, 1, *timeout_ms);
+ uint64_t millis = timer.duration_ms();
+ *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
- if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
+ if (nr > 0) {
+ return true;
+ }
+
+ if (nr == 0) {
+ // Timeout
+ break;
+ }
+
+ if (nr < 0 && errno != EINTR) {
+ PLOG(ERROR) << "sys_prop: error waiting for uid " << cred_.uid << " to send property message";
+ return false;
+ } else { // errno == EINTR
+ // Timer rounds milliseconds down in case of EINTR we want it to be rounded up
+ // to avoid slowing init down by causing EINTR with under millisecond timeout.
+ if (*timeout_ms > 0) {
+ --(*timeout_ms);
+ }
+ }
+ }
+
+ LOG(ERROR) << "sys_prop: timeout waiting for uid " << cred_.uid << " to send property message.";
+ return false;
+ }
+
+ bool RecvFully(void* data_ptr, size_t size, uint32_t* timeout_ms) {
+ size_t bytes_left = size;
+ char* data = static_cast<char*>(data_ptr);
+ while (*timeout_ms > 0 && bytes_left > 0) {
+ if (!PollIn(timeout_ms)) {
+ return false;
+ }
+
+ int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
+ if (result <= 0) {
+ return false;
+ }
+
+ bytes_left -= result;
+ data += result;
+ }
+
+ return bytes_left == 0;
+ }
+
+ int socket_;
+ struct ucred cred_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
+};
+
+static void handle_property_set(SocketConnection& socket,
+ const std::string& name,
+ const std::string& value,
+ bool legacy_protocol) {
+ const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
+ if (!is_legal_property_name(name)) {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\"";
+ socket.SendUint32(PROP_ERROR_INVALID_NAME);
+ return;
+ }
+
+ struct ucred cr = socket.cred();
+ char* source_ctx = nullptr;
+ getpeercon(socket.socket(), &source_ctx);
+
+ if (android::base::StartsWith(name, "ctl.")) {
+ if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
+ handle_control_message(name.c_str() + 4, value.c_str());
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_SUCCESS);
+ }
+ } else {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
+ << " service ctl [" << value << "]"
+ << " uid:" << cr.uid
+ << " gid:" << cr.gid
+ << " pid:" << cr.pid;
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
+ }
+ }
+ } else {
+ if (check_mac_perms(name, source_ctx, &cr)) {
+ uint32_t result = property_set(name, value);
+ if (!legacy_protocol) {
+ socket.SendUint32(result);
+ }
+ } else {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+ }
+ }
+ }
+
+ freecon(source_ctx);
+}
+
+static void handle_property_set_fd() {
+ static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
+
+ int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
+ if (s == -1) {
return;
}
/* Check socket options here */
+ struct ucred cr;
+ socklen_t cr_size = sizeof(cr);
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
close(s);
- ERROR("Unable to receive socket options\n");
+ PLOG(ERROR) << "Unable to receive socket options";
return;
}
- ufds[0].fd = s;
- ufds[0].events = POLLIN;
- ufds[0].revents = 0;
- nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
- if (nr == 0) {
- ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
- close(s);
- return;
- } else if (nr < 0) {
- ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
- close(s);
+ SocketConnection socket(s, cr);
+ uint32_t timeout_ms = kDefaultSocketTimeout;
+
+ uint32_t cmd = 0;
+
+ if (!socket.RecvUint32(&cmd, &timeout_ms)) {
+ PLOG(ERROR) << "sys_prop: error while reading command from the socket";
+ socket.SendUint32(PROP_ERROR_READ_CMD);
return;
}
- r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
- if(r != sizeof(prop_msg)) {
- ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
- r, sizeof(prop_msg), strerror(errno));
- close(s);
- return;
- }
+ switch(cmd) {
+ case PROP_MSG_SETPROP: {
+ char prop_name[PROP_NAME_MAX];
+ char prop_value[PROP_VALUE_MAX];
- switch(msg.cmd) {
- case PROP_MSG_SETPROP:
- msg.name[PROP_NAME_MAX-1] = 0;
- msg.value[PROP_VALUE_MAX-1] = 0;
-
- if (!is_legal_property_name(msg.name, strlen(msg.name))) {
- ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
- close(s);
- return;
+ if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
+ !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
+ PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
+ return;
}
- getpeercon(s, &source_ctx);
+ prop_name[PROP_NAME_MAX-1] = 0;
+ prop_value[PROP_VALUE_MAX-1] = 0;
- if(memcmp(msg.name,"ctl.",4) == 0) {
- // Keep the old close-socket-early behavior when handling
- // ctl.* properties.
- close(s);
- if (check_control_mac_perms(msg.value, source_ctx)) {
- handle_control_message((char*) msg.name + 4, (char*) msg.value);
- } else {
- ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
- msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
- }
- } else {
- if (check_perms(msg.name, source_ctx)) {
- property_set((char*) msg.name, (char*) msg.value);
- } else {
- ERROR("sys_prop: permission denied uid:%d name:%s\n",
- cr.uid, msg.name);
- }
-
- // Note: bionic's property client code assumes that the
- // property server will not close the socket until *AFTER*
- // the property is written to memory.
- close(s);
- }
- freecon(source_ctx);
+ handle_property_set(socket, prop_value, prop_value, true);
break;
+ }
+ case PROP_MSG_SETPROP2: {
+ std::string name;
+ std::string value;
+ if (!socket.RecvString(&name, &timeout_ms) ||
+ !socket.RecvString(&value, &timeout_ms)) {
+ PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
+ socket.SendUint32(PROP_ERROR_READ_DATA);
+ return;
+ }
+
+ handle_property_set(socket, name, value, false);
+ break;
+ }
default:
- close(s);
+ socket.SendUint32(PROP_ERROR_INVALID_CMD);
break;
}
}
-void get_property_workspace(int *fd, int *sz)
-{
- *fd = pa_workspace.fd;
- *sz = pa_workspace.size;
-}
-
static void load_properties_from_file(const char *, const char *);
/*
@@ -410,18 +505,18 @@
}
}
-/*
- * Filter is used to decide which properties to load: NULL loads all keys,
- * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
- */
+// Filter is used to decide which properties to load: NULL loads all keys,
+// "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
static void load_properties_from_file(const char* filename, const char* filter) {
Timer t;
std::string data;
- if (read_file(filename, &data)) {
- data.push_back('\n');
- load_properties(&data[0], filter);
+ if (!read_file(filename, &data)) {
+ PLOG(WARNING) << "Couldn't load properties from " << filename;
+ return;
}
- NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
+ data.push_back('\n');
+ load_properties(&data[0], filter);
+ LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
}
static void load_persistent_properties() {
@@ -429,8 +524,8 @@
std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
if (!dir) {
- ERROR("Unable to open persistent property directory \"%s\": %s\n",
- PERSISTENT_PROPERTY_DIR, strerror(errno));
+ PLOG(ERROR) << "Unable to open persistent property directory \""
+ << PERSISTENT_PROPERTY_DIR << "\"";
return;
}
@@ -446,25 +541,23 @@
// Open the file and read the property value.
int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
- ERROR("Unable to open persistent property file \"%s\": %s\n",
- entry->d_name, strerror(errno));
+ PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
continue;
}
struct stat sb;
if (fstat(fd, &sb) == -1) {
- ERROR("fstat on property file \"%s\" failed: %s\n", entry->d_name, strerror(errno));
+ PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
close(fd);
continue;
}
// File must not be accessible to others, be owned by root/root, and
// not be a hard link to any other file.
- if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || (sb.st_uid != 0) || (sb.st_gid != 0) ||
- (sb.st_nlink != 1)) {
- ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%u mode=%o)\n",
- entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid,
- (unsigned int)sb.st_nlink, sb.st_mode);
+ if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 || sb.st_nlink != 1) {
+ PLOG(ERROR) << "skipping insecure property file " << entry->d_name
+ << " (uid=" << sb.st_uid << " gid=" << sb.st_gid
+ << " nlink=" << sb.st_nlink << " mode=" << std::oct << sb.st_mode << ")";
close(fd);
continue;
}
@@ -475,26 +568,23 @@
value[length] = 0;
property_set(entry->d_name, value);
} else {
- ERROR("Unable to read persistent property file %s: %s\n",
- entry->d_name, strerror(errno));
+ PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
}
close(fd);
}
}
void property_load_boot_defaults() {
- load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
-}
-
-bool properties_initialized() {
- return property_area_initialized;
+ load_properties_from_file("/default.prop", NULL);
+ load_properties_from_file("/odm/default.prop", NULL);
+ load_properties_from_file("/vendor/default.prop", NULL);
}
static void load_override_properties() {
if (ALLOW_LOCAL_PROP_OVERRIDE) {
std::string debuggable = property_get("ro.debuggable");
if (debuggable == "1") {
- load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
+ load_properties_from_file("/data/local.prop", NULL);
}
}
}
@@ -513,27 +603,27 @@
void load_recovery_id_prop() {
std::string ro_hardware = property_get("ro.hardware");
if (ro_hardware.empty()) {
- ERROR("ro.hardware not set - unable to load recovery id\n");
+ LOG(ERROR) << "ro.hardware not set - unable to load recovery id";
return;
}
std::string fstab_filename = FSTAB_PREFIX + ro_hardware;
std::unique_ptr<fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_filename.c_str()),
- fs_mgr_free_fstab);
+ fs_mgr_free_fstab);
if (!tab) {
- ERROR("unable to read fstab %s: %s\n", fstab_filename.c_str(), strerror(errno));
+ PLOG(ERROR) << "unable to read fstab " << fstab_filename;
return;
}
fstab_rec* rec = fs_mgr_get_entry_for_mount_point(tab.get(), RECOVERY_MOUNT_POINT);
if (rec == NULL) {
- ERROR("/recovery not specified in fstab\n");
+ LOG(ERROR) << "/recovery not specified in fstab";
return;
}
int fd = open(rec->blk_device, O_RDONLY);
if (fd == -1) {
- ERROR("error opening block device %s: %s\n", rec->blk_device, strerror(errno));
+ PLOG(ERROR) << "error opening block device " << rec->blk_device;
return;
}
@@ -542,30 +632,27 @@
std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
property_set("ro.recovery_id", hex.c_str());
} else {
- ERROR("error reading /recovery: %s\n", strerror(errno));
+ PLOG(ERROR) << "error reading /recovery";
}
close(fd);
}
-void load_all_props() {
- load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
- load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
- load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
-
- load_override_properties();
-
- /* Read persistent properties after all default values have been loaded. */
- load_persistent_properties();
-
+void load_system_props() {
+ load_properties_from_file("/system/build.prop", NULL);
+ load_properties_from_file("/odm/build.prop", NULL);
+ load_properties_from_file("/vendor/build.prop", NULL);
+ load_properties_from_file("/factory/factory.prop", "ro.*");
load_recovery_id_prop();
}
void start_property_service() {
+ property_set("ro.property_service.version", "2");
+
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);
if (property_set_fd == -1) {
- ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
}
diff --git a/init/property_service.h b/init/property_service.h
index 51d7404..5d59473 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,18 +18,23 @@
#define _INIT_PROPERTY_H
#include <stddef.h>
+#include <sys/socket.h>
#include <sys/system_properties.h>
#include <string>
-extern void property_init(void);
-extern void property_load_boot_defaults(void);
-extern void load_persist_props(void);
-extern void load_all_props(void);
-extern void start_property_service(void);
-void get_property_workspace(int *fd, int *sz);
+struct property_audit_data {
+ ucred *cr;
+ const char* name;
+};
+
+void property_init(void);
+void property_load_boot_defaults(void);
+void load_persist_props(void);
+void load_system_props(void);
+void start_property_service(void);
std::string property_get(const char* name);
-extern int property_set(const char *name, const char *value);
-extern bool properties_initialized();
+uint32_t property_set(const std::string& name, const std::string& value);
+bool is_legal_property_name(const std::string& name);
-#endif /* _INIT_PROPERTY_H */
+#endif /* _INIT_PROPERTY_H */
diff --git a/init/readme.txt b/init/readme.txt
deleted file mode 100644
index d70c6f3..0000000
--- a/init/readme.txt
+++ /dev/null
@@ -1,426 +0,0 @@
-
-Android Init Language
----------------------
-
-The Android Init Language consists of four broad classes of statements,
-which are Actions, Commands, Services, and Options.
-
-All of these are line-oriented, consisting of tokens separated by
-whitespace. The c-style backslash escapes may be used to insert
-whitespace into a token. Double quotes may also be used to prevent
-whitespace from breaking text into multiple tokens. The backslash,
-when it is the last character on a line, may be used for line-folding.
-
-Lines which start with a # (leading whitespace allowed) are comments.
-
-Actions and Services implicitly declare a new section. All commands
-or options belong to the section most recently declared. Commands
-or options before the first section are ignored.
-
-Actions and Services have unique names. If a second Action or Service
-is declared with the same name as an existing one, it is ignored as
-an error. (??? should we override instead)
-
-
-Actions
--------
-Actions are named sequences of commands. Actions have a trigger which
-is used to determine when the action should occur. When an event
-occurs which matches an action's trigger, that action is added to
-the tail of a to-be-executed queue (unless it is already on the
-queue).
-
-Each action in the queue is dequeued in sequence and each command in
-that action is executed in sequence. Init handles other activities
-(device creation/destruction, property setting, process restarting)
-"between" the execution of the commands in activities.
-
-Actions take the form of:
-
-on <trigger>
- <command>
- <command>
- <command>
-
-
-Services
---------
-Services are programs which init launches and (optionally) restarts
-when they exit. Services take the form of:
-
-service <name> <pathname> [ <argument> ]*
- <option>
- <option>
- ...
-
-
-Options
--------
-Options are modifiers to services. They affect how and when init
-runs the service.
-
-critical
- This is a device-critical service. If it exits more than four times in
- four minutes, the device will reboot into recovery mode.
-
-disabled
- This service will not automatically start with its class.
- It must be explicitly started by name.
-
-setenv <name> <value>
- Set the environment variable <name> to <value> in the launched process.
-
-socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
- Create a unix domain socket named /dev/socket/<name> and pass
- its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket".
- User and group default to 0.
- 'seclabel' is the SELinux security context for the socket.
- It defaults to the service security context, as specified by seclabel or
- computed based on the service executable file security context.
-
-user <username>
- Change to username before exec'ing this service.
- Currently defaults to root. (??? probably should default to nobody)
- Currently, if your process requires linux capabilities then you cannot use
- this command. You must instead request the capabilities in-process while
- still root, and then drop to your desired uid.
-
-group <groupname> [ <groupname> ]*
- Change to groupname before exec'ing this service. Additional
- groupnames beyond the (required) first one are used to set the
- supplemental groups of the process (via setgroups()).
- Currently defaults to root. (??? probably should default to nobody)
-
-seclabel <seclabel>
- Change to 'seclabel' before exec'ing this service.
- Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
- Services on the system partition can instead use policy-defined transitions
- based on their file security context.
- If not specified and no transition is defined in policy, defaults to the init context.
-
-oneshot
- Do not restart the service when it exits.
-
-class <name>
- Specify a class name for the service. All services in a
- named class may be started or stopped together. A service
- is in the class "default" if one is not specified via the
- class option.
-
-onrestart
- Execute a Command (see below) when service restarts.
-
-writepid <file...>
- Write the child's pid to the given files when it forks. Meant for
- cgroup/cpuset usage.
-
-
-Triggers
---------
-Triggers are strings which can be used to match certain kinds
-of events and used to cause an action to occur.
-
-boot
- This is the first trigger that will occur when init starts
- (after /init.conf is loaded)
-
-<name>=<value>
- Triggers of this form occur when the property <name> is set
- to the specific value <value>.
-
- One can also test multiple properties to execute a group
- of commands. For example:
-
- on property:test.a=1 && property:test.b=1
- setprop test.c 1
-
- The above stub sets test.c to 1 only when
- both test.a=1 and test.b=1
-
-
-Commands
---------
-
-bootchart_init
- Start bootcharting if configured (see below).
- This is included in the default init.rc.
-
-chmod <octal-mode> <path>
- Change file access permissions.
-
-chown <owner> <group> <path>
- Change file owner and group.
-
-class_start <serviceclass>
- Start all services of the specified class if they are
- not already running.
-
-class_stop <serviceclass>
- Stop and disable all services of the specified class if they are
- currently running.
-
-class_reset <serviceclass>
- Stop all services of the specified class if they are
- currently running, without disabling them. They can be restarted
- later using class_start.
-
-copy <src> <dst>
- Copies a file. Similar to write, but useful for binary/large
- amounts of data.
-
-domainname <name>
- Set the domain name.
-
-enable <servicename>
- Turns a disabled service into an enabled one as if the service did not
- specify disabled.
- If the service is supposed to be running, it will be started now.
- Typically used when the bootloader sets a variable that indicates a specific
- service should be started when needed. E.g.
- on property:ro.boot.myfancyhardware=1
- enable my_fancy_service_for_my_fancy_hardware
-
-exec [ <seclabel> [ <user> [ <group> ]* ] ] -- <command> [ <argument> ]*
- Fork and execute command with the given arguments. The command starts
- after "--" so that an optional security context, user, and supplementary
- groups can be provided. No other commands will be run until this one
- finishes. <seclabel> can be a - to denote default.
-
-export <name> <value>
- Set the environment variable <name> equal to <value> in the
- global environment (which will be inherited by all processes
- started after this command is executed)
-
-hostname <name>
- Set the host name.
-
-ifup <interface>
- Bring the network interface <interface> online.
-
-import <path>
- Parse an init config file, extending the current configuration.
- If <path> is a directory, each file in the directory is parsed as
- a config file. It is not recursive, nested directories will
- not be parsed.
-
-insmod <path>
- Install the module at <path>
-
-load_all_props
- Loads properties from /system, /vendor, et cetera.
- This is included in the default init.rc.
-
-load_persist_props
- Loads persistent properties when /data has been decrypted.
- This is included in the default init.rc.
-
-loglevel <level>
- Sets the kernel log level to level. Properties are expanded within <level>.
-
-mkdir <path> [mode] [owner] [group]
- Create a directory at <path>, optionally with the given mode, owner, and
- group. If not provided, the directory is created with permissions 755 and
- owned by the root user and root group. If provided, the mode, owner and group
- will be updated if the directory exists already.
-
-mount_all <fstab>
- Calls fs_mgr_mount_all on the given fs_mgr-format fstab.
-
-mount <type> <device> <dir> [ <flag> ]* [<options>]
- Attempt to mount the named device at the directory <dir>
- <device> may be of the form mtd@name to specify a mtd block
- device by name.
- <flag>s include "ro", "rw", "remount", "noatime", ...
- <options> include "barrier=1", "noauto_da_alloc", "discard", ... as
- a comma separated string, eg: barrier=1,noauto_da_alloc
-
-powerctl
- Internal implementation detail used to respond to changes to the
- "sys.powerctl" system property, used to implement rebooting.
-
-restart <service>
- Like stop, but doesn't disable the service.
-
-restorecon <path> [ <path> ]*
- Restore the file named by <path> to the security context specified
- in the file_contexts configuration.
- Not required for directories created by the init.rc as these are
- automatically labeled correctly by init.
-
-restorecon_recursive <path> [ <path> ]*
- Recursively restore the directory tree named by <path> to the
- security contexts specified in the file_contexts configuration.
-
-rm <path>
- Calls unlink(2) on the given path. You might want to
- use "exec -- rm ..." instead (provided the system partition is
- already mounted).
-
-rmdir <path>
- Calls rmdir(2) on the given path.
-
-setprop <name> <value>
- Set system property <name> to <value>. Properties are expanded
- within <value>.
-
-setrlimit <resource> <cur> <max>
- Set the rlimit for a resource.
-
-start <service>
- Start a service running if it is not already running.
-
-stop <service>
- Stop a service from running if it is currently running.
-
-swapon_all <fstab>
- Calls fs_mgr_swapon_all on the given fstab file.
-
-symlink <target> <path>
- Create a symbolic link at <path> with the value <target>
-
-sysclktz <mins_west_of_gmt>
- Set the system clock base (0 if system clock ticks in GMT)
-
-trigger <event>
- Trigger an event. Used to queue an action from another
- action.
-
-verity_load_state
- Internal implementation detail used to load dm-verity state.
-
-verity_update_state <mount_point>
- Internal implementation detail used to update dm-verity state and
- set the partition.<mount_point>.verified properties used by adb remount
- because fs_mgr can't set them directly itself.
-
-wait <path> [ <timeout> ]
- Poll for the existence of the given file and return when found,
- or the timeout has been reached. If timeout is not specified it
- currently defaults to five seconds.
-
-write <path> <content>
- Open the file at <path> and write a string to it with write(2).
- If the file does not exist, it will be created. If it does exist,
- it will be truncated. Properties are expanded within <content>.
-
-
-Properties
-----------
-Init updates some system properties to provide some insight into
-what it's doing:
-
-init.action
- Equal to the name of the action currently being executed or "" if none
-
-init.command
- Equal to the command being executed or "" if none.
-
-init.svc.<name>
- State of a named service ("stopped", "running", "restarting")
-
-
-Bootcharting
-------------
-This version of init contains code to perform "bootcharting": generating log
-files that can be later processed by the tools provided by www.bootchart.org.
-
-On the emulator, use the -bootchart <timeout> option to boot with bootcharting
-activated for <timeout> seconds.
-
-On a device, create /data/bootchart/start with a command like the following:
-
- adb shell 'echo $TIMEOUT > /data/bootchart/start'
-
-Where the value of $TIMEOUT corresponds to the desired bootcharted period in
-seconds. Bootcharting will stop after that many seconds have elapsed.
-You can also stop the bootcharting at any moment by doing the following:
-
- adb shell 'echo 1 > /data/bootchart/stop'
-
-Note that /data/bootchart/stop is deleted automatically by init at the end of
-the bootcharting. This is not the case with /data/bootchart/start, so don't
-forget to delete it when you're done collecting data.
-
-The log files are written to /data/bootchart/. A script is provided to
-retrieve them and create a bootchart.tgz file that can be used with the
-bootchart command-line utility:
-
- sudo apt-get install pybootchartgui
- # grab-bootchart.sh uses $ANDROID_SERIAL.
- $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
-
-One thing to watch for is that the bootchart will show init as if it started
-running at 0s. You'll have to look at dmesg to work out when the kernel
-actually started init.
-
-
-Comparing two bootcharts
-------------------------
-A handy script named compare-bootcharts.py can be used to compare the
-start/end time of selected processes. The aforementioned grab-bootchart.sh
-will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
-If two such barballs are preserved on the host machine under different
-directories, the script can list the timestamps differences. For example:
-
-Usage: system/core/init/compare-bootcharts.py base_bootchart_dir
- exp_bootchart_dir
-
-process: baseline experiment (delta)
- - Unit is ms (a jiffy is 10 ms on the system)
-------------------------------------
-/init: 50 40 (-10)
-/system/bin/surfaceflinger: 4320 4470 (+150)
-/system/bin/bootanimation: 6980 6990 (+10)
-zygote64: 10410 10640 (+230)
-zygote: 10410 10640 (+230)
-system_server: 15350 15150 (-200)
-bootanimation ends at: 33790 31230 (-2560)
-
-
-Systrace
---------
-Systrace [1] can be used for obtaining performance analysis reports during boot
-time on userdebug or eng builds.
-Here is an example of trace events of "wm" and "am" categories:
-
- $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py wm am --boot
-
-This command will cause the device to reboot. After the device is rebooted and
-the boot sequence has finished, the trace report is obtained from the device
-and written as trace.html on the host by hitting Ctrl+C.
-
-LIMITATION
-Recording trace events is started after persistent properties are loaded, so
-the trace events that are emitted before that are not recorded. Several
-services such as vold, surfaceflinger, and servicemanager are affected by this
-limitation since they are started before persistent properties are loaded.
-Zygote initialization and the processes that are forked from the zygote are not
-affected.
-
-[1] http://developer.android.com/tools/help/systrace.html
-
-
-Debugging init
---------------
-By default, programs executed by init will drop stdout and stderr into
-/dev/null. To help with debugging, you can execute your program via the
-Android program logwrapper. This will redirect stdout/stderr into the
-Android logging system (accessed via logcat).
-
-For example
-service akmd /system/bin/logwrapper /sbin/akmd
-
-For quicker turnaround when working on init itself, use:
-
- mm -j
- m ramdisk-nodeps
- m bootimage-nodeps
- adb reboot bootloader
- fastboot boot $ANDROID_PRODUCT_OUT/boot.img
-
-Alternatively, use the emulator:
-
- emulator -partition-size 1024 -verbose -show-kernel -no-window
-
-You might want to call klog_set_level(6) after the klog_init() call
-so you see the kernel logging in dmesg (or the emulator output).
diff --git a/init/service.cpp b/init/service.cpp
index a370d25..0f7f62f 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -17,35 +17,128 @@
#include "service.h"
#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/securebits.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include <selinux/selinux.h>
-#include <base/file.h>
-#include <base/stringprintf.h>
-#include <cutils/android_reboot.h>
-#include <cutils/sockets.h>
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <system/thread_defs.h>
+
+#include <processgroup/processgroup.h>
#include "action.h"
#include "init.h"
#include "init_parser.h"
-#include "keywords.h"
#include "log.h"
#include "property_service.h"
#include "util.h"
-#define CRITICAL_CRASH_THRESHOLD 4 // if we crash >4 times ...
-#define CRITICAL_CRASH_WINDOW (4*60) // ... in 4 minutes, goto recovery
+using android::base::ParseInt;
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
-SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) {
+static std::string ComputeContextFromExecutable(std::string& service_name,
+ const std::string& service_path) {
+ std::string computed_context;
+
+ char* raw_con = nullptr;
+ char* raw_filecon = nullptr;
+
+ if (getcon(&raw_con) == -1) {
+ LOG(ERROR) << "could not get context while starting '" << service_name << "'";
+ return "";
+ }
+ std::unique_ptr<char> mycon(raw_con);
+
+ if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
+ LOG(ERROR) << "could not get file context while starting '" << service_name << "'";
+ return "";
+ }
+ std::unique_ptr<char> filecon(raw_filecon);
+
+ char* new_con = nullptr;
+ int rc = security_compute_create(mycon.get(), filecon.get(),
+ string_to_security_class("process"), &new_con);
+ if (rc == 0) {
+ computed_context = new_con;
+ free(new_con);
+ }
+ if (rc == 0 && computed_context == mycon.get()) {
+ LOG(ERROR) << "service " << service_name << " does not have a SELinux domain defined";
+ return "";
+ }
+ if (rc < 0) {
+ LOG(ERROR) << "could not get context while starting '" << service_name << "'";
+ return "";
+ }
+ return computed_context;
}
-SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& socketcon)
- : name(name), type(type), uid(uid), gid(gid), perm(perm), socketcon(socketcon) {
+static void SetUpPidNamespace(const std::string& service_name) {
+ 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.
+ if (mount("", "/proc", "proc", kSafeFlags | MS_REMOUNT, "") == -1) {
+ PLOG(FATAL) << "couldn't remount(/proc) for " << service_name;
+ }
+
+ if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
+ PLOG(FATAL) << "couldn't set name for " << service_name;
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name;
+ }
+
+ if (child_pid > 0) {
+ // So that we exit with the right status.
+ static int init_exitstatus = 0;
+ signal(SIGTERM, [](int) { _exit(init_exitstatus); });
+
+ pid_t waited_pid;
+ int status;
+ while ((waited_pid = wait(&status)) > 0) {
+ // This loop will end when there are no processes left inside the
+ // PID namespace or when the init process inside the PID namespace
+ // gets a signal.
+ if (waited_pid == child_pid) {
+ init_exitstatus = status;
+ }
+ }
+ if (!WIFEXITED(init_exitstatus)) {
+ _exit(EXIT_FAILURE);
+ }
+ _exit(WEXITSTATUS(init_exitstatus));
+ }
+}
+
+static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>* strs) {
+ std::vector<std::string> expanded_args;
+ expanded_args.resize(args.size());
+ strs->push_back(const_cast<char*>(args[0].c_str()));
+ for (std::size_t i = 1; i < args.size(); ++i) {
+ if (!expand_props(args[i], &expanded_args[i])) {
+ LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
+ }
+ strs->push_back(const_cast<char*>(expanded_args[i].c_str()));
+ }
+ strs->push_back(nullptr);
}
ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
@@ -58,59 +151,114 @@
Service::Service(const std::string& name, const std::string& classname,
const std::vector<std::string>& args)
- : name_(name), classname_(classname), flags_(0), pid_(0), time_started_(0),
- time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), seclabel_(""),
- ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+ : name_(name), classname_(classname), flags_(0), pid_(0),
+ crash_count_(0), uid_(0), gid_(0), namespace_flags_(0),
+ seclabel_(""), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0),
+ priority_(0), oom_score_adjust_(-1000), args_(args) {
onrestart_.InitSingleTrigger("onrestart");
}
Service::Service(const std::string& name, const std::string& classname,
- unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
- const std::string& seclabel, const std::vector<std::string>& args)
- : name_(name), classname_(classname), flags_(flags), pid_(0), time_started_(0),
- time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid), supp_gids_(supp_gids),
- seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+ unsigned flags, uid_t uid, gid_t gid,
+ const std::vector<gid_t>& supp_gids,
+ const CapSet& capabilities, unsigned namespace_flags,
+ const std::string& seclabel,
+ const std::vector<std::string>& args)
+ : name_(name), classname_(classname), flags_(flags), pid_(0),
+ crash_count_(0), uid_(uid), gid_(gid),
+ supp_gids_(supp_gids), capabilities_(capabilities),
+ namespace_flags_(namespace_flags), seclabel_(seclabel),
+ ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), priority_(0),
+ oom_score_adjust_(-1000), args_(args) {
onrestart_.InitSingleTrigger("onrestart");
}
void Service::NotifyStateChange(const std::string& new_state) const {
- if (!properties_initialized()) {
- // If properties aren't available yet, we can't set them.
- return;
- }
-
if ((flags_ & SVC_EXEC) != 0) {
// 'exec' commands don't have properties tracking their state.
return;
}
- std::string prop_name = android::base::StringPrintf("init.svc.%s", name_.c_str());
+ std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
if (prop_name.length() >= PROP_NAME_MAX) {
// If the property name would be too long, we can't set it.
- ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n",
- name_.c_str(), new_state.c_str());
+ LOG(ERROR) << "Property name \"init.svc." << name_ << "\" too long; not setting to " << new_state;
return;
}
property_set(prop_name.c_str(), new_state.c_str());
+
+ if (new_state == "running") {
+ uint64_t start_ns = time_started_.time_since_epoch().count();
+ property_set(StringPrintf("ro.boottime.%s", name_.c_str()).c_str(),
+ StringPrintf("%" PRIu64, start_ns).c_str());
+ }
+}
+
+void Service::KillProcessGroup(int signal) {
+ LOG(INFO) << "Sending signal " << signal
+ << " to service '" << name_
+ << "' (pid " << pid_ << ") process group...";
+ if (killProcessGroup(uid_, pid_, signal) == -1) {
+ PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
+ }
+ if (kill(-pid_, signal) == -1) {
+ PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
+ }
+}
+
+void Service::SetProcessAttributes() {
+ // Keep capabilites on uid change.
+ if (capabilities_.any() && uid_) {
+ if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED) != 0) {
+ PLOG(FATAL) << "prtcl(PR_SET_KEEPCAPS) failed for " << name_;
+ }
+ }
+
+ // TODO: work out why this fails for `console` then upgrade to FATAL.
+ if (setpgid(0, getpid()) == -1) PLOG(ERROR) << "setpgid failed for " << name_;
+
+ if (gid_) {
+ if (setgid(gid_) != 0) {
+ PLOG(FATAL) << "setgid failed for " << name_;
+ }
+ }
+ if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
+ PLOG(FATAL) << "setgroups failed for " << name_;
+ }
+ if (uid_) {
+ if (setuid(uid_) != 0) {
+ PLOG(FATAL) << "setuid failed for " << name_;
+ }
+ }
+ if (!seclabel_.empty()) {
+ if (setexeccon(seclabel_.c_str()) < 0) {
+ PLOG(FATAL) << "cannot setexeccon('" << seclabel_ << "') for " << name_;
+ }
+ }
+ if (priority_ != 0) {
+ if (setpriority(PRIO_PROCESS, 0, priority_) != 0) {
+ PLOG(FATAL) << "setpriority failed for " << name_;
+ }
+ }
+ if (capabilities_.any()) {
+ if (!SetCapsForExec(capabilities_)) {
+ LOG(FATAL) << "cannot set capabilities for " << name_;
+ }
+ }
}
bool Service::Reap() {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
- NOTICE("Service '%s' (pid %d) killing any children in process group\n",
- name_.c_str(), pid_);
- kill(-pid_, SIGKILL);
+ KillProcessGroup(SIGKILL);
}
- // Remove any sockets we may have created.
- for (const auto& si : sockets_) {
- std::string tmp = android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s",
- si.name.c_str());
- unlink(tmp.c_str());
- }
+ // Remove any descriptor resources we may have created.
+ std::for_each(descriptors_.begin(), descriptors_.end(),
+ std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
if (flags_ & SVC_EXEC) {
- INFO("SVC_EXEC pid %d finished...\n", pid_);
+ LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
return true;
}
@@ -129,19 +277,17 @@
return false;
}
- time_t now = gettime();
+ // If we crash > 4 times in 4 minutes, reboot into recovery.
+ boot_clock::time_point now = boot_clock::now();
if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
- if (time_crashed_ + CRITICAL_CRASH_WINDOW >= now) {
- if (++nr_crashed_ > CRITICAL_CRASH_THRESHOLD) {
- ERROR("critical process '%s' exited %d times in %d minutes; "
- "rebooting into recovery mode\n", name_.c_str(),
- CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- return false;
+ if (now < time_crashed_ + 4min) {
+ if (++crash_count_ > 4) {
+ LOG(ERROR) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
+ panic();
}
} else {
time_crashed_ = now;
- nr_crashed_ = 1;
+ crash_count_ = 1;
}
}
@@ -156,165 +302,272 @@
}
void Service::DumpState() const {
- INFO("service %s\n", name_.c_str());
- INFO(" class '%s'\n", classname_.c_str());
- INFO(" exec");
- for (const auto& s : args_) {
- INFO(" '%s'", s.c_str());
- }
- INFO("\n");
- for (const auto& si : sockets_) {
- INFO(" socket %s %s 0%o\n", si.name.c_str(), si.type.c_str(), si.perm);
- }
+ LOG(INFO) << "service " << name_;
+ LOG(INFO) << " class '" << classname_ << "'";
+ LOG(INFO) << " exec "<< android::base::Join(args_, " ");
+ std::for_each(descriptors_.begin(), descriptors_.end(),
+ [] (const auto& info) { LOG(INFO) << *info; });
}
-bool Service::HandleLine(int kw, const std::vector<std::string>& args, std::string* err) {
- std::vector<std::string> str_args;
+bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
+ capabilities_ = 0;
- ioprio_class_ = IoSchedClass_NONE;
-
- switch (kw) {
- case K_class:
- if (args.size() != 2) {
- *err = "class option requires a classname\n";
- return false;
- } else {
- classname_ = args[1];
- }
- break;
- case K_console:
- flags_ |= SVC_CONSOLE;
- break;
- case K_disabled:
- flags_ |= SVC_DISABLED;
- flags_ |= SVC_RC_DISABLED;
- break;
- case K_ioprio:
- if (args.size() != 3) {
- *err = "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n";
- return false;
- } else {
- ioprio_pri_ = std::stoul(args[2], 0, 8);
-
- if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
- *err = "priority value must be range 0 - 7\n";
- return false;
- }
-
- if (args[1] == "rt") {
- ioprio_class_ = IoSchedClass_RT;
- } else if (args[1] == "be") {
- ioprio_class_ = IoSchedClass_BE;
- } else if (args[1] == "idle") {
- ioprio_class_ = IoSchedClass_IDLE;
- } else {
- *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>\n";
- return false;
- }
- }
- break;
- case K_group:
- if (args.size() < 2) {
- *err = "group option requires a group id\n";
- return false;
- } else if (args.size() > NR_SVC_SUPP_GIDS + 2) {
- *err = android::base::StringPrintf("group option accepts at most %d supp. groups\n",
- NR_SVC_SUPP_GIDS);
- return false;
- } else {
- gid_ = decode_uid(args[1].c_str());
- for (std::size_t n = 2; n < args.size(); n++) {
- supp_gids_.push_back(decode_uid(args[n].c_str()));
- }
- }
- break;
- case K_keycodes:
- if (args.size() < 2) {
- *err = "keycodes option requires atleast one keycode\n";
- return false;
- } else {
- for (std::size_t i = 1; i < args.size(); i++) {
- keycodes_.push_back(std::stoi(args[i]));
- }
- }
- break;
- case K_oneshot:
- flags_ |= SVC_ONESHOT;
- break;
- case K_onrestart:
- if (args.size() < 2) {
- return false;
- }
- str_args.assign(args.begin() + 1, args.end());
- add_command_to_action(&onrestart_, str_args, "", 0, err);
- break;
- case K_critical:
- flags_ |= SVC_CRITICAL;
- break;
- case K_setenv: { /* name value */
- if (args.size() < 3) {
- *err = "setenv option requires name and value arguments\n";
- return false;
- }
-
- envvars_.push_back({args[1], args[2]});
- break;
+ if (!CapAmbientSupported()) {
+ *err = "capabilities requested but the kernel does not support ambient capabilities";
+ return false;
}
- case K_socket: {/* name type perm [ uid gid context ] */
- if (args.size() < 4) {
- *err = "socket option requires name, type, perm arguments\n";
- return false;
- }
- if (args[2] != "dgram" && args[2] != "stream" &&
- args[2] != "seqpacket") {
- *err = "socket type must be 'dgram', 'stream' or 'seqpacket'\n";
- return false;
- }
- int perm = std::stoul(args[3], 0, 8);
- uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
- gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
- std::string socketcon = args.size() > 6 ? args[6] : "";
-
- sockets_.push_back({args[1], args[2], uid, gid, perm, socketcon});
- break;
+ unsigned int last_valid_cap = GetLastValidCap();
+ if (last_valid_cap >= capabilities_.size()) {
+ LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP";
}
- case K_user:
- if (args.size() != 2) {
- *err = "user option requires a user id\n";
- return false;
- } else {
- uid_ = decode_uid(args[1].c_str());
- }
- break;
- case K_seclabel:
- if (args.size() != 2) {
- *err = "seclabel option requires a label string\n";
- return false;
- } else {
- seclabel_ = args[1];
- }
- break;
- case K_writepid:
- if (args.size() < 2) {
- *err = "writepid option requires at least one filename\n";
- return false;
- }
- writepid_files_.assign(args.begin() + 1, args.end());
- break;
- default:
- *err = android::base::StringPrintf("invalid option '%s'\n", args[0].c_str());
+ for (size_t i = 1; i < args.size(); i++) {
+ const std::string& arg = args[i];
+ int res = LookupCap(arg);
+ if (res < 0) {
+ *err = StringPrintf("invalid capability '%s'", arg.c_str());
+ return false;
+ }
+ unsigned int cap = static_cast<unsigned int>(res); // |res| is >= 0.
+ if (cap > last_valid_cap) {
+ *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
+ return false;
+ }
+ capabilities_[cap] = true;
+ }
+ return true;
+}
+
+bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
+ classname_ = args[1];
+ return true;
+}
+
+bool Service::ParseConsole(const std::vector<std::string>& args, std::string* err) {
+ flags_ |= SVC_CONSOLE;
+ console_ = args.size() > 1 ? "/dev/" + args[1] : "";
+ return true;
+}
+
+bool Service::ParseCritical(const std::vector<std::string>& args, std::string* err) {
+ flags_ |= SVC_CRITICAL;
+ return true;
+}
+
+bool Service::ParseDisabled(const std::vector<std::string>& args, std::string* err) {
+ flags_ |= SVC_DISABLED;
+ flags_ |= SVC_RC_DISABLED;
+ return true;
+}
+
+bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
+ gid_ = decode_uid(args[1].c_str());
+ for (std::size_t n = 2; n < args.size(); n++) {
+ supp_gids_.emplace_back(decode_uid(args[n].c_str()));
+ }
+ return true;
+}
+
+bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) {
+ priority_ = 0;
+ if (!ParseInt(args[1], &priority_,
+ static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
+ static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
+ *err = StringPrintf("process priority value must be range %d - %d",
+ ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
return false;
}
return true;
}
-bool Service::Start(const std::vector<std::string>& dynamic_args) {
+bool Service::ParseIoprio(const std::vector<std::string>& args, std::string* err) {
+ if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
+ *err = "priority value must be range 0 - 7";
+ return false;
+ }
+
+ if (args[1] == "rt") {
+ ioprio_class_ = IoSchedClass_RT;
+ } else if (args[1] == "be") {
+ ioprio_class_ = IoSchedClass_BE;
+ } else if (args[1] == "idle") {
+ ioprio_class_ = IoSchedClass_IDLE;
+ } else {
+ *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
+ return false;
+ }
+
+ return true;
+}
+
+bool Service::ParseKeycodes(const std::vector<std::string>& args, std::string* err) {
+ for (std::size_t i = 1; i < args.size(); i++) {
+ int code;
+ if (ParseInt(args[i], &code)) {
+ keycodes_.emplace_back(code);
+ } else {
+ LOG(WARNING) << "ignoring invalid keycode: " << args[i];
+ }
+ }
+ return true;
+}
+
+bool Service::ParseOneshot(const std::vector<std::string>& args, std::string* err) {
+ flags_ |= SVC_ONESHOT;
+ return true;
+}
+
+bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
+ std::vector<std::string> str_args(args.begin() + 1, args.end());
+ onrestart_.AddCommand(str_args, "", 0, err);
+ return true;
+}
+
+bool Service::ParseNamespace(const std::vector<std::string>& args, std::string* err) {
+ for (size_t i = 1; i < args.size(); i++) {
+ if (args[i] == "pid") {
+ namespace_flags_ |= CLONE_NEWPID;
+ // PID namespaces require mount namespaces.
+ namespace_flags_ |= CLONE_NEWNS;
+ } else if (args[i] == "mnt") {
+ namespace_flags_ |= CLONE_NEWNS;
+ } else {
+ *err = "namespace must be 'pid' or 'mnt'";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Service::ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err) {
+ if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
+ *err = "oom_score_adjust value must be in range -1000 - +1000";
+ return false;
+ }
+ return true;
+}
+
+bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) {
+ seclabel_ = args[1];
+ return true;
+}
+
+bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err) {
+ envvars_.emplace_back(args[1], args[2]);
+ return true;
+}
+
+template <typename T>
+bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
+ int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
+ uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
+ gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+ std::string context = args.size() > 6 ? args[6] : "";
+
+ auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
+
+ auto old =
+ std::find_if(descriptors_.begin(), descriptors_.end(),
+ [&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
+
+ if (old != descriptors_.end()) {
+ *err = "duplicate descriptor " + args[1] + " " + args[2];
+ return false;
+ }
+
+ descriptors_.emplace_back(std::move(descriptor));
+ return true;
+}
+
+// name type perm [ uid gid context ]
+bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
+ if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
+ *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
+ return false;
+ }
+ return AddDescriptor<SocketInfo>(args, err);
+}
+
+// name type perm [ uid gid context ]
+bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
+ if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
+ *err = "file type must be 'r', 'w' or 'rw'";
+ return false;
+ }
+ if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
+ *err = "file name must not be relative";
+ return false;
+ }
+ return AddDescriptor<FileInfo>(args, err);
+}
+
+bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
+ uid_ = decode_uid(args[1].c_str());
+ return true;
+}
+
+bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* err) {
+ writepid_files_.assign(args.begin() + 1, args.end());
+ return true;
+}
+
+class Service::OptionParserMap : public KeywordMap<OptionParser> {
+public:
+ OptionParserMap() {
+ }
+private:
+ Map& map() const override;
+};
+
+Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
+ constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+ static const Map option_parsers = {
+ {"capabilities",
+ {1, kMax, &Service::ParseCapabilities}},
+ {"class", {1, 1, &Service::ParseClass}},
+ {"console", {0, 1, &Service::ParseConsole}},
+ {"critical", {0, 0, &Service::ParseCritical}},
+ {"disabled", {0, 0, &Service::ParseDisabled}},
+ {"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
+ {"ioprio", {2, 2, &Service::ParseIoprio}},
+ {"priority", {1, 1, &Service::ParsePriority}},
+ {"keycodes", {1, kMax, &Service::ParseKeycodes}},
+ {"oneshot", {0, 0, &Service::ParseOneshot}},
+ {"onrestart", {1, kMax, &Service::ParseOnrestart}},
+ {"oom_score_adjust",
+ {1, 1, &Service::ParseOomScoreAdjust}},
+ {"namespace", {1, 2, &Service::ParseNamespace}},
+ {"seclabel", {1, 1, &Service::ParseSeclabel}},
+ {"setenv", {2, 2, &Service::ParseSetenv}},
+ {"socket", {3, 6, &Service::ParseSocket}},
+ {"file", {2, 2, &Service::ParseFile}},
+ {"user", {1, 1, &Service::ParseUser}},
+ {"writepid", {1, kMax, &Service::ParseWritepid}},
+ };
+ return option_parsers;
+}
+
+bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
+ if (args.empty()) {
+ *err = "option needed, but not provided";
+ return false;
+ }
+
+ static const OptionParserMap parser_map;
+ auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);
+
+ if (!parser) {
+ return false;
+ }
+
+ return (this->*parser)(args, err);
+}
+
+bool Service::Start() {
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
- time_started_ = 0;
// Running processes require no additional work --- if they're in the
// process of exiting, we've ensured that they will immediately restart
@@ -324,23 +577,25 @@
}
bool needs_console = (flags_ & SVC_CONSOLE);
- if (needs_console && !have_console) {
- ERROR("service '%s' requires console\n", name_.c_str());
- flags_ |= SVC_DISABLED;
- return false;
+ if (needs_console) {
+ if (console_.empty()) {
+ console_ = default_console;
+ }
+
+ // Make sure that open call succeeds to ensure a console driver is
+ // properly registered for the device node
+ int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
+ if (console_fd < 0) {
+ PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
+ flags_ |= SVC_DISABLED;
+ return false;
+ }
+ close(console_fd);
}
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
- ERROR("cannot find '%s' (%s), disabling '%s'\n",
- args_[0].c_str(), strerror(errno), name_.c_str());
- flags_ |= SVC_DISABLED;
- return false;
- }
-
- if ((!(flags_ & SVC_ONESHOT)) && !dynamic_args.empty()) {
- ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
- args_[0].c_str());
+ PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
flags_ |= SVC_DISABLED;
return false;
}
@@ -349,87 +604,49 @@
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- char* mycon = nullptr;
- char* fcon = nullptr;
-
- INFO("computing context for service '%s'\n", args_[0].c_str());
- int rc = getcon(&mycon);
- if (rc < 0) {
- ERROR("could not get context while starting '%s'\n", name_.c_str());
- return false;
- }
-
- rc = getfilecon(args_[0].c_str(), &fcon);
- if (rc < 0) {
- ERROR("could not get context while starting '%s'\n", name_.c_str());
- free(mycon);
- return false;
- }
-
- char* ret_scon = nullptr;
- rc = security_compute_create(mycon, fcon, string_to_security_class("process"),
- &ret_scon);
- if (rc == 0) {
- scon = ret_scon;
- free(ret_scon);
- }
- if (rc == 0 && scon == mycon) {
- ERROR("Service %s does not have a SELinux domain defined.\n", name_.c_str());
- free(mycon);
- free(fcon);
- return false;
- }
- free(mycon);
- free(fcon);
- if (rc < 0) {
- ERROR("could not get context while starting '%s'\n", name_.c_str());
+ LOG(INFO) << "computing context for service '" << name_ << "'";
+ scon = ComputeContextFromExecutable(name_, args_[0]);
+ if (scon == "") {
return false;
}
}
- NOTICE("Starting service '%s'...\n", name_.c_str());
+ LOG(INFO) << "starting service '" << name_ << "'...";
- pid_t pid = fork();
+ pid_t pid = -1;
+ if (namespace_flags_) {
+ pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
+ } else {
+ pid = fork();
+ }
+
if (pid == 0) {
- int fd, sz;
-
umask(077);
- if (properties_initialized()) {
- get_property_workspace(&fd, &sz);
- std::string tmp = android::base::StringPrintf("%d,%d", dup(fd), sz);
- add_environment("ANDROID_PROPERTY_WORKSPACE", tmp.c_str());
+
+ if (namespace_flags_ & CLONE_NEWPID) {
+ // This will fork again to run an init process inside the PID
+ // namespace.
+ SetUpPidNamespace(name_);
}
for (const auto& ei : envvars_) {
add_environment(ei.name.c_str(), ei.value.c_str());
}
- for (const auto& si : sockets_) {
- int socket_type = ((si.type == "stream" ? SOCK_STREAM :
- (si.type == "dgram" ? SOCK_DGRAM :
- SOCK_SEQPACKET)));
- const char* socketcon =
- !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
+ std::for_each(descriptors_.begin(), descriptors_.end(),
+ std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
- int s = create_socket(si.name.c_str(), socket_type, si.perm,
- si.uid, si.gid, socketcon);
- if (s >= 0) {
- PublishSocket(si.name, s);
- }
- }
-
- std::string pid_str = android::base::StringPrintf("%d", pid);
+ std::string pid_str = StringPrintf("%d", getpid());
for (const auto& file : writepid_files_) {
- if (!android::base::WriteStringToFile(pid_str, file)) {
- ERROR("couldn't write %s to %s: %s\n",
- pid_str.c_str(), file.c_str(), strerror(errno));
+ if (!WriteStringToFile(pid_str, file)) {
+ PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
}
}
if (ioprio_class_ != IoSchedClass_NONE) {
if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
- ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
- getpid(), ioprio_class_, ioprio_pri_, strerror(errno));
+ PLOG(ERROR) << "failed to set pid " << getpid()
+ << " ioprio=" << ioprio_class_ << "," << ioprio_pri_;
}
}
@@ -440,75 +657,53 @@
ZapStdio();
}
- setpgid(0, getpid());
-
- // As requested, set our gid, supplemental gids, and uid.
- if (gid_) {
- if (setgid(gid_) != 0) {
- ERROR("setgid failed: %s\n", strerror(errno));
- _exit(127);
- }
- }
- if (!supp_gids_.empty()) {
- if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
- ERROR("setgroups failed: %s\n", strerror(errno));
- _exit(127);
- }
- }
- if (uid_) {
- if (setuid(uid_) != 0) {
- ERROR("setuid failed: %s\n", strerror(errno));
- _exit(127);
- }
- }
- if (!seclabel_.empty()) {
- if (setexeccon(seclabel_.c_str()) < 0) {
- ERROR("cannot setexeccon('%s'): %s\n",
- seclabel_.c_str(), strerror(errno));
- _exit(127);
- }
- }
+ // As requested, set our gid, supplemental gids, uid, context, and
+ // priority. Aborts on failure.
+ SetProcessAttributes();
std::vector<char*> strs;
- for (const auto& s : args_) {
- strs.push_back(const_cast<char*>(s.c_str()));
- }
- for (const auto& s : dynamic_args) {
- strs.push_back(const_cast<char*>(s.c_str()));
- }
- strs.push_back(nullptr);
- if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
- ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
+ ExpandArgs(args_, &strs);
+ if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
+ PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
}
_exit(127);
}
if (pid < 0) {
- ERROR("failed to start '%s'\n", name_.c_str());
+ PLOG(ERROR) << "failed to fork for '" << name_ << "'";
pid_ = 0;
return false;
}
- time_started_ = gettime();
+ if (oom_score_adjust_ != -1000) {
+ std::string oom_str = StringPrintf("%d", oom_score_adjust_);
+ std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
+ if (!WriteStringToFile(oom_str, oom_file)) {
+ PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
+ }
+ }
+
+ time_started_ = boot_clock::now();
pid_ = pid;
flags_ |= SVC_RUNNING;
+ errno = -createProcessGroup(uid_, pid_);
+ if (errno != 0) {
+ PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
+ << name_ << "'";
+ }
+
if ((flags_ & SVC_EXEC) != 0) {
- INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
- pid_, uid_, gid_, supp_gids_.size(),
- !seclabel_.empty() ? seclabel_.c_str() : "default");
+ LOG(INFO) << android::base::StringPrintf(
+ "SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...", pid_, uid_, gid_,
+ supp_gids_.size(), !seclabel_.empty() ? seclabel_.c_str() : "default");
}
NotifyStateChange("running");
return true;
}
-bool Service::Start() {
- const std::vector<std::string> null_dynamic_args;
- return Start(null_dynamic_args);
-}
-
bool Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
@@ -534,6 +729,15 @@
StopOrReset(SVC_DISABLED);
}
+void Service::Terminate() {
+ flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
+ flags_ |= SVC_DISABLED;
+ if (pid_) {
+ KillProcessGroup(SIGTERM);
+ NotifyStateChange("stopping");
+ }
+}
+
void Service::Restart() {
if (flags_ & SVC_RUNNING) {
/* Stop, wait, then start the service. */
@@ -544,34 +748,34 @@
} /* else: Service is restarting anyways. */
}
-void Service::RestartIfNeeded(time_t& process_needs_restart) {
- time_t next_start_time = time_started_ + 5;
-
- if (next_start_time <= gettime()) {
+void Service::RestartIfNeeded(time_t* process_needs_restart_at) {
+ boot_clock::time_point now = boot_clock::now();
+ boot_clock::time_point next_start = time_started_ + 5s;
+ if (now > next_start) {
flags_ &= (~SVC_RESTARTING);
Start();
return;
}
- if ((next_start_time < process_needs_restart) ||
- (process_needs_restart == 0)) {
- process_needs_restart = next_start_time;
+ time_t next_start_time_t = time(nullptr) +
+ time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count());
+ if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) {
+ *process_needs_restart_at = next_start_time_t;
}
}
-/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
+// The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART.
void Service::StopOrReset(int how) {
- /* The service is still SVC_RUNNING until its process exits, but if it has
- * already exited it shoudn't attempt a restart yet. */
+ // The service is still SVC_RUNNING until its process exits, but if it has
+ // already exited it shoudn't attempt a restart yet.
flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
- /* Hrm, an illegal flag. Default to SVC_DISABLED */
+ // An illegal flag: default to SVC_DISABLED.
how = SVC_DISABLED;
}
- /* if the service has not yet started, prevent
- * it from auto-starting with its class
- */
+
+ // If the service has not yet started, prevent it from auto-starting with its class.
if (how == SVC_RESET) {
flags_ |= (flags_ & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
} else {
@@ -579,8 +783,7 @@
}
if (pid_) {
- NOTICE("Service '%s' is being killed...\n", name_.c_str());
- kill(-pid_, SIGKILL);
+ KillProcessGroup(SIGKILL);
NotifyStateChange("stopping");
} else {
NotifyStateChange("stopped");
@@ -597,10 +800,8 @@
}
void Service::OpenConsole() const {
- int fd;
- if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
- fd = open("/dev/null", O_RDWR);
- }
+ int fd = open(console_.c_str(), O_RDWR);
+ if (fd == -1) fd = open("/dev/null", O_RDWR);
ioctl(fd, TIOCSCTTY, 0);
dup2(fd, 0);
dup2(fd, 1);
@@ -608,16 +809,6 @@
close(fd);
}
-void Service::PublishSocket(const std::string& name, int fd) const {
- std::string key = android::base::StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s",
- name.c_str());
- std::string val = android::base::StringPrintf("%d", fd);
- add_environment(key.c_str(), val.c_str());
-
- /* make sure we don't close-on-exec */
- fcntl(fd, F_SETFD, 0);
-}
-
int ServiceManager::exec_count_ = 0;
ServiceManager::ServiceManager() {
@@ -628,31 +819,13 @@
return instance;
}
-Service* ServiceManager::AddNewService(const std::string& name,
- const std::string& classname,
- const std::vector<std::string>& args,
- std::string* err) {
- if (!IsValidName(name)) {
- *err = android::base::StringPrintf("invalid service name '%s'\n", name.c_str());
- return nullptr;
+void ServiceManager::AddService(std::unique_ptr<Service> service) {
+ Service* old_service = FindServiceByName(service->name());
+ if (old_service) {
+ LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
+ return;
}
-
- Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
- if (svc) {
- *err = android::base::StringPrintf("ignored duplicate definition of service '%s'\n",
- name.c_str());
- return nullptr;
- }
-
- std::unique_ptr<Service> svc_p(new Service(name, classname, args));
- if (!svc_p) {
- ERROR("Couldn't allocate service for service '%s'", name.c_str());
- return nullptr;
- }
- svc = svc_p.get();
- services_.push_back(std::move(svc_p));
-
- return svc;
+ services_.emplace_back(std::move(service));
}
Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
@@ -666,20 +839,21 @@
}
}
if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
- ERROR("exec called with too many supplementary group ids\n");
+ LOG(ERROR) << "exec called with too many supplementary group ids";
return nullptr;
}
if (command_arg >= args.size()) {
- ERROR("exec called without command\n");
+ LOG(ERROR) << "exec called without command";
return nullptr;
}
std::vector<std::string> str_args(args.begin() + command_arg, args.end());
exec_count_++;
- std::string name = android::base::StringPrintf("exec %d (%s)", exec_count_,
- str_args[0].c_str());
+ std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
unsigned flags = SVC_EXEC | SVC_ONESHOT;
+ CapSet no_capabilities;
+ unsigned namespace_flags = 0;
std::string seclabel = "";
if (command_arg > 2 && args[1] != "-") {
@@ -699,11 +873,11 @@
}
}
- std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid,
- supp_gids, seclabel, str_args));
+ std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid, supp_gids,
+ no_capabilities, namespace_flags, seclabel,
+ str_args));
if (!svc_p) {
- ERROR("Couldn't allocate service for exec of '%s'",
- str_args[0].c_str());
+ LOG(ERROR) << "Couldn't allocate service for exec of '" << str_args[0] << "'";
return nullptr;
}
Service* svc = svc_p.get();
@@ -746,9 +920,9 @@
return nullptr;
}
-void ServiceManager::ForEachService(void (*func)(Service* svc)) const {
+void ServiceManager::ForEachService(const std::function<void(Service*)>& callback) const {
for (const auto& s : services_) {
- func(s.get());
+ callback(s.get());
}
}
@@ -770,8 +944,7 @@
}
}
-void ServiceManager::RemoveService(const Service& svc)
-{
+void ServiceManager::RemoveService(const Service& svc) {
auto svc_it = std::find_if(services_.begin(), services_.end(),
[&svc] (const std::unique_ptr<Service>& s) {
return svc.name() == s->name();
@@ -783,23 +956,89 @@
services_.erase(svc_it);
}
-bool ServiceManager::IsValidName(const std::string& name) const
-{
- if (name.size() > 16) {
- return false;
- }
- for (const auto& c : name) {
- if (!isalnum(c) && (c != '_') && (c != '-')) {
- return false;
- }
- }
- return true;
-}
-
-void ServiceManager::DumpState() const
-{
+void ServiceManager::DumpState() const {
for (const auto& s : services_) {
s->DumpState();
}
- INFO("\n");
+}
+
+bool ServiceManager::ReapOneProcess() {
+ int status;
+ pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
+ if (pid == 0) {
+ return false;
+ } else if (pid == -1) {
+ PLOG(ERROR) << "waitpid failed";
+ return false;
+ }
+
+ Service* svc = FindServiceByPid(pid);
+
+ std::string name;
+ if (svc) {
+ name = android::base::StringPrintf("Service '%s' (pid %d)",
+ svc->name().c_str(), pid);
+ } else {
+ name = android::base::StringPrintf("Untracked pid %d", pid);
+ }
+
+ if (WIFEXITED(status)) {
+ LOG(INFO) << name << " exited with status " << WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ LOG(INFO) << name << " killed by signal " << WTERMSIG(status);
+ } else if (WIFSTOPPED(status)) {
+ LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status);
+ } else {
+ LOG(INFO) << name << " state changed";
+ }
+
+ if (!svc) {
+ return true;
+ }
+
+ if (svc->Reap()) {
+ waiting_for_exec = false;
+ RemoveService(*svc);
+ }
+
+ return true;
+}
+
+void ServiceManager::ReapAnyOutstandingChildren() {
+ while (ReapOneProcess()) {
+ }
+}
+
+bool ServiceParser::ParseSection(const std::vector<std::string>& args,
+ std::string* err) {
+ if (args.size() < 3) {
+ *err = "services must have a name and a program";
+ return false;
+ }
+
+ const std::string& name = args[1];
+ if (!IsValidName(name)) {
+ *err = StringPrintf("invalid service name '%s'", name.c_str());
+ return false;
+ }
+
+ std::vector<std::string> str_args(args.begin() + 2, args.end());
+ service_ = std::make_unique<Service>(name, "default", str_args);
+ return true;
+}
+
+bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
+ const std::string& filename, int line,
+ std::string* err) const {
+ return service_ ? service_->ParseLine(args, err) : false;
+}
+
+void ServiceParser::EndSection() {
+ if (service_) {
+ ServiceManager::GetInstance().AddService(std::move(service_));
+ }
+}
+
+bool ServiceParser::IsValidName(const std::string& name) const {
+ return is_legal_property_name("init.svc." + name);
}
diff --git a/init/service.h b/init/service.h
index 1ecf78a..013e65f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -26,6 +26,11 @@
#include <vector>
#include "action.h"
+#include "capabilities.h"
+#include "descriptors.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+#include "util.h"
#define SVC_DISABLED 0x001 // do not autostart with class
#define SVC_ONESHOT 0x002 // do not restart on exit
@@ -45,18 +50,6 @@
class Action;
class ServiceManager;
-struct SocketInfo {
- SocketInfo();
- SocketInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& socketcon);
- std::string name;
- std::string type;
- uid_t uid;
- gid_t gid;
- int perm;
- std::string socketcon;
-};
-
struct ServiceEnvironmentInfo {
ServiceEnvironmentInfo();
ServiceEnvironmentInfo(const std::string& name, const std::string& value);
@@ -70,18 +63,20 @@
const std::vector<std::string>& args);
Service(const std::string& name, const std::string& classname,
- unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
- const std::string& seclabel, const std::vector<std::string>& args);
+ unsigned flags, uid_t uid, gid_t gid,
+ const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
+ unsigned namespace_flags, const std::string& seclabel,
+ const std::vector<std::string>& args);
- bool HandleLine(int kw, const std::vector<std::string>& args, std::string* err);
- bool Start(const std::vector<std::string>& dynamic_args);
+ bool ParseLine(const std::vector<std::string>& args, std::string* err);
bool Start();
bool StartIfNotDisabled();
bool Enable();
void Reset();
void Stop();
+ void Terminate();
void Restart();
- void RestartIfNeeded(time_t& process_needs_restart);
+ void RestartIfNeeded(time_t* process_needs_restart_at);
bool Reap();
void DumpState() const;
@@ -91,6 +86,7 @@
pid_t pid() const { return pid_; }
uid_t uid() const { return uid_; }
gid_t gid() const { return gid_; }
+ int priority() const { return priority_; }
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_; }
@@ -99,28 +95,59 @@
const std::vector<std::string>& args() const { return args_; }
private:
+ using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
+ std::string* err);
+ class OptionParserMap;
+
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
void ZapStdio() const;
void OpenConsole() const;
- void PublishSocket(const std::string& name, int fd) const;
+ void KillProcessGroup(int signal);
+ void SetProcessAttributes();
+
+ bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
+ bool ParseClass(const std::vector<std::string>& args, std::string* err);
+ bool ParseConsole(const std::vector<std::string>& args, std::string* err);
+ bool ParseCritical(const std::vector<std::string>& args, std::string* err);
+ bool ParseDisabled(const std::vector<std::string>& args, std::string* err);
+ bool ParseGroup(const std::vector<std::string>& args, std::string* err);
+ bool ParsePriority(const std::vector<std::string>& args, std::string* err);
+ bool ParseIoprio(const std::vector<std::string>& args, std::string* err);
+ bool ParseKeycodes(const std::vector<std::string>& args, std::string* err);
+ bool ParseOneshot(const std::vector<std::string>& args, std::string* err);
+ bool ParseOnrestart(const std::vector<std::string>& args, std::string* err);
+ bool ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err);
+ bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
+ bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
+ bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
+ bool ParseSocket(const std::vector<std::string>& args, std::string* err);
+ bool ParseFile(const std::vector<std::string>& args, std::string* err);
+ bool ParseUser(const std::vector<std::string>& args, std::string* err);
+ bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
+
+ template <typename T>
+ bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
std::string name_;
std::string classname_;
+ std::string console_;
unsigned flags_;
pid_t pid_;
- time_t time_started_; // time of last start
- time_t time_crashed_; // first crash within inspection window
- int nr_crashed_; // number of times crashed within window
+ boot_clock::time_point time_started_; // time of last start
+ boot_clock::time_point time_crashed_; // first crash within inspection window
+ int crash_count_; // number of times crashed within window
uid_t uid_;
gid_t gid_;
std::vector<gid_t> supp_gids_;
+ CapSet capabilities_;
+ unsigned namespace_flags_;
std::string seclabel_;
- std::vector<SocketInfo> sockets_;
+ std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
std::vector<ServiceEnvironmentInfo> envvars_;
Action onrestart_; // Commands to execute on restart.
@@ -133,6 +160,9 @@
IoSchedClass ioprio_class_;
int ioprio_pri_;
+ int priority_;
+
+ int oom_score_adjust_;
std::vector<std::string> args_;
};
@@ -141,27 +171,47 @@
public:
static ServiceManager& GetInstance();
- Service* AddNewService(const std::string& name, const std::string& classname,
- const std::vector<std::string>& args,
- std::string* err);
+ void AddService(std::unique_ptr<Service> service);
Service* MakeExecOneshotService(const std::vector<std::string>& args);
Service* FindServiceByName(const std::string& name) const;
Service* FindServiceByPid(pid_t pid) const;
Service* FindServiceByKeychord(int keychord_id) const;
- void ForEachService(void (*func)(Service* svc)) const;
+ void ForEachService(const std::function<void(Service*)>& callback) const;
void ForEachServiceInClass(const std::string& classname,
void (*func)(Service* svc)) const;
void ForEachServiceWithFlags(unsigned matchflags,
void (*func)(Service* svc)) const;
+ void ReapAnyOutstandingChildren();
void RemoveService(const Service& svc);
void DumpState() const;
+
private:
ServiceManager();
- bool IsValidName(const std::string& name) const;
+ // Cleans up a child process that exited.
+ // Returns true iff a children was cleaned up.
+ bool ReapOneProcess();
static int exec_count_; // Every service needs a unique name.
std::vector<std::unique_ptr<Service>> services_;
};
+class ServiceParser : public SectionParser {
+public:
+ ServiceParser() : service_(nullptr) {
+ }
+ bool ParseSection(const std::vector<std::string>& args,
+ std::string* err) override;
+ bool ParseLineSection(const std::vector<std::string>& args,
+ const std::string& filename, int line,
+ std::string* err) const override;
+ void EndSection() override;
+ void EndFile(const std::string&) override {
+ }
+private:
+ bool IsValidName(const std::string& name) const;
+
+ std::unique_ptr<Service> service_;
+};
+
#endif
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 867abbc..1041b82 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -23,8 +23,7 @@
#include <sys/wait.h>
#include <unistd.h>
-#include <base/stringprintf.h>
-#include <cutils/android_reboot.h>
+#include <android-base/stringprintf.h>
#include <cutils/list.h>
#include <cutils/sockets.h>
@@ -37,67 +36,17 @@
static int signal_write_fd = -1;
static int signal_read_fd = -1;
-static std::string DescribeStatus(int status) {
- if (WIFEXITED(status)) {
- return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- return android::base::StringPrintf("killed by signal %d", WTERMSIG(status));
- } else if (WIFSTOPPED(status)) {
- return android::base::StringPrintf("stopped by signal %d", WSTOPSIG(status));
- } else {
- return "state changed";
- }
-}
-
-static bool wait_for_one_process() {
- int status;
- pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
- if (pid == 0) {
- return false;
- } else if (pid == -1) {
- ERROR("waitpid failed: %s\n", strerror(errno));
- return false;
- }
-
- Service* svc = ServiceManager::GetInstance().FindServiceByPid(pid);
-
- std::string name;
- if (svc) {
- name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
- } else {
- name = android::base::StringPrintf("Untracked pid %d", pid);
- }
-
- NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());
-
- if (!svc) {
- return true;
- }
-
- if (svc->Reap()) {
- waiting_for_exec = false;
- ServiceManager::GetInstance().RemoveService(*svc);
- }
-
- return true;
-}
-
-static void reap_any_outstanding_children() {
- while (wait_for_one_process()) {
- }
-}
-
static void handle_signal() {
// Clear outstanding requests.
char buf[32];
read(signal_read_fd, buf, sizeof(buf));
- reap_any_outstanding_children();
+ ServiceManager::GetInstance().ReapAnyOutstandingChildren();
}
static void SIGCHLD_handler(int) {
if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
- ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "write(signal_write_fd) failed";
}
}
@@ -105,7 +54,7 @@
// Create a signalling mechanism for SIGCHLD.
int s[2];
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
- ERROR("socketpair failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "socketpair failed";
exit(1);
}
@@ -119,7 +68,7 @@
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
- reap_any_outstanding_children();
+ ServiceManager::GetInstance().ReapAnyOutstandingChildren();
register_epoll_handler(signal_read_fd, handle_signal);
}
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 75924cb..361b925 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -16,14 +16,17 @@
#include <ctype.h>
#include <fcntl.h>
+#include <grp.h>
#include <poll.h>
+#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <base/stringprintf.h>
-#include <private/android_filesystem_config.h>
+#include <sys/types.h>
+
+#include <android-base/stringprintf.h>
#include <selinux/selinux.h>
#include "ueventd.h"
@@ -49,11 +52,9 @@
*/
signal(SIGCHLD, SIG_IGN);
- open_devnull_stdio();
- klog_init();
- klog_set_level(KLOG_NOTICE_LEVEL);
+ InitKernelLogging(argv);
- NOTICE("ueventd started!\n");
+ LOG(INFO) << "ueventd started!";
selinux_callback cb;
cb.func_log = selinux_klog_callback;
@@ -84,15 +85,6 @@
return 0;
}
-static int get_android_id(const char *id)
-{
- unsigned int i;
- for (i = 0; i < ARRAY_SIZE(android_ids); i++)
- if (!strcmp(id, android_ids[i].name))
- return android_ids[i].aid;
- return -1;
-}
-
void set_device_permission(int nargs, char **args)
{
char *name;
@@ -103,8 +95,6 @@
int prefix = 0;
int wildcard = 0;
char *endptr;
- int ret;
- char *tmp = 0;
if (nargs == 0)
return;
@@ -115,58 +105,53 @@
name = args[0];
if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
- INFO("/sys/ rule %s %s\n",args[0],args[1]);
+ LOG(INFO) << "/sys/ rule " << args[0] << " " << args[1];
attr = args[1];
args++;
nargs--;
}
if (nargs != 4) {
- ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);
+ LOG(ERROR) << "invalid line ueventd.rc line for '" << args[0] << "'";
return;
}
- /* If path starts with mtd@ lookup the mount number. */
- if (!strncmp(name, "mtd@", 4)) {
- int n = mtd_name_to_number(name + 4);
- if (n >= 0)
- asprintf(&tmp, "/dev/mtd/mtd%d", n);
- name = tmp;
- } else {
- int len = strlen(name);
- char *wildcard_chr = strchr(name, '*');
- if ((name[len - 1] == '*') &&
- (wildcard_chr == (name + len - 1))) {
- prefix = 1;
- name[len - 1] = '\0';
- } else if (wildcard_chr) {
- wildcard = 1;
- }
+ int len = strlen(name);
+ char *wildcard_chr = strchr(name, '*');
+ if ((name[len - 1] == '*') && (wildcard_chr == (name + len - 1))) {
+ prefix = 1;
+ name[len - 1] = '\0';
+ } else if (wildcard_chr) {
+ wildcard = 1;
}
perm = strtol(args[1], &endptr, 8);
if (!endptr || *endptr != '\0') {
- ERROR("invalid mode '%s'\n", args[1]);
- free(tmp);
+ LOG(ERROR) << "invalid mode '" << args[1] << "'";
return;
}
- ret = get_android_id(args[2]);
- if (ret < 0) {
- ERROR("invalid uid '%s'\n", args[2]);
- free(tmp);
+ struct passwd* pwd = getpwnam(args[2]);
+ if (!pwd) {
+ LOG(ERROR) << "invalid uid '" << args[2] << "'";
return;
}
- uid = ret;
+ uid = pwd->pw_uid;
- ret = get_android_id(args[3]);
- if (ret < 0) {
- ERROR("invalid gid '%s'\n", args[3]);
- free(tmp);
+ struct group* grp = getgrnam(args[3]);
+ if (!grp) {
+ LOG(ERROR) << "invalid gid '" << args[3] << "'";
return;
}
- gid = ret;
+ gid = grp->gr_gid;
- add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard);
- free(tmp);
+ if (add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard) != 0) {
+ PLOG(ERROR) << "add_dev_perms(name=" << name <<
+ ", attr=" << attr <<
+ ", perm=" << std::oct << perm << std::dec <<
+ ", uid=" << uid << ", gid=" << gid <<
+ ", prefix=" << prefix << ", wildcard=" << wildcard <<
+ ")";
+ return;
+ }
}
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 497c606..baff58c 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -38,7 +38,7 @@
#include "ueventd_keywords.h"
#define KEYWORD(symbol, flags, nargs) \
- [ K_##symbol ] = { #symbol, nargs + 1, flags, },
+ [ K_##symbol ] = { #symbol, (nargs) + 1, flags, },
static struct {
const char *name;
@@ -233,7 +233,6 @@
data.push_back('\n'); // TODO: fix parse_config.
parse_config(fn, data);
- dump_parser_state();
return 0;
}
diff --git a/init/util.cpp b/init/util.cpp
index f6131e3..888a366 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,62 +14,56 @@
* limitations under the License.
*/
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <pwd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <errno.h>
#include <time.h>
-#include <ftw.h>
+#include <unistd.h>
-#include <selinux/label.h>
#include <selinux/android.h>
+#include <selinux/label.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/socket.h>
#include <sys/un.h>
-#include <base/file.h>
-#include <base/strings.h>
+#include <thread>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include <cutils/android_reboot.h>
/* for ANDROID_SOCKET_* */
#include <cutils/sockets.h>
-#include <base/stringprintf.h>
-
-#include <private/android_filesystem_config.h>
#include "init.h"
#include "log.h"
+#include "property_service.h"
#include "util.h"
-/*
- * android_name_to_id - returns the integer uid/gid associated with the given
- * name, or UINT_MAX on error.
- */
-static unsigned int android_name_to_id(const char *name)
-{
- const struct android_id_info *info = android_ids;
- unsigned int n;
-
- for (n = 0; n < android_id_count; n++) {
- if (!strcmp(info[n].name, name))
- return info[n].aid;
- }
-
- return UINT_MAX;
-}
-
static unsigned int do_decode_uid(const char *s)
{
unsigned int v;
if (!s || *s == '\0')
return UINT_MAX;
- if (isalpha(s[0]))
- return android_name_to_id(s);
+
+ if (isalpha(s[0])) {
+ struct passwd* pwd = getpwnam(s);
+ if (!pwd)
+ return UINT_MAX;
+ return pwd->pw_uid;
+ }
errno = 0;
v = (unsigned int) strtoul(s, 0, 0);
@@ -86,7 +80,7 @@
unsigned int decode_uid(const char *s) {
unsigned int v = do_decode_uid(s);
if (v == UINT_MAX) {
- ERROR("decode_uid: Unable to find UID for '%s'. Returning UINT_MAX\n", s);
+ LOG(ERROR) << "decode_uid: Unable to find UID for '" << s << "'; returning UINT_MAX";
}
return v;
}
@@ -100,61 +94,69 @@
int create_socket(const char *name, int type, mode_t perm, uid_t uid,
gid_t gid, const char *socketcon)
{
- struct sockaddr_un addr;
- int fd, ret;
- char *filecon;
+ if (socketcon) {
+ if (setsockcreatecon(socketcon) == -1) {
+ PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
+ return -1;
+ }
+ }
- if (socketcon)
- setsockcreatecon(socketcon);
-
- fd = socket(PF_UNIX, type, 0);
+ android::base::unique_fd fd(socket(PF_UNIX, type, 0));
if (fd < 0) {
- ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));
+ PLOG(ERROR) << "Failed to open socket '" << name << "'";
return -1;
}
- if (socketcon)
- setsockcreatecon(NULL);
+ if (socketcon) setsockcreatecon(NULL);
+ struct sockaddr_un addr;
memset(&addr, 0 , sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
name);
- ret = unlink(addr.sun_path);
- if (ret != 0 && errno != ENOENT) {
- ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));
- goto out_close;
+ if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {
+ PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
+ return -1;
}
- filecon = NULL;
+ char *filecon = NULL;
if (sehandle) {
- ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);
- if (ret == 0)
+ if (selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK) == 0) {
setfscreatecon(filecon);
+ }
}
- ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
- if (ret) {
- ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));
- goto out_unlink;
- }
+ int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
+ int savederrno = errno;
setfscreatecon(NULL);
freecon(filecon);
- chown(addr.sun_path, uid, gid);
- chmod(addr.sun_path, perm);
+ if (ret) {
+ errno = savederrno;
+ PLOG(ERROR) << "Failed to bind socket '" << name << "'";
+ goto out_unlink;
+ }
- INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",
- addr.sun_path, perm, uid, gid);
+ if (lchown(addr.sun_path, uid, gid)) {
+ PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
+ goto out_unlink;
+ }
+ if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
+ PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
+ goto out_unlink;
+ }
- return fd;
+ LOG(INFO) << "Created socket '" << addr.sun_path << "'"
+ << ", mode " << std::oct << perm << std::dec
+ << ", user " << uid
+ << ", group " << gid;
+
+ return fd.release();
out_unlink:
unlink(addr.sun_path);
-out_close:
- close(fd);
return -1;
}
@@ -170,11 +172,11 @@
// or group-writable files.
struct stat sb;
if (fstat(fd, &sb) == -1) {
- ERROR("fstat failed for '%s': %s\n", path, strerror(errno));
+ PLOG(ERROR) << "fstat failed for '" << path << "'";
return false;
}
if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- ERROR("skipping insecure file '%s'\n", path);
+ PLOG(ERROR) << "skipping insecure file '" << path << "'";
return false;
}
@@ -183,102 +185,25 @@
return okay;
}
-int write_file(const char* path, const char* content) {
+bool write_file(const char* path, const char* content) {
int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
if (fd == -1) {
- NOTICE("write_file: Unable to open '%s': %s\n", path, strerror(errno));
- return -1;
+ PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
+ return false;
}
- int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
- if (result == -1) {
- NOTICE("write_file: Unable to write to '%s': %s\n", path, strerror(errno));
+ bool success = android::base::WriteStringToFd(content, fd);
+ if (!success) {
+ PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
}
close(fd);
- return result;
+ return success;
}
-#define MAX_MTD_PARTITIONS 16
-
-static struct {
- char name[16];
- int number;
-} mtd_part_map[MAX_MTD_PARTITIONS];
-
-static int mtd_part_count = -1;
-
-static void find_mtd_partitions(void)
-{
- int fd;
- char buf[1024];
- char *pmtdbufp;
- ssize_t pmtdsize;
- int r;
-
- fd = open("/proc/mtd", O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- return;
-
- buf[sizeof(buf) - 1] = '\0';
- pmtdsize = read(fd, buf, sizeof(buf) - 1);
- pmtdbufp = buf;
- while (pmtdsize > 0) {
- int mtdnum, mtdsize, mtderasesize;
- char mtdname[16];
- mtdname[0] = '\0';
- mtdnum = -1;
- r = sscanf(pmtdbufp, "mtd%d: %x %x %15s",
- &mtdnum, &mtdsize, &mtderasesize, mtdname);
- if ((r == 4) && (mtdname[0] == '"')) {
- char *x = strchr(mtdname + 1, '"');
- if (x) {
- *x = 0;
- }
- INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1);
- if (mtd_part_count < MAX_MTD_PARTITIONS) {
- strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1);
- mtd_part_map[mtd_part_count].number = mtdnum;
- mtd_part_count++;
- } else {
- ERROR("too many mtd partitions\n");
- }
- }
- while (pmtdsize > 0 && *pmtdbufp != '\n') {
- pmtdbufp++;
- pmtdsize--;
- }
- if (pmtdsize > 0) {
- pmtdbufp++;
- pmtdsize--;
- }
- }
- close(fd);
-}
-
-int mtd_name_to_number(const char *name)
-{
- int n;
- if (mtd_part_count < 0) {
- mtd_part_count = 0;
- find_mtd_partitions();
- }
- for (n = 0; n < mtd_part_count; n++) {
- if (!strcmp(name, mtd_part_map[n].name)) {
- return mtd_part_map[n].number;
- }
- }
- return -1;
-}
-
-time_t gettime() {
- timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- return now.tv_sec;
-}
-
-uint64_t gettime_ns() {
- timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
+boot_clock::time_point boot_clock::now() {
+ timespec ts;
+ clock_gettime(CLOCK_BOOTTIME, &ts);
+ return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
+ std::chrono::nanoseconds(ts.tv_nsec));
}
int mkdir_recursive(const char *pathname, mode_t mode)
@@ -298,7 +223,7 @@
if (width == 0)
continue;
if ((unsigned int)width > sizeof(buf) - 1) {
- ERROR("path too long for mkdir_recursive\n");
+ LOG(ERROR) << "path too long for mkdir_recursive";
return -1;
}
memcpy(buf, pathname, width);
@@ -336,82 +261,19 @@
}
}
-void make_link(const char *oldpath, const char *newpath)
-{
- int ret;
- char buf[256];
- char *slash;
- int width;
+int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
+ boot_clock::time_point timeout_time = boot_clock::now() + timeout;
+ while (boot_clock::now() < timeout_time) {
+ struct stat sb;
+ if (stat(filename, &sb) != -1) return 0;
- slash = strrchr(newpath, '/');
- if (!slash)
- return;
- width = slash - newpath;
- if (width <= 0 || width > (int)sizeof(buf) - 1)
- return;
- memcpy(buf, newpath, width);
- buf[width] = 0;
- ret = mkdir_recursive(buf, 0755);
- if (ret)
- ERROR("Failed to create directory %s: %s (%d)\n", buf, strerror(errno), errno);
-
- ret = symlink(oldpath, newpath);
- if (ret && errno != EEXIST)
- ERROR("Failed to symlink %s to %s: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
-}
-
-void remove_link(const char *oldpath, const char *newpath)
-{
- char path[256];
- ssize_t ret;
- ret = readlink(newpath, path, sizeof(path) - 1);
- if (ret <= 0)
- return;
- path[ret] = 0;
- if (!strcmp(path, oldpath))
- unlink(newpath);
-}
-
-int wait_for_file(const char *filename, int timeout)
-{
- struct stat info;
- time_t timeout_time = gettime() + timeout;
- int ret = -1;
-
- while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
- usleep(10000);
-
- return ret;
-}
-
-void open_devnull_stdio(void)
-{
- // Try to avoid the mknod() call if we can. Since SELinux makes
- // a /dev/null replacement available for free, let's use it.
- int fd = open("/sys/fs/selinux/null", O_RDWR);
- if (fd == -1) {
- // OOPS, /sys/fs/selinux/null isn't available, likely because
- // /sys/fs/selinux isn't mounted. Fall back to mknod.
- static const char *name = "/dev/__null__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
- fd = open(name, O_RDWR);
- unlink(name);
- }
- if (fd == -1) {
- exit(1);
- }
+ std::this_thread::sleep_for(10ms);
}
-
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) {
- close(fd);
- }
+ return -1;
}
void import_kernel_cmdline(bool in_qemu,
- std::function<void(const std::string&, const std::string&, bool)> fn) {
+ const std::function<void(const std::string&, const std::string&, bool)>& fn) {
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
@@ -446,14 +308,9 @@
return rc;
}
-int restorecon(const char* pathname)
+int restorecon(const char* pathname, int flags)
{
- return selinux_android_restorecon(pathname, 0);
-}
-
-int restorecon_recursive(const char* pathname)
-{
- return selinux_android_restorecon(pathname, SELINUX_ANDROID_RESTORECON_RECURSE);
+ return selinux_android_restorecon(pathname, flags);
}
/*
@@ -476,3 +333,100 @@
}
return S_ISDIR(info.st_mode);
}
+
+bool expand_props(const std::string& src, std::string* dst) {
+ const char* src_ptr = src.c_str();
+
+ if (!dst) {
+ return false;
+ }
+
+ /* - variables can either be $x.y or ${x.y}, in case they are only part
+ * of the string.
+ * - will accept $$ as a literal $.
+ * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
+ * bad things will happen
+ * - ${x.y:-default} will return default value if property empty.
+ */
+ while (*src_ptr) {
+ const char* c;
+
+ c = strchr(src_ptr, '$');
+ if (!c) {
+ dst->append(src_ptr);
+ return true;
+ }
+
+ dst->append(src_ptr, c);
+ c++;
+
+ if (*c == '$') {
+ dst->push_back(*(c++));
+ src_ptr = c;
+ continue;
+ } else if (*c == '\0') {
+ return true;
+ }
+
+ std::string prop_name;
+ std::string def_val;
+ if (*c == '{') {
+ c++;
+ const char* end = strchr(c, '}');
+ if (!end) {
+ // failed to find closing brace, abort.
+ LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
+ return false;
+ }
+ prop_name = std::string(c, end);
+ c = end + 1;
+ size_t def = prop_name.find(":-");
+ if (def < prop_name.size()) {
+ def_val = prop_name.substr(def + 2);
+ prop_name = prop_name.substr(0, def);
+ }
+ } else {
+ prop_name = c;
+ LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
+ c += prop_name.size();
+ }
+
+ if (prop_name.empty()) {
+ LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
+ return false;
+ }
+
+ std::string prop_val = property_get(prop_name.c_str());
+ if (prop_val.empty()) {
+ if (def_val.empty()) {
+ LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
+ return false;
+ }
+ prop_val = def_val;
+ }
+
+ dst->append(prop_val);
+ src_ptr = c;
+ }
+
+ return true;
+}
+
+void reboot(const char* destination) {
+ android_reboot(ANDROID_RB_RESTART2, 0, destination);
+ // We're init, so android_reboot will actually have been a syscall so there's nothing
+ // to wait for. If android_reboot returns, just abort so that the kernel will reboot
+ // itself when init dies.
+ PLOG(FATAL) << "reboot failed";
+ abort();
+}
+
+void panic() {
+ LOG(ERROR) << "panic: rebooting to bootloader";
+ reboot("bootloader");
+}
+
+std::ostream& operator<<(std::ostream& os, const Timer& t) {
+ os << t.duration_s() << " seconds";
+ return os;
+}
diff --git a/init/util.h b/init/util.h
index f08cb8d..5c38dc3 100644
--- a/init/util.h
+++ b/init/util.h
@@ -20,49 +20,65 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <string>
+#include <chrono>
#include <functional>
-
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#include <ostream>
+#include <string>
#define COLDBOOT_DONE "/dev/.coldboot_done"
-int mtd_name_to_number(const char *name);
+using namespace std::chrono_literals;
+
int create_socket(const char *name, int type, mode_t perm,
uid_t uid, gid_t gid, const char *socketcon);
bool read_file(const char* path, std::string* content);
-int write_file(const char* path, const char* content);
+bool write_file(const char* path, const char* content);
-time_t gettime();
-uint64_t gettime_ns();
+// A std::chrono clock based on CLOCK_BOOTTIME.
+class boot_clock {
+ public:
+ typedef std::chrono::nanoseconds duration;
+ typedef std::chrono::time_point<boot_clock, duration> time_point;
+ static constexpr bool is_steady = true;
+
+ static time_point now();
+};
class Timer {
public:
- Timer() : t0(gettime_ns()) {
+ Timer() : start_(boot_clock::now()) {
}
- double duration() {
- return static_cast<double>(gettime_ns() - t0) / 1000000000.0;
+ double duration_s() const {
+ typedef std::chrono::duration<double> double_duration;
+ return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
+ }
+
+ int64_t duration_ms() const {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_).count();
}
private:
- uint64_t t0;
+ boot_clock::time_point start_;
};
+std::ostream& operator<<(std::ostream& os, const Timer& t);
+
unsigned int decode_uid(const char *s);
int mkdir_recursive(const char *pathname, mode_t mode);
void sanitize(char *p);
-void make_link(const char *oldpath, const char *newpath);
-void remove_link(const char *oldpath, const char *newpath);
-int wait_for_file(const char *filename, int timeout);
-void open_devnull_stdio(void);
+int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
void import_kernel_cmdline(bool in_qemu,
- std::function<void(const std::string&, const std::string&, bool)>);
+ const std::function<void(const std::string&, const std::string&, bool)>&);
int make_dir(const char *path, mode_t mode);
-int restorecon(const char *pathname);
-int restorecon_recursive(const char *pathname);
+int restorecon(const char *pathname, int flags = 0);
std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
bool is_dir(const char* pathname);
+bool expand_props(const std::string& src, std::string* dst);
+
+void reboot(const char* destination) __attribute__((__noreturn__));
+void panic() __attribute__((__noreturn__));
+
#endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 228954b..24c75c4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,6 +17,7 @@
#include "util.h"
#include <errno.h>
+
#include <gtest/gtest.h>
TEST(util, read_file_ENOENT) {
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index 0d16db9..b196147 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -28,9 +28,7 @@
#define DEV_NAME "/dev/watchdog"
int watchdogd_main(int argc, char **argv) {
- open_devnull_stdio();
- klog_init();
- klog_set_level(KLOG_NOTICE_LEVEL);
+ InitKernelLogging(argv);
int interval = 10;
if (argc >= 2) interval = atoi(argv[1]);
@@ -38,30 +36,31 @@
int margin = 10;
if (argc >= 3) margin = atoi(argv[2]);
- NOTICE("started (interval %d, margin %d)!\n", interval, margin);
+ LOG(INFO) << "watchdogd started (interval " << interval << ", margin " << margin << ")!";
int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
if (fd == -1) {
- ERROR("Failed to open %s: %s\n", DEV_NAME, strerror(errno));
+ PLOG(ERROR) << "Failed to open " << DEV_NAME;
return 1;
}
int timeout = interval + margin;
int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
if (ret) {
- ERROR("Failed to set timeout to %d: %s\n", timeout, strerror(errno));
+ PLOG(ERROR) << "Failed to set timeout to " << timeout;
ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
if (ret) {
- ERROR("Failed to get timeout: %s\n", strerror(errno));
+ PLOG(ERROR) << "Failed to get timeout";
} else {
if (timeout > margin) {
interval = timeout - margin;
} else {
interval = 1;
}
- WARNING("Adjusted interval to timeout returned by driver:"
- " timeout %d, interval %d, margin %d\n",
- timeout, interval, margin);
+ LOG(WARNING) << "Adjusted interval to timeout returned by driver: "
+ << "timeout " << timeout
+ << ", interval " << interval
+ << ", margin " << margin;
}
}
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
new file mode 100644
index 0000000..f729faf
--- /dev/null
+++ b/libappfuse/Android.bp
@@ -0,0 +1,34 @@
+// Copyright 2016 The Android Open Source Project
+
+cc_defaults {
+ name: "libappfuse_defaults",
+ local_include_dirs: ["include"],
+ shared_libs: ["libbase"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ clang: true
+}
+
+cc_library_shared {
+ name: "libappfuse",
+ defaults: ["libappfuse_defaults"],
+ export_include_dirs: ["include"],
+ srcs: [
+ "FuseAppLoop.cc",
+ "FuseBuffer.cc",
+ "FuseBridgeLoop.cc",
+ ]
+}
+
+cc_test {
+ name: "libappfuse_test",
+ defaults: ["libappfuse_defaults"],
+ shared_libs: ["libappfuse"],
+ srcs: [
+ "tests/FuseAppLoopTest.cc",
+ "tests/FuseBridgeLoopTest.cc",
+ "tests/FuseBufferTest.cc",
+ ]
+}
diff --git a/libappfuse/FuseAppLoop.cc b/libappfuse/FuseAppLoop.cc
new file mode 100644
index 0000000..a31880e
--- /dev/null
+++ b/libappfuse/FuseAppLoop.cc
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseAppLoop.h"
+
+#include <sys/stat.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fuse {
+
+namespace {
+
+void HandleLookUp(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ // AppFuse does not support directory structure now.
+ // It can lookup only files under the mount point.
+ if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
+ LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
+ buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+ return;
+ }
+
+ // Ensure that the filename ends with 0.
+ const size_t filename_length =
+ buffer->request.header.len - sizeof(fuse_in_header);
+ if (buffer->request.lookup_name[filename_length - 1] != 0) {
+ LOG(ERROR) << "File name does not end with 0.";
+ buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+ return;
+ }
+
+ const uint64_t inode =
+ static_cast<uint64_t>(atol(buffer->request.lookup_name));
+ if (inode == 0 || inode == LONG_MAX) {
+ LOG(ERROR) << "Invalid filename";
+ buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+ return;
+ }
+
+ const int64_t size = callback->OnGetSize(inode);
+ if (size < 0) {
+ buffer->response.Reset(0, size, buffer->request.header.unique);
+ return;
+ }
+
+ buffer->response.Reset(sizeof(fuse_entry_out), 0,
+ buffer->request.header.unique);
+ buffer->response.entry_out.nodeid = inode;
+ buffer->response.entry_out.attr_valid = 10;
+ buffer->response.entry_out.entry_valid = 10;
+ buffer->response.entry_out.attr.ino = inode;
+ buffer->response.entry_out.attr.mode = S_IFREG | 0777;
+ buffer->response.entry_out.attr.size = size;
+}
+
+void HandleGetAttr(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const uint64_t nodeid = buffer->request.header.nodeid;
+ int64_t size;
+ uint32_t mode;
+ if (nodeid == FUSE_ROOT_ID) {
+ size = 0;
+ mode = S_IFDIR | 0777;
+ } else {
+ size = callback->OnGetSize(buffer->request.header.nodeid);
+ if (size < 0) {
+ buffer->response.Reset(0, size, buffer->request.header.unique);
+ return;
+ }
+ mode = S_IFREG | 0777;
+ }
+
+ buffer->response.Reset(sizeof(fuse_attr_out), 0,
+ buffer->request.header.unique);
+ buffer->response.attr_out.attr_valid = 10;
+ buffer->response.attr_out.attr.ino = nodeid;
+ buffer->response.attr_out.attr.mode = mode;
+ buffer->response.attr_out.attr.size = size;
+}
+
+void HandleOpen(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const int32_t file_handle = callback->OnOpen(buffer->request.header.nodeid);
+ if (file_handle < 0) {
+ buffer->response.Reset(0, file_handle, buffer->request.header.unique);
+ return;
+ }
+ buffer->response.Reset(sizeof(fuse_open_out), kFuseSuccess,
+ buffer->request.header.unique);
+ buffer->response.open_out.fh = file_handle;
+}
+
+void HandleFsync(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ buffer->response.Reset(0, callback->OnFsync(buffer->request.header.nodeid),
+ buffer->request.header.unique);
+}
+
+void HandleRelease(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ buffer->response.Reset(0, callback->OnRelease(buffer->request.header.nodeid),
+ buffer->request.header.unique);
+}
+
+void HandleRead(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const uint64_t unique = buffer->request.header.unique;
+ const uint64_t nodeid = buffer->request.header.nodeid;
+ const uint64_t offset = buffer->request.read_in.offset;
+ const uint32_t size = buffer->request.read_in.size;
+
+ if (size > kFuseMaxRead) {
+ buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
+ return;
+ }
+
+ const int32_t read_size = callback->OnRead(nodeid, offset, size,
+ buffer->response.read_data);
+ if (read_size < 0) {
+ buffer->response.Reset(0, read_size, buffer->request.header.unique);
+ return;
+ }
+
+ buffer->response.ResetHeader(read_size, kFuseSuccess, unique);
+}
+
+void HandleWrite(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const uint64_t unique = buffer->request.header.unique;
+ const uint64_t nodeid = buffer->request.header.nodeid;
+ const uint64_t offset = buffer->request.write_in.offset;
+ const uint32_t size = buffer->request.write_in.size;
+
+ if (size > kFuseMaxWrite) {
+ buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
+ return;
+ }
+
+ const int32_t write_size = callback->OnWrite(nodeid, offset, size,
+ buffer->request.write_data);
+ if (write_size < 0) {
+ buffer->response.Reset(0, write_size, buffer->request.header.unique);
+ return;
+ }
+
+ buffer->response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
+ buffer->response.write_out.size = write_size;
+}
+
+} // namespace
+
+bool StartFuseAppLoop(int raw_fd, FuseAppLoopCallback* callback) {
+ base::unique_fd fd(raw_fd);
+ FuseBuffer buffer;
+
+ LOG(DEBUG) << "Start fuse loop.";
+ while (callback->IsActive()) {
+ if (!buffer.request.Read(fd)) {
+ return false;
+ }
+
+ const uint32_t opcode = buffer.request.header.opcode;
+ LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+ switch (opcode) {
+ case FUSE_FORGET:
+ // Do not reply to FUSE_FORGET.
+ continue;
+
+ case FUSE_LOOKUP:
+ HandleLookUp(&buffer, callback);
+ break;
+
+ case FUSE_GETATTR:
+ HandleGetAttr(&buffer, callback);
+ break;
+
+ case FUSE_OPEN:
+ HandleOpen(&buffer, callback);
+ break;
+
+ case FUSE_READ:
+ HandleRead(&buffer, callback);
+ break;
+
+ case FUSE_WRITE:
+ HandleWrite(&buffer, callback);
+ break;
+
+ case FUSE_RELEASE:
+ HandleRelease(&buffer, callback);
+ break;
+
+ case FUSE_FSYNC:
+ HandleFsync(&buffer, callback);
+ break;
+
+ default:
+ buffer.HandleNotImpl();
+ break;
+ }
+
+ if (!buffer.response.Write(fd)) {
+ LOG(ERROR) << "Failed to write a response to the device.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
new file mode 100644
index 0000000..2386bf8
--- /dev/null
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseBridgeLoop.h"
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fuse {
+
+bool StartFuseBridgeLoop(
+ int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoopCallback* callback) {
+ base::unique_fd dev_fd(raw_dev_fd);
+ base::unique_fd proxy_fd(raw_proxy_fd);
+ FuseBuffer buffer;
+ size_t open_count = 0;
+
+ LOG(DEBUG) << "Start fuse loop.";
+ while (true) {
+ if (!buffer.request.Read(dev_fd)) {
+ return false;
+ }
+
+ const uint32_t opcode = buffer.request.header.opcode;
+ LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+ switch (opcode) {
+ case FUSE_FORGET:
+ // Do not reply to FUSE_FORGET.
+ continue;
+
+ case FUSE_LOOKUP:
+ case FUSE_GETATTR:
+ case FUSE_OPEN:
+ case FUSE_READ:
+ case FUSE_WRITE:
+ case FUSE_RELEASE:
+ case FUSE_FSYNC:
+ if (!buffer.request.Write(proxy_fd)) {
+ LOG(ERROR) << "Failed to write a request to the proxy.";
+ return false;
+ }
+ if (!buffer.response.Read(proxy_fd)) {
+ LOG(ERROR) << "Failed to read a response from the proxy.";
+ return false;
+ }
+ break;
+
+ case FUSE_INIT:
+ buffer.HandleInit();
+ break;
+
+ default:
+ buffer.HandleNotImpl();
+ break;
+ }
+
+ if (!buffer.response.Write(dev_fd)) {
+ LOG(ERROR) << "Failed to write a response to the device.";
+ return false;
+ }
+
+ switch (opcode) {
+ case FUSE_INIT:
+ callback->OnMount();
+ break;
+ case FUSE_OPEN:
+ if (buffer.response.header.error == fuse::kFuseSuccess) {
+ open_count++;
+ }
+ break;
+ case FUSE_RELEASE:
+ if (open_count != 0) {
+ open_count--;
+ } else {
+ LOG(WARNING) << "Unexpected FUSE_RELEASE before opening a file.";
+ break;
+ }
+ if (open_count == 0) {
+ return true;
+ }
+ break;
+ }
+ }
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
new file mode 100644
index 0000000..8fb2dbc
--- /dev/null
+++ b/libappfuse/FuseBuffer.cc
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseBuffer.h"
+
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <type_traits>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace fuse {
+
+static_assert(
+ std::is_standard_layout<FuseBuffer>::value,
+ "FuseBuffer must be standard layout union.");
+
+template <typename T>
+bool FuseMessage<T>::CheckHeaderLength(const char* name) const {
+ const auto& header = static_cast<const T*>(this)->header;
+ if (header.len >= sizeof(header) && header.len <= sizeof(T)) {
+ return true;
+ } else {
+ LOG(ERROR) << "Invalid header length is found in " << name << ": " <<
+ header.len;
+ return false;
+ }
+}
+
+template <typename T>
+bool FuseMessage<T>::Read(int fd) {
+ char* const buf = reinterpret_cast<char*>(this);
+ const ssize_t result = TEMP_FAILURE_RETRY(::read(fd, buf, sizeof(T)));
+ if (result < 0) {
+ PLOG(ERROR) << "Failed to read a FUSE message";
+ return false;
+ }
+
+ const auto& header = static_cast<const T*>(this)->header;
+ if (result < static_cast<ssize_t>(sizeof(header))) {
+ LOG(ERROR) << "Read bytes " << result << " are shorter than header size " <<
+ sizeof(header);
+ return false;
+ }
+
+ if (!CheckHeaderLength("Read")) {
+ return false;
+ }
+
+ if (static_cast<uint32_t>(result) > header.len) {
+ LOG(ERROR) << "Read bytes " << result << " are longer than header.len " <<
+ header.len;
+ return false;
+ }
+
+ if (!base::ReadFully(fd, buf + result, header.len - result)) {
+ PLOG(ERROR) << "ReadFully failed";
+ return false;
+ }
+
+ return true;
+}
+
+template <typename T>
+bool FuseMessage<T>::Write(int fd) const {
+ if (!CheckHeaderLength("Write")) {
+ return false;
+ }
+
+ const char* const buf = reinterpret_cast<const char*>(this);
+ const auto& header = static_cast<const T*>(this)->header;
+ if (!base::WriteFully(fd, buf, header.len)) {
+ PLOG(ERROR) << "WriteFully failed";
+ return false;
+ }
+
+ return true;
+}
+
+template class FuseMessage<FuseRequest>;
+template class FuseMessage<FuseResponse>;
+
+void FuseRequest::Reset(
+ uint32_t data_length, uint32_t opcode, uint64_t unique) {
+ memset(this, 0, sizeof(fuse_in_header) + data_length);
+ header.len = sizeof(fuse_in_header) + data_length;
+ header.opcode = opcode;
+ header.unique = unique;
+}
+
+void FuseResponse::ResetHeader(
+ uint32_t data_length, int32_t error, uint64_t unique) {
+ CHECK_LE(error, 0) << "error should be zero or negative.";
+ header.len = sizeof(fuse_out_header) + data_length;
+ header.error = error;
+ header.unique = unique;
+}
+
+void FuseResponse::Reset(uint32_t data_length, int32_t error, uint64_t unique) {
+ memset(this, 0, sizeof(fuse_out_header) + data_length);
+ ResetHeader(data_length, error, unique);
+}
+
+void FuseBuffer::HandleInit() {
+ const fuse_init_in* const in = &request.init_in;
+
+ // Before writing |out|, we need to copy data from |in|.
+ const uint64_t unique = request.header.unique;
+ const uint32_t minor = in->minor;
+ const uint32_t max_readahead = in->max_readahead;
+
+ // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
+ // defined (fuse version 7.6). The structure is the same from 7.6 through
+ // 7.22. Beginning with 7.23, the structure increased in size and added
+ // new parameters.
+ if (in->major != FUSE_KERNEL_VERSION || in->minor < 6) {
+ LOG(ERROR) << "Fuse kernel version mismatch: Kernel version " << in->major
+ << "." << in->minor << " Expected at least " << FUSE_KERNEL_VERSION
+ << ".6";
+ response.Reset(0, -EPERM, unique);
+ return;
+ }
+
+ // We limit ourselves to minor=15 because we don't handle BATCH_FORGET yet.
+ // Thus we need to use FUSE_COMPAT_22_INIT_OUT_SIZE.
+#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
+ // FUSE_KERNEL_VERSION >= 23.
+ const size_t response_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
+#else
+ const size_t response_size = sizeof(fuse_init_out);
+#endif
+
+ response.Reset(response_size, kFuseSuccess, unique);
+ fuse_init_out* const out = &response.init_out;
+ out->major = FUSE_KERNEL_VERSION;
+ out->minor = std::min(minor, 15u);
+ out->max_readahead = max_readahead;
+ out->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
+ out->max_background = 32;
+ out->congestion_threshold = 32;
+ out->max_write = kFuseMaxWrite;
+}
+
+void FuseBuffer::HandleNotImpl() {
+ LOG(VERBOSE) << "NOTIMPL op=" << request.header.opcode << " uniq="
+ << request.header.unique << " nid=" << request.header.nodeid;
+ const uint64_t unique = request.header.unique;
+ response.Reset(0, -ENOSYS, unique);
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/include/libappfuse/FuseAppLoop.h b/libappfuse/include/libappfuse/FuseAppLoop.h
new file mode 100644
index 0000000..c3edfcc
--- /dev/null
+++ b/libappfuse/include/libappfuse/FuseAppLoop.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+
+#include "libappfuse/FuseBuffer.h"
+
+namespace android {
+namespace fuse {
+
+class FuseAppLoopCallback {
+ public:
+ virtual bool IsActive() = 0;
+ virtual int64_t OnGetSize(uint64_t inode) = 0;
+ virtual int32_t OnFsync(uint64_t inode) = 0;
+ virtual int32_t OnWrite(
+ uint64_t inode, uint64_t offset, uint32_t size, const void* data) = 0;
+ virtual int32_t OnRead(
+ uint64_t inode, uint64_t offset, uint32_t size, void* data) = 0;
+ virtual int32_t OnOpen(uint64_t inode) = 0;
+ virtual int32_t OnRelease(uint64_t inode) = 0;
+ virtual ~FuseAppLoopCallback() = default;
+};
+
+bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
+
+} // namespace fuse
+} // namespace android
+
+#endif // ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
diff --git a/libappfuse/include/libappfuse/FuseBridgeLoop.h b/libappfuse/include/libappfuse/FuseBridgeLoop.h
new file mode 100644
index 0000000..1f71cf2
--- /dev/null
+++ b/libappfuse/include/libappfuse/FuseBridgeLoop.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
+#define ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
+
+#include "libappfuse/FuseBuffer.h"
+
+namespace android {
+namespace fuse {
+
+class FuseBridgeLoopCallback {
+ public:
+ virtual void OnMount() = 0;
+ virtual ~FuseBridgeLoopCallback() = default;
+};
+
+bool StartFuseBridgeLoop(
+ int dev_fd, int proxy_fd, FuseBridgeLoopCallback* callback);
+
+} // namespace fuse
+} // namespace android
+
+#endif // ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
new file mode 100644
index 0000000..7abd2fa
--- /dev/null
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
+#define ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
+
+#include <linux/fuse.h>
+
+namespace android {
+namespace fuse {
+
+// The numbers came from sdcard.c.
+// Maximum number of bytes to write/read in one request/one reply.
+constexpr size_t kFuseMaxWrite = 256 * 1024;
+constexpr size_t kFuseMaxRead = 128 * 1024;
+constexpr int32_t kFuseSuccess = 0;
+
+template<typename T>
+class FuseMessage {
+ public:
+ bool Read(int fd);
+ bool Write(int fd) const;
+ private:
+ bool CheckHeaderLength(const char* name) const;
+};
+
+// FuseRequest represents file operation requests from /dev/fuse. It starts
+// from fuse_in_header. The body layout depends on the operation code.
+struct FuseRequest : public FuseMessage<FuseRequest> {
+ fuse_in_header header;
+ union {
+ // for FUSE_WRITE
+ struct {
+ fuse_write_in write_in;
+ char write_data[kFuseMaxWrite];
+ };
+ // for FUSE_OPEN
+ fuse_open_in open_in;
+ // for FUSE_INIT
+ fuse_init_in init_in;
+ // for FUSE_READ
+ fuse_read_in read_in;
+ // for FUSE_LOOKUP
+ char lookup_name[0];
+ };
+ void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique);
+};
+
+// FuseResponse represents file operation responses to /dev/fuse. It starts
+// from fuse_out_header. The body layout depends on the operation code.
+struct FuseResponse : public FuseMessage<FuseResponse> {
+ fuse_out_header header;
+ union {
+ // for FUSE_INIT
+ fuse_init_out init_out;
+ // for FUSE_LOOKUP
+ fuse_entry_out entry_out;
+ // for FUSE_GETATTR
+ fuse_attr_out attr_out;
+ // for FUSE_OPEN
+ fuse_open_out open_out;
+ // for FUSE_READ
+ char read_data[kFuseMaxRead];
+ // for FUSE_WRITE
+ fuse_write_out write_out;
+ };
+ void Reset(uint32_t data_length, int32_t error, uint64_t unique);
+ void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
+};
+
+// To reduce memory usage, FuseBuffer shares the memory region for request and
+// response.
+union FuseBuffer final {
+ FuseRequest request;
+ FuseResponse response;
+
+ void HandleInit();
+ void HandleNotImpl();
+};
+
+} // namespace fuse
+} // namespace android
+
+#endif // ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
new file mode 100644
index 0000000..25906cf
--- /dev/null
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseAppLoop.h"
+
+#include <sys/socket.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+namespace fuse {
+namespace {
+
+constexpr unsigned int kTestFileSize = 1024;
+
+struct CallbackRequest {
+ uint32_t code;
+ uint64_t inode;
+};
+
+class Callback : public FuseAppLoopCallback {
+ public:
+ std::vector<CallbackRequest> requests;
+
+ bool IsActive() override {
+ return true;
+ }
+
+ int64_t OnGetSize(uint64_t inode) override {
+ if (inode == FUSE_ROOT_ID) {
+ return 0;
+ } else {
+ return kTestFileSize;
+ }
+ }
+
+ int32_t OnFsync(uint64_t inode) override {
+ requests.push_back({
+ .code = FUSE_FSYNC,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnWrite(uint64_t inode,
+ uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED,
+ const void* data ATTRIBUTE_UNUSED) override {
+ requests.push_back({
+ .code = FUSE_WRITE,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnRead(uint64_t inode,
+ uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED,
+ void* data ATTRIBUTE_UNUSED) override {
+ requests.push_back({
+ .code = FUSE_READ,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnOpen(uint64_t inode) override {
+ requests.push_back({
+ .code = FUSE_OPEN,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnRelease(uint64_t inode) override {
+ requests.push_back({
+ .code = FUSE_RELEASE,
+ .inode = inode
+ });
+ return 0;
+ }
+};
+
+class FuseAppLoopTest : public ::testing::Test {
+ private:
+ std::thread thread_;
+
+ protected:
+ base::unique_fd sockets_[2];
+ Callback callback_;
+ FuseRequest request_;
+ FuseResponse response_;
+
+ void SetUp() override {
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ int sockets[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets));
+ sockets_[0].reset(sockets[0]);
+ sockets_[1].reset(sockets[1]);
+ thread_ = std::thread([this] {
+ StartFuseAppLoop(sockets_[1].release(), &callback_);
+ });
+ }
+
+ void CheckCallback(
+ size_t data_size, uint32_t code, size_t expected_out_size) {
+ request_.Reset(data_size, code, 1);
+ request_.header.nodeid = 10;
+
+ ASSERT_TRUE(request_.Write(sockets_[0]));
+ ASSERT_TRUE(response_.Read(sockets_[0]));
+
+ Close();
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + expected_out_size,
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ ASSERT_EQ(1u, callback_.requests.size());
+ EXPECT_EQ(code, callback_.requests[0].code);
+ EXPECT_EQ(10u, callback_.requests[0].inode);
+ }
+
+ void Close() {
+ sockets_[0].reset();
+ sockets_[1].reset();
+ if (thread_.joinable()) {
+ thread_.join();
+ }
+ }
+
+ void TearDown() override {
+ Close();
+ }
+};
+
+} // namespace
+
+TEST_F(FuseAppLoopTest, LookUp) {
+ request_.Reset(3u, FUSE_LOOKUP, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+ strcpy(request_.lookup_name, "10");
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_entry_out),
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ EXPECT_EQ(10u, response_.entry_out.nodeid);
+ EXPECT_EQ(0u, response_.entry_out.generation);
+ EXPECT_EQ(10u, response_.entry_out.entry_valid);
+ EXPECT_EQ(10u, response_.entry_out.attr_valid);
+ EXPECT_EQ(0u, response_.entry_out.entry_valid_nsec);
+ EXPECT_EQ(0u, response_.entry_out.attr_valid_nsec);
+
+ EXPECT_EQ(10u, response_.entry_out.attr.ino);
+ EXPECT_EQ(kTestFileSize, response_.entry_out.attr.size);
+ EXPECT_EQ(0u, response_.entry_out.attr.blocks);
+ EXPECT_EQ(0u, response_.entry_out.attr.atime);
+ EXPECT_EQ(0u, response_.entry_out.attr.mtime);
+ EXPECT_EQ(0u, response_.entry_out.attr.ctime);
+ EXPECT_EQ(0u, response_.entry_out.attr.atimensec);
+ EXPECT_EQ(0u, response_.entry_out.attr.mtimensec);
+ EXPECT_EQ(0u, response_.entry_out.attr.ctimensec);
+ EXPECT_EQ(S_IFREG | 0777u, response_.entry_out.attr.mode);
+ EXPECT_EQ(0u, response_.entry_out.attr.nlink);
+ EXPECT_EQ(0u, response_.entry_out.attr.uid);
+ EXPECT_EQ(0u, response_.entry_out.attr.gid);
+ EXPECT_EQ(0u, response_.entry_out.attr.rdev);
+ EXPECT_EQ(0u, response_.entry_out.attr.blksize);
+ EXPECT_EQ(0u, response_.entry_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, LookUp_InvalidName) {
+ request_.Reset(3u, FUSE_LOOKUP, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+ strcpy(request_.lookup_name, "aa");
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
+ EXPECT_EQ(-ENOENT, response_.header.error);
+ EXPECT_EQ(1u, response_.header.unique);
+}
+
+TEST_F(FuseAppLoopTest, LookUp_TooLargeName) {
+ request_.Reset(21u, FUSE_LOOKUP, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+ strcpy(request_.lookup_name, "18446744073709551616");
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
+ EXPECT_EQ(-ENOENT, response_.header.error);
+ EXPECT_EQ(1u, response_.header.unique);
+}
+
+TEST_F(FuseAppLoopTest, GetAttr) {
+ request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
+ request_.header.nodeid = 10;
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ EXPECT_EQ(10u, response_.attr_out.attr_valid);
+ EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
+
+ EXPECT_EQ(10u, response_.attr_out.attr.ino);
+ EXPECT_EQ(kTestFileSize, response_.attr_out.attr.size);
+ EXPECT_EQ(0u, response_.attr_out.attr.blocks);
+ EXPECT_EQ(0u, response_.attr_out.attr.atime);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtime);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctime);
+ EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
+ EXPECT_EQ(S_IFREG | 0777u, response_.attr_out.attr.mode);
+ EXPECT_EQ(0u, response_.attr_out.attr.nlink);
+ EXPECT_EQ(0u, response_.attr_out.attr.uid);
+ EXPECT_EQ(0u, response_.attr_out.attr.gid);
+ EXPECT_EQ(0u, response_.attr_out.attr.rdev);
+ EXPECT_EQ(0u, response_.attr_out.attr.blksize);
+ EXPECT_EQ(0u, response_.attr_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, GetAttr_Root) {
+ request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ EXPECT_EQ(10u, response_.attr_out.attr_valid);
+ EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
+
+ EXPECT_EQ(static_cast<unsigned>(FUSE_ROOT_ID), response_.attr_out.attr.ino);
+ EXPECT_EQ(0u, response_.attr_out.attr.size);
+ EXPECT_EQ(0u, response_.attr_out.attr.blocks);
+ EXPECT_EQ(0u, response_.attr_out.attr.atime);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtime);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctime);
+ EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
+ EXPECT_EQ(S_IFDIR | 0777u, response_.attr_out.attr.mode);
+ EXPECT_EQ(0u, response_.attr_out.attr.nlink);
+ EXPECT_EQ(0u, response_.attr_out.attr.uid);
+ EXPECT_EQ(0u, response_.attr_out.attr.gid);
+ EXPECT_EQ(0u, response_.attr_out.attr.rdev);
+ EXPECT_EQ(0u, response_.attr_out.attr.blksize);
+ EXPECT_EQ(0u, response_.attr_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, Open) {
+ CheckCallback(sizeof(fuse_open_in), FUSE_OPEN, sizeof(fuse_open_out));
+}
+
+TEST_F(FuseAppLoopTest, Fsync) {
+ CheckCallback(0u, FUSE_FSYNC, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Release) {
+ CheckCallback(0u, FUSE_RELEASE, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Read) {
+ CheckCallback(sizeof(fuse_read_in), FUSE_READ, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Write) {
+ CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc
new file mode 100644
index 0000000..e74d9e7
--- /dev/null
+++ b/libappfuse/tests/FuseBridgeLoopTest.cc
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseBridgeLoop.h"
+
+#include <sys/socket.h>
+
+#include <sstream>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace fuse {
+namespace {
+
+class Callback : public FuseBridgeLoopCallback {
+ public:
+ bool mounted;
+ Callback() : mounted(false) {}
+ void OnMount() override {
+ mounted = true;
+ }
+};
+
+class FuseBridgeLoopTest : public ::testing::Test {
+ protected:
+ base::unique_fd dev_sockets_[2];
+ base::unique_fd proxy_sockets_[2];
+ Callback callback_;
+ std::thread thread_;
+
+ FuseRequest request_;
+ FuseResponse response_;
+
+ void SetUp() override {
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ int dev_sockets[2];
+ int proxy_sockets[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets));
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets));
+ dev_sockets_[0].reset(dev_sockets[0]);
+ dev_sockets_[1].reset(dev_sockets[1]);
+ proxy_sockets_[0].reset(proxy_sockets[0]);
+ proxy_sockets_[1].reset(proxy_sockets[1]);
+
+ thread_ = std::thread([this] {
+ StartFuseBridgeLoop(
+ dev_sockets_[1].release(), proxy_sockets_[0].release(), &callback_);
+ });
+ }
+
+ void CheckNotImpl(uint32_t opcode) {
+ SCOPED_TRACE((std::ostringstream() << "opcode: " << opcode).str());
+
+ memset(&request_, 0, sizeof(FuseRequest));
+ request_.header.opcode = opcode;
+ request_.header.len = sizeof(fuse_in_header);
+ ASSERT_TRUE(request_.Write(dev_sockets_[0]));
+
+ memset(&response_, 0, sizeof(FuseResponse));
+ ASSERT_TRUE(response_.Read(dev_sockets_[0]));
+ EXPECT_EQ(-ENOSYS, response_.header.error);
+ }
+
+ void CheckProxy(uint32_t opcode) {
+ SCOPED_TRACE((std::ostringstream() << "opcode: " << opcode).str());
+
+ memset(&request_, 0, sizeof(FuseRequest));
+ request_.header.opcode = opcode;
+ request_.header.unique = opcode; // Use opcode as unique.
+ request_.header.len = sizeof(fuse_in_header);
+ ASSERT_TRUE(request_.Write(dev_sockets_[0]));
+
+ memset(&request_, 0, sizeof(FuseRequest));
+ ASSERT_TRUE(request_.Read(proxy_sockets_[1]));
+ EXPECT_EQ(opcode, request_.header.opcode);
+ EXPECT_EQ(opcode, request_.header.unique);
+
+ memset(&response_, 0, sizeof(FuseResponse));
+ response_.header.len = sizeof(fuse_out_header);
+ response_.header.unique = opcode; // Use opcode as unique.
+ response_.header.error = kFuseSuccess;
+ ASSERT_TRUE(response_.Write(proxy_sockets_[1]));
+
+ memset(&response_, 0, sizeof(FuseResponse));
+ ASSERT_TRUE(response_.Read(dev_sockets_[0]));
+ EXPECT_EQ(opcode, response_.header.unique);
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ }
+
+ void SendInitRequest(uint64_t unique) {
+ memset(&request_, 0, sizeof(FuseRequest));
+ request_.header.opcode = FUSE_INIT;
+ request_.header.unique = unique;
+ request_.header.len = sizeof(fuse_in_header) + sizeof(fuse_init_in);
+ request_.init_in.major = FUSE_KERNEL_VERSION;
+ request_.init_in.minor = FUSE_KERNEL_MINOR_VERSION;
+ ASSERT_TRUE(request_.Write(dev_sockets_[0]));
+ }
+
+ void Close() {
+ dev_sockets_[0].reset();
+ dev_sockets_[1].reset();
+ proxy_sockets_[0].reset();
+ proxy_sockets_[1].reset();
+ if (thread_.joinable()) {
+ thread_.join();
+ }
+ }
+
+ void TearDown() override {
+ Close();
+ }
+};
+
+} // namespace
+
+TEST_F(FuseBridgeLoopTest, FuseInit) {
+ SendInitRequest(1u);
+
+ memset(&response_, 0, sizeof(FuseResponse));
+ ASSERT_TRUE(response_.Read(dev_sockets_[0]));
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ // Unmount.
+ Close();
+ EXPECT_TRUE(callback_.mounted);
+}
+
+TEST_F(FuseBridgeLoopTest, FuseForget) {
+ memset(&request_, 0, sizeof(FuseRequest));
+ request_.header.opcode = FUSE_FORGET;
+ request_.header.unique = 1u;
+ request_.header.len = sizeof(fuse_in_header) + sizeof(fuse_forget_in);
+ ASSERT_TRUE(request_.Write(dev_sockets_[0]));
+
+ SendInitRequest(2u);
+
+ memset(&response_, 0, sizeof(FuseResponse));
+ ASSERT_TRUE(response_.Read(dev_sockets_[0]));
+ EXPECT_EQ(2u, response_.header.unique) <<
+ "The loop must not respond to FUSE_FORGET";
+}
+
+TEST_F(FuseBridgeLoopTest, FuseNotImpl) {
+ CheckNotImpl(FUSE_SETATTR);
+ CheckNotImpl(FUSE_READLINK);
+ CheckNotImpl(FUSE_SYMLINK);
+ CheckNotImpl(FUSE_MKNOD);
+ CheckNotImpl(FUSE_MKDIR);
+ CheckNotImpl(FUSE_UNLINK);
+ CheckNotImpl(FUSE_RMDIR);
+ CheckNotImpl(FUSE_RENAME);
+ CheckNotImpl(FUSE_LINK);
+ CheckNotImpl(FUSE_STATFS);
+ CheckNotImpl(FUSE_SETXATTR);
+ CheckNotImpl(FUSE_GETXATTR);
+ CheckNotImpl(FUSE_LISTXATTR);
+ CheckNotImpl(FUSE_REMOVEXATTR);
+ CheckNotImpl(FUSE_FLUSH);
+ CheckNotImpl(FUSE_OPENDIR);
+ CheckNotImpl(FUSE_READDIR);
+ CheckNotImpl(FUSE_RELEASEDIR);
+ CheckNotImpl(FUSE_FSYNCDIR);
+ CheckNotImpl(FUSE_GETLK);
+ CheckNotImpl(FUSE_SETLK);
+ CheckNotImpl(FUSE_SETLKW);
+ CheckNotImpl(FUSE_ACCESS);
+ CheckNotImpl(FUSE_CREATE);
+ CheckNotImpl(FUSE_INTERRUPT);
+ CheckNotImpl(FUSE_BMAP);
+ CheckNotImpl(FUSE_DESTROY);
+ CheckNotImpl(FUSE_IOCTL);
+ CheckNotImpl(FUSE_POLL);
+ CheckNotImpl(FUSE_NOTIFY_REPLY);
+ CheckNotImpl(FUSE_BATCH_FORGET);
+ CheckNotImpl(FUSE_FALLOCATE);
+ CheckNotImpl(FUSE_READDIRPLUS);
+ CheckNotImpl(FUSE_RENAME2);
+ CheckNotImpl(FUSE_LSEEK);
+}
+
+TEST_F(FuseBridgeLoopTest, Proxy) {
+ CheckProxy(FUSE_LOOKUP);
+ CheckProxy(FUSE_GETATTR);
+ CheckProxy(FUSE_READ);
+ CheckProxy(FUSE_WRITE);
+ CheckProxy(FUSE_FSYNC);
+
+ // Invoke FUSE_OPEN and FUSE_RELEASE at last as the loop will exit when all files are closed.
+ CheckProxy(FUSE_OPEN);
+ CheckProxy(FUSE_RELEASE);
+
+ // Ensure the loop exits.
+ Close();
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
new file mode 100644
index 0000000..db35d33
--- /dev/null
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseBuffer.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <thread>
+
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace fuse {
+
+constexpr char kTempFile[] = "/data/local/tmp/appfuse_test_dump";
+
+void OpenTempFile(android::base::unique_fd* fd) {
+ fd->reset(open(kTempFile, O_CREAT | O_RDWR));
+ ASSERT_NE(-1, *fd) << strerror(errno);
+ unlink(kTempFile);
+ ASSERT_NE(-1, *fd) << strerror(errno);
+}
+
+void TestReadInvalidLength(size_t headerSize, size_t write_size) {
+ android::base::unique_fd fd;
+ OpenTempFile(&fd);
+
+ char buffer[std::max(headerSize, sizeof(FuseRequest))];
+ FuseRequest* const packet = reinterpret_cast<FuseRequest*>(buffer);
+ packet->header.len = headerSize;
+ ASSERT_NE(-1, write(fd, packet, write_size)) << strerror(errno);
+
+ lseek(fd, 0, SEEK_SET);
+ EXPECT_FALSE(packet->Read(fd));
+}
+
+void TestWriteInvalidLength(size_t size) {
+ android::base::unique_fd fd;
+ OpenTempFile(&fd);
+
+ char buffer[std::max(size, sizeof(FuseRequest))];
+ FuseRequest* const packet = reinterpret_cast<FuseRequest*>(buffer);
+ packet->header.len = size;
+ EXPECT_FALSE(packet->Write(fd));
+}
+
+// Use FuseRequest as a template instance of FuseMessage.
+
+TEST(FuseMessageTest, ReadAndWrite) {
+ android::base::unique_fd fd;
+ OpenTempFile(&fd);
+
+ FuseRequest request;
+ request.header.len = sizeof(FuseRequest);
+ request.header.opcode = 1;
+ request.header.unique = 2;
+ request.header.nodeid = 3;
+ request.header.uid = 4;
+ request.header.gid = 5;
+ request.header.pid = 6;
+ strcpy(request.lookup_name, "test");
+
+ ASSERT_TRUE(request.Write(fd));
+
+ memset(&request, 0, sizeof(FuseRequest));
+ lseek(fd, 0, SEEK_SET);
+
+ ASSERT_TRUE(request.Read(fd));
+ EXPECT_EQ(sizeof(FuseRequest), request.header.len);
+ EXPECT_EQ(1u, request.header.opcode);
+ EXPECT_EQ(2u, request.header.unique);
+ EXPECT_EQ(3u, request.header.nodeid);
+ EXPECT_EQ(4u, request.header.uid);
+ EXPECT_EQ(5u, request.header.gid);
+ EXPECT_EQ(6u, request.header.pid);
+ EXPECT_STREQ("test", request.lookup_name);
+}
+
+TEST(FuseMessageTest, Read_InconsistentLength) {
+ TestReadInvalidLength(sizeof(fuse_in_header), sizeof(fuse_in_header) + 1);
+}
+
+TEST(FuseMessageTest, Read_TooLong) {
+ TestReadInvalidLength(sizeof(FuseRequest) + 1, sizeof(FuseRequest) + 1);
+}
+
+TEST(FuseMessageTest, Read_TooShort) {
+ TestReadInvalidLength(sizeof(fuse_in_header) - 1, sizeof(fuse_in_header) - 1);
+}
+
+TEST(FuseMessageTest, Write_TooLong) {
+ TestWriteInvalidLength(sizeof(FuseRequest) + 1);
+}
+
+TEST(FuseMessageTest, Write_TooShort) {
+ TestWriteInvalidLength(sizeof(fuse_in_header) - 1);
+}
+
+TEST(FuseMessageTest, ShortWriteAndRead) {
+ int raw_fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, raw_fds));
+
+ android::base::unique_fd fds[2];
+ fds[0].reset(raw_fds[0]);
+ fds[1].reset(raw_fds[1]);
+
+ const int send_buffer_size = 1024;
+ ASSERT_EQ(0, setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &send_buffer_size,
+ sizeof(int)));
+
+ bool succeed = false;
+ const int sender_fd = fds[0].get();
+ std::thread thread([sender_fd, &succeed] {
+ FuseRequest request;
+ request.header.len = 1024 * 4;
+ succeed = request.Write(sender_fd);
+ });
+ thread.detach();
+ FuseRequest request;
+ ASSERT_TRUE(request.Read(fds[1]));
+}
+
+TEST(FuseResponseTest, Reset) {
+ FuseResponse response;
+ // Write 1 to the first ten bytes.
+ memset(response.read_data, 'a', 10);
+
+ response.Reset(0, -1, 2);
+ EXPECT_EQ(sizeof(fuse_out_header), response.header.len);
+ EXPECT_EQ(-1, response.header.error);
+ EXPECT_EQ(2u, response.header.unique);
+ EXPECT_EQ('a', response.read_data[0]);
+ EXPECT_EQ('a', response.read_data[9]);
+
+ response.Reset(5, -4, 3);
+ EXPECT_EQ(sizeof(fuse_out_header) + 5, response.header.len);
+ EXPECT_EQ(-4, response.header.error);
+ EXPECT_EQ(3u, response.header.unique);
+ EXPECT_EQ(0, response.read_data[0]);
+ EXPECT_EQ(0, response.read_data[1]);
+ EXPECT_EQ(0, response.read_data[2]);
+ EXPECT_EQ(0, response.read_data[3]);
+ EXPECT_EQ(0, response.read_data[4]);
+ EXPECT_EQ('a', response.read_data[5]);
+}
+
+TEST(FuseResponseTest, ResetHeader) {
+ FuseResponse response;
+ // Write 1 to the first ten bytes.
+ memset(response.read_data, 'a', 10);
+
+ response.ResetHeader(0, -1, 2);
+ EXPECT_EQ(sizeof(fuse_out_header), response.header.len);
+ EXPECT_EQ(-1, response.header.error);
+ EXPECT_EQ(2u, response.header.unique);
+ EXPECT_EQ('a', response.read_data[0]);
+ EXPECT_EQ('a', response.read_data[9]);
+
+ response.ResetHeader(5, -4, 3);
+ EXPECT_EQ(sizeof(fuse_out_header) + 5, response.header.len);
+ EXPECT_EQ(-4, response.header.error);
+ EXPECT_EQ(3u, response.header.unique);
+ EXPECT_EQ('a', response.read_data[0]);
+ EXPECT_EQ('a', response.read_data[9]);
+}
+
+TEST(FuseBufferTest, HandleInit) {
+ FuseBuffer buffer;
+ memset(&buffer, 0, sizeof(FuseBuffer));
+
+ buffer.request.header.opcode = FUSE_INIT;
+ buffer.request.init_in.major = FUSE_KERNEL_VERSION;
+ buffer.request.init_in.minor = FUSE_KERNEL_MINOR_VERSION;
+
+ buffer.HandleInit();
+
+ ASSERT_EQ(sizeof(fuse_out_header) + FUSE_COMPAT_22_INIT_OUT_SIZE,
+ buffer.response.header.len);
+ EXPECT_EQ(kFuseSuccess, buffer.response.header.error);
+ EXPECT_EQ(static_cast<unsigned int>(FUSE_KERNEL_VERSION),
+ buffer.response.init_out.major);
+ EXPECT_EQ(15u, buffer.response.init_out.minor);
+ EXPECT_EQ(static_cast<unsigned int>(FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES),
+ buffer.response.init_out.flags);
+ EXPECT_EQ(kFuseMaxWrite, buffer.response.init_out.max_write);
+}
+
+TEST(FuseBufferTest, HandleNotImpl) {
+ FuseBuffer buffer;
+ memset(&buffer, 0, sizeof(FuseBuffer));
+
+ buffer.HandleNotImpl();
+
+ ASSERT_EQ(sizeof(fuse_out_header), buffer.response.header.len);
+ EXPECT_EQ(-ENOSYS, buffer.response.header.error);
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
new file mode 100644
index 0000000..5b31ecb
--- /dev/null
+++ b/libbacktrace/Android.bp
@@ -0,0 +1,210 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "libbacktrace_common",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
+ clang_cflags: ["-Wno-inline-asm"],
+
+ include_dirs: ["external/libunwind/include/tdep"],
+
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ }
+}
+
+libbacktrace_sources = [
+ "Backtrace.cpp",
+ "BacktraceCurrent.cpp",
+ "BacktracePtrace.cpp",
+ "thread_utils.c",
+ "ThreadEntry.cpp",
+ "UnwindCurrent.cpp",
+ "UnwindMap.cpp",
+ "UnwindPtrace.cpp",
+]
+
+cc_library {
+ name: "libbacktrace",
+ defaults: ["libbacktrace_common"],
+ host_supported: true,
+
+ srcs: [
+ "BacktraceMap.cpp",
+ ],
+
+ target: {
+ darwin: {
+ enabled: true,
+ },
+ linux: {
+ srcs: libbacktrace_sources,
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libunwind",
+ ],
+
+ static_libs: ["libcutils"],
+ host_ldlibs: ["-lrt"],
+ },
+ android: {
+ srcs: libbacktrace_sources,
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libunwind",
+ ],
+
+ static_libs: ["libcutils"],
+ },
+ },
+}
+
+cc_library_shared {
+ name: "libbacktrace_test",
+ defaults: ["libbacktrace_common"],
+ host_supported: true,
+ strip: {
+ none: true,
+ },
+ cflags: ["-O0"],
+ srcs: ["backtrace_testlib.c"],
+
+ target: {
+ linux: {
+ shared_libs: [
+ "libunwind",
+ ],
+ },
+ android: {
+ shared_libs: [
+ "libunwind",
+ ],
+ },
+ }
+}
+
+//-------------------------------------------------------------------------
+// The libbacktrace_offline static library.
+//-------------------------------------------------------------------------
+cc_library_static {
+ name: "libbacktrace_offline",
+ defaults: ["libbacktrace_common"],
+ host_supported: true,
+ srcs: ["BacktraceOffline.cpp"],
+
+ cflags: [
+ "-D__STDC_CONSTANT_MACROS",
+ "-D__STDC_LIMIT_MACROS",
+ "-D__STDC_FORMAT_MACROS",
+ ],
+
+ header_libs: ["llvm-headers"],
+
+ // Use shared libraries so their headers get included during build.
+ shared_libs = [
+ "libbase",
+ "libunwind",
+ ],
+}
+
+//-------------------------------------------------------------------------
+// The backtrace_test executable.
+//-------------------------------------------------------------------------
+cc_test {
+ name: "backtrace_test",
+ defaults: ["libbacktrace_common"],
+ host_supported: true,
+ srcs: [
+ "backtrace_offline_test.cpp",
+ "backtrace_test.cpp",
+ "GetPss.cpp",
+ "thread_utils.c",
+ ],
+
+ cflags: [
+ "-fno-builtin",
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libbacktrace_test",
+ "libbacktrace",
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libunwind",
+ ],
+
+ group_static_libs: true,
+
+ // Statically link LLVMlibraries to remove dependency on llvm shared library.
+ static_libs = [
+ "libbacktrace_offline",
+ "libLLVMObject",
+ "libLLVMBitReader",
+ "libLLVMMC",
+ "libLLVMMCParser",
+ "libLLVMCore",
+ "libLLVMSupport",
+
+ "libziparchive",
+ "libz",
+ ],
+
+ header_libs: ["llvm-headers"],
+
+ target: {
+ android: {
+ cflags: ["-DENABLE_PSS_TESTS"],
+ shared_libs: [
+ "libdl",
+ "libutils",
+ ],
+ },
+ linux: {
+ host_ldlibs: [
+ "-lpthread",
+ "-lrt",
+ "-ldl",
+ "-lncurses",
+ ],
+ static_libs: ["libutils"],
+ },
+ },
+}
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
deleted file mode 100644
index 4983b55..0000000
--- a/libbacktrace/Android.build.mk
+++ /dev/null
@@ -1,82 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := $(module)
-LOCAL_MODULE_TAGS := $(module_tag)
-LOCAL_MULTILIB := $($(module)_multilib)
-ifeq ($(LOCAL_MULTILIB),both)
-ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRARY))
- LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
- LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-endif
-endif
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(LOCAL_PATH)/Android.mk \
- $(LOCAL_PATH)/Android.build.mk \
-
-LOCAL_CFLAGS := \
- $(libbacktrace_common_cflags) \
- $($(module)_cflags) \
- $($(module)_cflags_$(build_type)) \
-
-LOCAL_CLANG_CFLAGS += \
- $(libbacktrace_common_clang_cflags) \
-
-LOCAL_CONLYFLAGS += \
- $(libbacktrace_common_conlyflags) \
- $($(module)_conlyflags) \
- $($(module)_conlyflags_$(build_type)) \
-
-LOCAL_CPPFLAGS += \
- $(libbacktrace_common_cppflags) \
- $($(module)_cppflags) \
- $($(module)_cppflags_$(build_type)) \
-
-LOCAL_C_INCLUDES := \
- $(libbacktrace_common_c_includes) \
- $($(module)_c_includes) \
- $($(module)_c_includes_$(build_type)) \
-
-LOCAL_SRC_FILES := \
- $($(module)_src_files) \
- $($(module)_src_files_$(build_type)) \
-
-LOCAL_STATIC_LIBRARIES := \
- $($(module)_static_libraries) \
- $($(module)_static_libraries_$(build_type)) \
-
-LOCAL_SHARED_LIBRARIES := \
- $($(module)_shared_libraries) \
- $($(module)_shared_libraries_$(build_type)) \
-
-LOCAL_LDLIBS := \
- $($(module)_ldlibs) \
- $($(module)_ldlibs_$(build_type)) \
-
-ifeq ($(build_type),target)
- include $(BUILD_$(build_target))
-endif
-
-ifeq ($(build_type),host)
- # Only build if host builds are supported.
- ifeq ($(build_host),true)
- LOCAL_CFLAGS += -Wno-extern-c-compat -fno-omit-frame-pointer
- include $(BUILD_HOST_$(build_target))
- endif
-endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
deleted file mode 100644
index 395d677..0000000
--- a/libbacktrace/Android.mk
+++ /dev/null
@@ -1,156 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-libbacktrace_common_cflags := \
- -Wall \
- -Werror \
-
-libbacktrace_common_conlyflags := \
- -std=gnu99 \
-
-libbacktrace_common_cppflags := \
- -std=gnu++11 \
-
-# The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
-libbacktrace_common_clang_cflags += \
- -Wno-inline-asm
-
-build_host := false
-ifeq ($(HOST_OS),linux)
-ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
-build_host := true
-endif
-endif
-
-#-------------------------------------------------------------------------
-# The libbacktrace library.
-#-------------------------------------------------------------------------
-libbacktrace_src_files := \
- Backtrace.cpp \
- BacktraceCurrent.cpp \
- BacktraceMap.cpp \
- BacktracePtrace.cpp \
- thread_utils.c \
- ThreadEntry.cpp \
- UnwindCurrent.cpp \
- UnwindMap.cpp \
- UnwindPtrace.cpp \
-
-libbacktrace_shared_libraries := \
- libbase \
- liblog \
- libunwind \
-
-libbacktrace_ldlibs_host := \
- -lpthread \
- -lrt \
-
-module := libbacktrace
-module_tag := optional
-build_type := target
-build_target := SHARED_LIBRARY
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-libbacktrace_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-libbacktrace_static_libraries := \
- libbase \
- liblog \
- libunwind \
-
-build_target := STATIC_LIBRARY
-include $(LOCAL_PATH)/Android.build.mk
-libbacktrace_static_libraries :=
-
-#-------------------------------------------------------------------------
-# The libbacktrace_test library needed by backtrace_test.
-#-------------------------------------------------------------------------
-libbacktrace_test_cflags := \
- -O0 \
-
-libbacktrace_test_src_files := \
- backtrace_testlib.c \
-
-module := libbacktrace_test
-module_tag := debug
-build_type := target
-build_target := SHARED_LIBRARY
-libbacktrace_test_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
-
-#-------------------------------------------------------------------------
-# The backtrace_test executable.
-#-------------------------------------------------------------------------
-backtrace_test_cflags := \
- -fno-builtin \
- -O0 \
- -g \
-
-backtrace_test_cflags_target := \
- -DENABLE_PSS_TESTS \
-
-backtrace_test_src_files := \
- backtrace_test.cpp \
- GetPss.cpp \
- thread_utils.c \
-
-backtrace_test_ldlibs_host := \
- -lpthread \
- -lrt \
-
-backtrace_test_shared_libraries := \
- libbacktrace_test \
- libbacktrace \
- libbase \
- libcutils \
-
-backtrace_test_shared_libraries_target += \
- libdl \
-
-backtrace_test_ldlibs_host += \
- -ldl \
-
-module := backtrace_test
-module_tag := debug
-build_type := target
-build_target := NATIVE_TEST
-backtrace_test_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
-
-#----------------------------------------------------------------------------
-# Special truncated libbacktrace library for mac.
-#----------------------------------------------------------------------------
-ifeq ($(HOST_OS),darwin)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbacktrace
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := \
- BacktraceMap.cpp \
-
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-endif # HOST_OS-darwin
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 128bb04..0d2e11b 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -22,13 +22,11 @@
#include <string>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <cutils/threads.h>
-
#include "BacktraceLog.h"
#include "thread_utils.h"
#include "UnwindCurrent.h"
@@ -54,24 +52,8 @@
}
}
-extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len,
- int* status);
-
std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
std::string func_name = GetFunctionNameRaw(pc, offset);
- if (!func_name.empty()) {
-#if defined(__APPLE__)
- // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
- if (func_name[0] != '_') {
- return func_name;
- }
-#endif
- char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0);
- if (name) {
- func_name = name;
- free(name);
- }
- }
return func_name;
}
@@ -92,16 +74,31 @@
}
std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
- const char* map_name;
- if (BacktraceMap::IsValid(frame->map) && !frame->map.name.empty()) {
- map_name = frame->map.name.c_str();
+ uintptr_t relative_pc;
+ std::string map_name;
+ if (BacktraceMap::IsValid(frame->map)) {
+ relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
+ if (!frame->map.name.empty()) {
+ map_name = frame->map.name.c_str();
+ if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
+ map_name.resize(map_name.size() - 1);
+ map_name += StringPrintf(":%" PRIPTR "]", frame->map.start);
+ }
+ } else {
+ map_name = StringPrintf("<anonymous:%" PRIPTR ">", frame->map.start);
+ }
} else {
map_name = "<unknown>";
+ relative_pc = frame->pc;
}
- uintptr_t relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
-
- std::string line(StringPrintf("#%02zu pc %" PRIPTR " %s", frame->num, relative_pc, map_name));
+ std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, relative_pc));
+ line += map_name;
+ // Special handling for non-zero offset maps, we need to print that
+ // information.
+ if (frame->map.offset != 0) {
+ line += " (offset " + StringPrintf("0x%" PRIxPTR, frame->map.offset) + ")";
+ }
if (!frame->func_name.empty()) {
line += " (" + frame->func_name;
if (frame->func_offset) {
@@ -135,3 +132,24 @@
return new UnwindPtrace(pid, tid, map);
}
}
+
+std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
+ switch (error) {
+ case BACKTRACE_UNWIND_NO_ERROR:
+ return "No error";
+ case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
+ return "Setup failed";
+ case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
+ return "No map found";
+ case BACKTRACE_UNWIND_ERROR_INTERNAL:
+ return "Internal libbacktrace error, please submit a bugreport";
+ case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
+ return "Thread doesn't exist";
+ case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
+ return "Thread has not repsonded to signal in time";
+ case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
+ return "Attempt to use an unsupported feature";
+ case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
+ return "Attempt to do an offline unwind without a context";
+ }
+}
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 2714d93..5173e2c 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -24,13 +24,13 @@
#include <ucontext.h>
#include <unistd.h>
+#include <stdlib.h>
+
#include <string>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <cutils/threads.h>
-
#include "BacktraceCurrent.h"
#include "BacktraceLog.h"
#include "ThreadEntry.h"
@@ -67,9 +67,11 @@
bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
if (GetMap() == nullptr) {
// Without a map object, we can't do anything.
+ error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
return false;
}
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
if (ucontext) {
return UnwindFromContext(num_ignore_frames, ucontext);
}
@@ -93,6 +95,10 @@
static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
+static void SignalLogOnly(int, siginfo_t*, void*) {
+ BACK_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(), THREAD_SIGNAL);
+}
+
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
if (!entry) {
@@ -136,11 +142,19 @@
BACK_LOGE("sigaction failed: %s", strerror(errno));
ThreadEntry::Remove(entry);
pthread_mutex_unlock(&g_sigaction_mutex);
+ error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
return false;
}
if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
- BACK_LOGE("tgkill %d failed: %s", Tid(), strerror(errno));
+ // Do not emit an error message, this might be expected. Set the
+ // error and let the caller decide.
+ if (errno == ESRCH) {
+ error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+ } else {
+ error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
+ }
+
sigaction(THREAD_SIGNAL, &oldact, nullptr);
ThreadEntry::Remove(entry);
pthread_mutex_unlock(&g_sigaction_mutex);
@@ -151,9 +165,21 @@
// that we are waiting for the first Wake() call made by the thread.
bool wait_completed = entry->Wait(1);
+ if (!wait_completed && oldact.sa_sigaction == nullptr) {
+ // If the wait failed, it could be that the signal could not be delivered
+ // within the timeout. Add a signal handler that's simply going to log
+ // something so that we don't crash if the signal eventually gets
+ // delivered. Only do this if there isn't already an action set up.
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalLogOnly;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ sigemptyset(&act.sa_mask);
+ sigaction(THREAD_SIGNAL, &act, nullptr);
+ } else {
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ }
// After the thread has received the signal, allow other unwinders to
// continue.
- sigaction(THREAD_SIGNAL, &oldact, nullptr);
pthread_mutex_unlock(&g_sigaction_mutex);
bool unwind_done = false;
@@ -169,7 +195,13 @@
BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
}
} else {
- BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+ // Check to see if the thread has disappeared.
+ if (tgkill(Pid(), Tid(), 0) == -1 && errno == ESRCH) {
+ error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+ } else {
+ error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
+ BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+ }
}
ThreadEntry::Remove(entry);
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index ca47f67..0e31495 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -14,14 +14,18 @@
* limitations under the License.
*/
+#define LOG_TAG "backtrace-map"
+
#include <ctype.h>
+#include <inttypes.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
+#include <log/log.h>
+
#include <backtrace/backtrace_constants.h>
#include <backtrace/BacktraceMap.h>
-#include <log/log.h>
#include "thread_utils.h"
@@ -35,8 +39,8 @@
}
void BacktraceMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
- for (BacktraceMap::const_iterator it = begin();
- it != end(); ++it) {
+ ScopedBacktraceMapIteratorLock lock(this);
+ for (BacktraceMap::const_iterator it = begin(); it != end(); ++it) {
if (addr >= it->start && addr < it->end) {
*map = *it;
return;
@@ -46,8 +50,8 @@
}
bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
- unsigned long int start;
- unsigned long int end;
+ uint64_t start;
+ uint64_t end;
char permissions[5];
int name_pos;
@@ -56,14 +60,14 @@
// __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 %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
+ 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, "%lx-%lx %4s %*x %*x:%*x %*d%n",
+ if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %*x %*x:%*x %*d %n",
&start, &end, permissions, &name_pos) != 3) {
#endif
return false;
@@ -82,9 +86,6 @@
map->flags |= PROT_EXEC;
}
- while (isspace(line[name_pos])) {
- name_pos += 1;
- }
map->name = line+name_pos;
if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
map->name.erase(map->name.length()-1);
@@ -144,3 +145,13 @@
return map;
}
#endif
+
+BacktraceMap* BacktraceMap::Create(pid_t pid, const std::vector<backtrace_map_t>& maps) {
+ BacktraceMap* backtrace_map = new BacktraceMap(pid);
+ backtrace_map->maps_.insert(backtrace_map->maps_.begin(), maps.begin(), maps.end());
+ std::sort(backtrace_map->maps_.begin(), backtrace_map->maps_.end(),
+ [](const backtrace_map_t& map1, const backtrace_map_t& map2) {
+ return map1.start < map2.start;
+ });
+ return backtrace_map;
+}
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
new file mode 100644
index 0000000..5e54328
--- /dev/null
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -0,0 +1,866 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BacktraceOffline.h"
+
+extern "C" {
+#define UNW_REMOTE_ONLY
+#include <dwarf.h>
+}
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#include <ziparchive/zip_archive.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+#include <llvm/ADT/StringRef.h>
+#include <llvm/Object/Binary.h>
+#include <llvm/Object/ELFObjectFile.h>
+#include <llvm/Object/ObjectFile.h>
+
+#pragma clang diagnostic pop
+
+#include "BacktraceLog.h"
+
+struct EhFrame {
+ uint64_t hdr_vaddr;
+ uint64_t vaddr;
+ uint64_t fde_table_offset;
+ uintptr_t min_func_vaddr;
+ std::vector<uint8_t> hdr_data;
+ std::vector<uint8_t> data;
+};
+
+struct ArmIdxEntry {
+ uint32_t func_offset;
+ uint32_t value;
+};
+
+struct ArmExidx {
+ uint64_t exidx_vaddr;
+ uint64_t extab_vaddr;
+ std::vector<ArmIdxEntry> exidx_data;
+ std::vector<uint8_t> extab_data;
+ // There is a one-to-one map from exidx_data.func_offset to func_vaddr_array.
+ std::vector<uint32_t> func_vaddr_array;
+};
+
+struct DebugFrameInfo {
+ bool has_arm_exidx;
+ bool has_eh_frame;
+ bool has_debug_frame;
+ bool has_gnu_debugdata;
+
+ EhFrame eh_frame;
+ ArmExidx arm_exidx;
+
+ uint64_t min_vaddr;
+ uint64_t text_end_vaddr;
+
+ DebugFrameInfo() : has_arm_exidx(false), has_eh_frame(false),
+ has_debug_frame(false), has_gnu_debugdata(false) { }
+};
+
+static std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>& g_debug_frames =
+ *new std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>;
+
+void Space::Clear() {
+ start = 0;
+ end = 0;
+ data = nullptr;
+}
+
+size_t Space::Read(uint64_t addr, uint8_t* buffer, size_t size) {
+ if (addr >= start && addr < end) {
+ size_t read_size = std::min(size, static_cast<size_t>(end - addr));
+ memcpy(buffer, data + (addr - start), read_size);
+ return read_size;
+ }
+ return 0;
+}
+
+static int FindProcInfo(unw_addr_space_t addr_space, unw_word_t ip, unw_proc_info* proc_info,
+ int need_unwind_info, void* arg) {
+ BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
+ bool result = backtrace->FindProcInfo(addr_space, ip, proc_info, need_unwind_info);
+ return result ? 0 : -UNW_EINVAL;
+}
+
+static void PutUnwindInfo(unw_addr_space_t, unw_proc_info_t*, void*) {
+}
+
+static int GetDynInfoListAddr(unw_addr_space_t, unw_word_t*, void*) {
+ return -UNW_ENOINFO;
+}
+
+static int AccessMem(unw_addr_space_t, unw_word_t addr, unw_word_t* value, int write, void* arg) {
+ if (write == 1) {
+ return -UNW_EINVAL;
+ }
+ BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
+ *value = 0;
+ size_t read_size = backtrace->Read(addr, reinterpret_cast<uint8_t*>(value), sizeof(unw_word_t));
+ // Strictly we should check if read_size matches sizeof(unw_word_t), but it is possible in
+ // .eh_frame_hdr that the section can end at a position not aligned in sizeof(unw_word_t), and
+ // we should permit the read at the end of the section.
+ return (read_size > 0u ? 0 : -UNW_EINVAL);
+}
+
+static int AccessReg(unw_addr_space_t, unw_regnum_t unwind_reg, unw_word_t* value, int write,
+ void* arg) {
+ if (write == 1) {
+ return -UNW_EINVAL;
+ }
+ BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
+ uint64_t reg_value;
+ bool result = backtrace->ReadReg(unwind_reg, ®_value);
+ if (result) {
+ *value = static_cast<unw_word_t>(reg_value);
+ }
+ return result ? 0 : -UNW_EINVAL;
+}
+
+static int AccessFpReg(unw_addr_space_t, unw_regnum_t, unw_fpreg_t*, int, void*) {
+ return -UNW_EINVAL;
+}
+
+static int Resume(unw_addr_space_t, unw_cursor_t*, void*) {
+ return -UNW_EINVAL;
+}
+
+static int GetProcName(unw_addr_space_t, unw_word_t, char*, size_t, unw_word_t*, void*) {
+ return -UNW_EINVAL;
+}
+
+static unw_accessors_t accessors = {
+ .find_proc_info = FindProcInfo,
+ .put_unwind_info = PutUnwindInfo,
+ .get_dyn_info_list_addr = GetDynInfoListAddr,
+ .access_mem = AccessMem,
+ .access_reg = AccessReg,
+ .access_fpreg = AccessFpReg,
+ .resume = Resume,
+ .get_proc_name = GetProcName,
+};
+
+bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) {
+ if (context == nullptr) {
+ BACK_LOGW("The context is needed for offline backtracing.");
+ error_ = BACKTRACE_UNWIND_ERROR_NO_CONTEXT;
+ return false;
+ }
+ context_ = context;
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
+
+ unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0);
+ unw_cursor_t cursor;
+ int ret = unw_init_remote(&cursor, addr_space, this);
+ if (ret != 0) {
+ BACK_LOGW("unw_init_remote failed %d", ret);
+ unw_destroy_addr_space(addr_space);
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+ return false;
+ }
+ size_t num_frames = 0;
+ do {
+ unw_word_t pc;
+ ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
+ if (ret < 0) {
+ BACK_LOGW("Failed to read IP %d", ret);
+ break;
+ }
+ unw_word_t sp;
+ ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
+ if (ret < 0) {
+ BACK_LOGW("Failed to read SP %d", ret);
+ break;
+ }
+
+ if (num_ignore_frames == 0) {
+ frames_.resize(num_frames + 1);
+ backtrace_frame_data_t* frame = &frames_[num_frames];
+ frame->num = num_frames;
+ frame->pc = static_cast<uintptr_t>(pc);
+ frame->sp = static_cast<uintptr_t>(sp);
+ frame->stack_size = 0;
+
+ if (num_frames > 0) {
+ backtrace_frame_data_t* prev = &frames_[num_frames - 1];
+ prev->stack_size = frame->sp - prev->sp;
+ }
+ frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+ FillInMap(frame->pc, &frame->map);
+ num_frames++;
+ } else {
+ num_ignore_frames--;
+ }
+ ret = unw_step(&cursor);
+ } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
+
+ unw_destroy_addr_space(addr_space);
+ context_ = nullptr;
+ return true;
+}
+
+bool BacktraceOffline::ReadWord(uintptr_t ptr, word_t* out_value) {
+ size_t bytes_read = Read(ptr, reinterpret_cast<uint8_t*>(out_value), sizeof(word_t));
+ return bytes_read == sizeof(word_t);
+}
+
+size_t BacktraceOffline::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+ // Normally, libunwind needs stack information and call frame information to do remote unwinding.
+ // If call frame information is stored in .debug_frame, libunwind can read it from file
+ // by itself. If call frame information is stored in .eh_frame, we need to provide data in
+ // .eh_frame/.eh_frame_hdr sections.
+ // The order of readings below doesn't matter, as the spaces don't overlap with each other.
+ size_t read_size = eh_frame_hdr_space_.Read(addr, buffer, bytes);
+ if (read_size != 0) {
+ return read_size;
+ }
+ read_size = eh_frame_space_.Read(addr, buffer, bytes);
+ if (read_size != 0) {
+ return read_size;
+ }
+ read_size = arm_exidx_space_.Read(addr, buffer, bytes);
+ if (read_size != 0) {
+ return read_size;
+ }
+ read_size = arm_extab_space_.Read(addr, buffer, bytes);
+ if (read_size != 0) {
+ return read_size;
+ }
+ read_size = stack_space_.Read(addr, buffer, bytes);
+ return read_size;
+}
+
+bool BacktraceOffline::FindProcInfo(unw_addr_space_t addr_space, uint64_t ip,
+ unw_proc_info_t* proc_info, int need_unwind_info) {
+ backtrace_map_t map;
+ FillInMap(ip, &map);
+ if (!BacktraceMap::IsValid(map)) {
+ return false;
+ }
+ const std::string& filename = map.name;
+ DebugFrameInfo* debug_frame = GetDebugFrameInFile(filename);
+ if (debug_frame == nullptr) {
+ return false;
+ }
+
+ eh_frame_hdr_space_.Clear();
+ eh_frame_space_.Clear();
+ arm_exidx_space_.Clear();
+ arm_extab_space_.Clear();
+
+ // vaddr in the elf file.
+ uint64_t ip_vaddr = ip - map.start + debug_frame->min_vaddr;
+ if (debug_frame->has_arm_exidx) {
+ auto& func_vaddrs = debug_frame->arm_exidx.func_vaddr_array;
+ if (ip_vaddr >= func_vaddrs[0] && ip_vaddr < debug_frame->text_end_vaddr) {
+ // Use binary search to find the correct function.
+ auto it = std::upper_bound(func_vaddrs.begin(), func_vaddrs.end(),
+ static_cast<uint32_t>(ip_vaddr));
+ if (it != func_vaddrs.begin()) {
+ --it;
+ // Found the exidx entry.
+ size_t index = it - func_vaddrs.begin();
+
+ proc_info->format = UNW_INFO_FORMAT_ARM_EXIDX;
+ proc_info->unwind_info = reinterpret_cast<void*>(
+ static_cast<uintptr_t>(index * sizeof(ArmIdxEntry) +
+ debug_frame->arm_exidx.exidx_vaddr +
+ debug_frame->min_vaddr));
+
+ // Prepare arm_exidx space and arm_extab space.
+ arm_exidx_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.exidx_vaddr;
+ arm_exidx_space_.end = arm_exidx_space_.start +
+ debug_frame->arm_exidx.exidx_data.size() * sizeof(ArmIdxEntry);
+ arm_exidx_space_.data = reinterpret_cast<const uint8_t*>(
+ debug_frame->arm_exidx.exidx_data.data());
+
+ arm_extab_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.extab_vaddr;
+ arm_extab_space_.end = arm_extab_space_.start +
+ debug_frame->arm_exidx.extab_data.size();
+ arm_extab_space_.data = debug_frame->arm_exidx.extab_data.data();
+ return true;
+ }
+ }
+ }
+
+ if (debug_frame->has_eh_frame) {
+ if (ip_vaddr >= debug_frame->eh_frame.min_func_vaddr &&
+ ip_vaddr < debug_frame->text_end_vaddr) {
+ // Prepare eh_frame_hdr space and eh_frame space.
+ eh_frame_hdr_space_.start = ip - ip_vaddr + debug_frame->eh_frame.hdr_vaddr;
+ eh_frame_hdr_space_.end =
+ eh_frame_hdr_space_.start + debug_frame->eh_frame.hdr_data.size();
+ eh_frame_hdr_space_.data = debug_frame->eh_frame.hdr_data.data();
+
+ eh_frame_space_.start = ip - ip_vaddr + debug_frame->eh_frame.vaddr;
+ eh_frame_space_.end = eh_frame_space_.start + debug_frame->eh_frame.data.size();
+ eh_frame_space_.data = debug_frame->eh_frame.data.data();
+
+ unw_dyn_info di;
+ memset(&di, '\0', sizeof(di));
+ di.start_ip = map.start;
+ di.end_ip = map.end;
+ di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
+ di.u.rti.name_ptr = 0;
+ di.u.rti.segbase = eh_frame_hdr_space_.start;
+ di.u.rti.table_data =
+ eh_frame_hdr_space_.start + debug_frame->eh_frame.fde_table_offset;
+ di.u.rti.table_len = (eh_frame_hdr_space_.end - di.u.rti.table_data) / sizeof(unw_word_t);
+ // TODO: Do it ourselves is more efficient than calling this function.
+ int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
+ if (ret == 0) {
+ return true;
+ }
+ }
+ }
+
+ if (debug_frame->has_debug_frame || debug_frame->has_gnu_debugdata) {
+ unw_dyn_info_t di;
+ unw_word_t segbase = map.start - map.offset;
+ // TODO: http://b/32916571
+ // TODO: Do it ourselves is more efficient than calling libunwind functions.
+ int found = dwarf_find_debug_frame(0, &di, ip, segbase, filename.c_str(), map.start, map.end);
+ if (found == 1) {
+ int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
+ if (ret == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool BacktraceOffline::ReadReg(size_t reg, uint64_t* value) {
+ bool result = true;
+#if defined(__arm__)
+ switch (reg) {
+ case UNW_ARM_R0:
+ *value = context_->uc_mcontext.arm_r0;
+ break;
+ case UNW_ARM_R1:
+ *value = context_->uc_mcontext.arm_r1;
+ break;
+ case UNW_ARM_R2:
+ *value = context_->uc_mcontext.arm_r2;
+ break;
+ case UNW_ARM_R3:
+ *value = context_->uc_mcontext.arm_r3;
+ break;
+ case UNW_ARM_R4:
+ *value = context_->uc_mcontext.arm_r4;
+ break;
+ case UNW_ARM_R5:
+ *value = context_->uc_mcontext.arm_r5;
+ break;
+ case UNW_ARM_R6:
+ *value = context_->uc_mcontext.arm_r6;
+ break;
+ case UNW_ARM_R7:
+ *value = context_->uc_mcontext.arm_r7;
+ break;
+ case UNW_ARM_R8:
+ *value = context_->uc_mcontext.arm_r8;
+ break;
+ case UNW_ARM_R9:
+ *value = context_->uc_mcontext.arm_r9;
+ break;
+ case UNW_ARM_R10:
+ *value = context_->uc_mcontext.arm_r10;
+ break;
+ case UNW_ARM_R11:
+ *value = context_->uc_mcontext.arm_fp;
+ break;
+ case UNW_ARM_R12:
+ *value = context_->uc_mcontext.arm_ip;
+ break;
+ case UNW_ARM_R13:
+ *value = context_->uc_mcontext.arm_sp;
+ break;
+ case UNW_ARM_R14:
+ *value = context_->uc_mcontext.arm_lr;
+ break;
+ case UNW_ARM_R15:
+ *value = context_->uc_mcontext.arm_pc;
+ break;
+ default:
+ result = false;
+ }
+#elif defined(__aarch64__)
+ if (reg <= UNW_AARCH64_PC) {
+ *value = context_->uc_mcontext.regs[reg];
+ } else {
+ result = false;
+ }
+#elif defined(__x86_64__)
+ switch (reg) {
+ case UNW_X86_64_R8:
+ *value = context_->uc_mcontext.gregs[REG_R8];
+ break;
+ case UNW_X86_64_R9:
+ *value = context_->uc_mcontext.gregs[REG_R9];
+ break;
+ case UNW_X86_64_R10:
+ *value = context_->uc_mcontext.gregs[REG_R10];
+ break;
+ case UNW_X86_64_R11:
+ *value = context_->uc_mcontext.gregs[REG_R11];
+ break;
+ case UNW_X86_64_R12:
+ *value = context_->uc_mcontext.gregs[REG_R12];
+ break;
+ case UNW_X86_64_R13:
+ *value = context_->uc_mcontext.gregs[REG_R13];
+ break;
+ case UNW_X86_64_R14:
+ *value = context_->uc_mcontext.gregs[REG_R14];
+ break;
+ case UNW_X86_64_R15:
+ *value = context_->uc_mcontext.gregs[REG_R15];
+ break;
+ case UNW_X86_64_RDI:
+ *value = context_->uc_mcontext.gregs[REG_RDI];
+ break;
+ case UNW_X86_64_RSI:
+ *value = context_->uc_mcontext.gregs[REG_RSI];
+ break;
+ case UNW_X86_64_RBP:
+ *value = context_->uc_mcontext.gregs[REG_RBP];
+ break;
+ case UNW_X86_64_RBX:
+ *value = context_->uc_mcontext.gregs[REG_RBX];
+ break;
+ case UNW_X86_64_RDX:
+ *value = context_->uc_mcontext.gregs[REG_RDX];
+ break;
+ case UNW_X86_64_RAX:
+ *value = context_->uc_mcontext.gregs[REG_RAX];
+ break;
+ case UNW_X86_64_RCX:
+ *value = context_->uc_mcontext.gregs[REG_RCX];
+ break;
+ case UNW_X86_64_RSP:
+ *value = context_->uc_mcontext.gregs[REG_RSP];
+ break;
+ case UNW_X86_64_RIP:
+ *value = context_->uc_mcontext.gregs[REG_RIP];
+ break;
+ default:
+ result = false;
+ }
+#elif defined(__i386__)
+ switch (reg) {
+ case UNW_X86_GS:
+ *value = context_->uc_mcontext.gregs[REG_GS];
+ break;
+ case UNW_X86_FS:
+ *value = context_->uc_mcontext.gregs[REG_FS];
+ break;
+ case UNW_X86_ES:
+ *value = context_->uc_mcontext.gregs[REG_ES];
+ break;
+ case UNW_X86_DS:
+ *value = context_->uc_mcontext.gregs[REG_DS];
+ break;
+ case UNW_X86_EAX:
+ *value = context_->uc_mcontext.gregs[REG_EAX];
+ break;
+ case UNW_X86_EBX:
+ *value = context_->uc_mcontext.gregs[REG_EBX];
+ break;
+ case UNW_X86_ECX:
+ *value = context_->uc_mcontext.gregs[REG_ECX];
+ break;
+ case UNW_X86_EDX:
+ *value = context_->uc_mcontext.gregs[REG_EDX];
+ break;
+ case UNW_X86_ESI:
+ *value = context_->uc_mcontext.gregs[REG_ESI];
+ break;
+ case UNW_X86_EDI:
+ *value = context_->uc_mcontext.gregs[REG_EDI];
+ break;
+ case UNW_X86_EBP:
+ *value = context_->uc_mcontext.gregs[REG_EBP];
+ break;
+ case UNW_X86_EIP:
+ *value = context_->uc_mcontext.gregs[REG_EIP];
+ break;
+ case UNW_X86_ESP:
+ *value = context_->uc_mcontext.gregs[REG_ESP];
+ break;
+ case UNW_X86_TRAPNO:
+ *value = context_->uc_mcontext.gregs[REG_TRAPNO];
+ break;
+ case UNW_X86_CS:
+ *value = context_->uc_mcontext.gregs[REG_CS];
+ break;
+ case UNW_X86_EFLAGS:
+ *value = context_->uc_mcontext.gregs[REG_EFL];
+ break;
+ case UNW_X86_SS:
+ *value = context_->uc_mcontext.gregs[REG_SS];
+ break;
+ default:
+ result = false;
+ }
+#else
+ UNUSED(reg);
+ UNUSED(value);
+ result = false;
+#endif
+ return result;
+}
+
+std::string BacktraceOffline::GetFunctionNameRaw(uintptr_t, uintptr_t* offset) {
+ // We don't have enough information to support this. And it is expensive.
+ *offset = 0;
+ return "";
+}
+
+static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename);
+
+DebugFrameInfo* BacktraceOffline::GetDebugFrameInFile(const std::string& filename) {
+ if (cache_file_) {
+ auto it = g_debug_frames.find(filename);
+ if (it != g_debug_frames.end()) {
+ return it->second.get();
+ }
+ }
+ DebugFrameInfo* debug_frame = ReadDebugFrameFromFile(filename);
+ if (cache_file_) {
+ g_debug_frames.emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
+ }
+ return debug_frame;
+}
+
+static bool OmitEncodedValue(uint8_t encode, const uint8_t*& p) {
+ if (encode == DW_EH_PE_omit) {
+ return 0;
+ }
+ uint8_t format = encode & 0x0f;
+ switch (format) {
+ case DW_EH_PE_ptr:
+ p += sizeof(unw_word_t);
+ break;
+ case DW_EH_PE_uleb128:
+ case DW_EH_PE_sleb128:
+ while ((*p & 0x80) != 0) {
+ ++p;
+ }
+ ++p;
+ break;
+ case DW_EH_PE_udata2:
+ case DW_EH_PE_sdata2:
+ p += 2;
+ break;
+ case DW_EH_PE_udata4:
+ case DW_EH_PE_sdata4:
+ p += 4;
+ break;
+ case DW_EH_PE_udata8:
+ case DW_EH_PE_sdata8:
+ p += 8;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool GetFdeTableOffsetInEhFrameHdr(const std::vector<uint8_t>& data,
+ uint64_t* table_offset_in_eh_frame_hdr) {
+ const uint8_t* p = data.data();
+ const uint8_t* end = p + data.size();
+ if (p + 4 > end) {
+ return false;
+ }
+ uint8_t version = *p++;
+ if (version != 1) {
+ return false;
+ }
+ uint8_t eh_frame_ptr_encode = *p++;
+ uint8_t fde_count_encode = *p++;
+ uint8_t fde_table_encode = *p++;
+
+ if (fde_table_encode != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
+ return false;
+ }
+
+ if (!OmitEncodedValue(eh_frame_ptr_encode, p) || !OmitEncodedValue(fde_count_encode, p)) {
+ return false;
+ }
+ if (p >= end) {
+ return false;
+ }
+ *table_offset_in_eh_frame_hdr = p - data.data();
+ return true;
+}
+
+template <class ELFT>
+DebugFrameInfo* ReadDebugFrameFromELFFile(const llvm::object::ELFFile<ELFT>* elf) {
+ DebugFrameInfo* result = new DebugFrameInfo;
+ result->text_end_vaddr = std::numeric_limits<uint64_t>::max();
+
+ bool has_eh_frame_hdr = false;
+ bool has_eh_frame = false;
+
+ for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
+ llvm::ErrorOr<llvm::StringRef> name = elf->getSectionName(&*it);
+ if (name) {
+ std::string s = name.get();
+ if (s == ".debug_frame") {
+ result->has_debug_frame = true;
+ } else if (s == ".gnu_debugdata") {
+ result->has_gnu_debugdata = true;
+ } else if (s == ".eh_frame_hdr") {
+ result->eh_frame.hdr_vaddr = it->sh_addr;
+ llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
+ if (data) {
+ result->eh_frame.hdr_data.insert(result->eh_frame.hdr_data.end(),
+ data->data(), data->data() + data->size());
+
+ uint64_t fde_table_offset;
+ if (GetFdeTableOffsetInEhFrameHdr(result->eh_frame.hdr_data,
+ &fde_table_offset)) {
+ result->eh_frame.fde_table_offset = fde_table_offset;
+ // Make sure we have at least one entry in fde_table.
+ if (fde_table_offset + 2 * sizeof(int32_t) <= data->size()) {
+ intptr_t eh_frame_hdr_vaddr = it->sh_addr;
+ int32_t sdata;
+ uint8_t* p = result->eh_frame.hdr_data.data() + fde_table_offset;
+ memcpy(&sdata, p, sizeof(sdata));
+ result->eh_frame.min_func_vaddr = eh_frame_hdr_vaddr + sdata;
+ has_eh_frame_hdr = true;
+ }
+ }
+ }
+ } else if (s == ".eh_frame") {
+ result->eh_frame.vaddr = it->sh_addr;
+ llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
+ if (data) {
+ result->eh_frame.data.insert(result->eh_frame.data.end(),
+ data->data(), data->data() + data->size());
+ has_eh_frame = true;
+ }
+ } else if (s == ".ARM.exidx") {
+ result->arm_exidx.exidx_vaddr = it->sh_addr;
+ llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
+ if (data) {
+ size_t entry_count = data->size() / sizeof(ArmIdxEntry);
+ result->arm_exidx.exidx_data.resize(entry_count);
+ memcpy(result->arm_exidx.exidx_data.data(), data->data(),
+ entry_count * sizeof(ArmIdxEntry));
+ if (entry_count > 0u) {
+ // Change IdxEntry.func_offset into vaddr.
+ result->arm_exidx.func_vaddr_array.reserve(entry_count);
+ uint32_t vaddr = it->sh_addr;
+ for (auto& entry : result->arm_exidx.exidx_data) {
+ uint32_t func_offset = entry.func_offset + vaddr;
+ // Clear bit 31 for the prel31 offset.
+ // Arm sets bit 0 to mark it as thumb code, remove the flag.
+ result->arm_exidx.func_vaddr_array.push_back(
+ func_offset & 0x7ffffffe);
+ vaddr += 8;
+ }
+ result->has_arm_exidx = true;
+ }
+ }
+ } else if (s == ".ARM.extab") {
+ result->arm_exidx.extab_vaddr = it->sh_addr;
+ llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
+ if (data) {
+ result->arm_exidx.extab_data.insert(result->arm_exidx.extab_data.end(),
+ data->data(), data->data() + data->size());
+ }
+ } else if (s == ".text") {
+ result->text_end_vaddr = it->sh_addr + it->sh_size;
+ }
+ }
+ }
+
+ if (has_eh_frame_hdr && has_eh_frame) {
+ result->has_eh_frame = true;
+ }
+
+ result->min_vaddr = std::numeric_limits<uint64_t>::max();
+ for (auto it = elf->program_header_begin(); it != elf->program_header_end(); ++it) {
+ if ((it->p_type == llvm::ELF::PT_LOAD) && (it->p_flags & llvm::ELF::PF_X)) {
+ if (it->p_vaddr < result->min_vaddr) {
+ result->min_vaddr = it->p_vaddr;
+ }
+ }
+ }
+ if (!result->has_eh_frame && !result->has_arm_exidx && !result->has_debug_frame &&
+ !result->has_gnu_debugdata) {
+ delete result;
+ return nullptr;
+ }
+ return result;
+}
+
+static bool IsValidElfPath(const std::string& filename) {
+ static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
+
+ struct stat st;
+ if (stat(filename.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
+ return false;
+ }
+ FILE* fp = fopen(filename.c_str(), "reb");
+ if (fp == nullptr) {
+ return false;
+ }
+ char buf[4];
+ if (fread(buf, 4, 1, fp) != 1) {
+ fclose(fp);
+ return false;
+ }
+ fclose(fp);
+ return memcmp(buf, elf_magic, 4) == 0;
+}
+
+static bool IsValidApkPath(const std::string& apk_path) {
+ static const char zip_preamble[] = {0x50, 0x4b, 0x03, 0x04};
+ struct stat st;
+ if (stat(apk_path.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
+ return false;
+ }
+ FILE* fp = fopen(apk_path.c_str(), "reb");
+ if (fp == nullptr) {
+ return false;
+ }
+ char buf[4];
+ if (fread(buf, 4, 1, fp) != 1) {
+ fclose(fp);
+ return false;
+ }
+ fclose(fp);
+ return memcmp(buf, zip_preamble, 4) == 0;
+}
+
+class ScopedZiparchiveHandle {
+ public:
+ explicit ScopedZiparchiveHandle(ZipArchiveHandle handle) : handle_(handle) {
+ }
+
+ ~ScopedZiparchiveHandle() {
+ CloseArchive(handle_);
+ }
+
+ private:
+ ZipArchiveHandle handle_;
+};
+
+llvm::object::OwningBinary<llvm::object::Binary> OpenEmbeddedElfFile(const std::string& filename) {
+ llvm::object::OwningBinary<llvm::object::Binary> nothing;
+ size_t pos = filename.find("!/");
+ if (pos == std::string::npos) {
+ return nothing;
+ }
+ std::string apk_file = filename.substr(0, pos);
+ std::string elf_file = filename.substr(pos + 2);
+ if (!IsValidApkPath(apk_file)) {
+ BACK_LOGW("%s is not a valid apk file", apk_file.c_str());
+ return nothing;
+ }
+ ZipArchiveHandle handle;
+ int32_t ret_code = OpenArchive(apk_file.c_str(), &handle);
+ if (ret_code != 0) {
+ CloseArchive(handle);
+ BACK_LOGW("failed to open archive %s: %s", apk_file.c_str(), ErrorCodeString(ret_code));
+ return nothing;
+ }
+ ScopedZiparchiveHandle scoped_handle(handle);
+ ZipEntry zentry;
+ ret_code = FindEntry(handle, ZipString(elf_file.c_str()), &zentry);
+ if (ret_code != 0) {
+ BACK_LOGW("failed to find %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
+ ErrorCodeString(ret_code));
+ return nothing;
+ }
+ if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) {
+ BACK_LOGW("%s is compressed in %s, which doesn't support running directly", elf_file.c_str(),
+ apk_file.c_str());
+ return nothing;
+ }
+ auto buffer_or_err = llvm::MemoryBuffer::getOpenFileSlice(GetFileDescriptor(handle), apk_file,
+ zentry.uncompressed_length,
+ zentry.offset);
+ if (!buffer_or_err) {
+ BACK_LOGW("failed to read %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
+ buffer_or_err.getError().message().c_str());
+ return nothing;
+ }
+ auto binary_or_err = llvm::object::createBinary(buffer_or_err.get()->getMemBufferRef());
+ if (!binary_or_err) {
+ BACK_LOGW("failed to create binary for %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
+ llvm::toString(binary_or_err.takeError()).c_str());
+ return nothing;
+ }
+ return llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
+ std::move(buffer_or_err.get()));
+}
+
+static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) {
+ llvm::object::OwningBinary<llvm::object::Binary> owning_binary;
+ if (filename.find("!/") != std::string::npos) {
+ owning_binary = OpenEmbeddedElfFile(filename);
+ } else {
+ if (!IsValidElfPath(filename)) {
+ return nullptr;
+ }
+ auto binary_or_err = llvm::object::createBinary(llvm::StringRef(filename));
+ if (!binary_or_err) {
+ return nullptr;
+ }
+ owning_binary = std::move(binary_or_err.get());
+ }
+ llvm::object::Binary* binary = owning_binary.getBinary();
+ auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
+ return ReadDebugFrameFromELFFile(elf->getELFFile());
+ }
+ if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
+ return ReadDebugFrameFromELFFile(elf->getELFFile());
+ }
+ return nullptr;
+}
+
+Backtrace* Backtrace::CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
+ const backtrace_stackinfo_t& stack, bool cache_file) {
+ return new BacktraceOffline(pid, tid, map, stack, cache_file);
+}
diff --git a/libbacktrace/BacktraceOffline.h b/libbacktrace/BacktraceOffline.h
new file mode 100644
index 0000000..c0b686e
--- /dev/null
+++ b/libbacktrace/BacktraceOffline.h
@@ -0,0 +1,83 @@
+/*
+ * 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 _LIBBACKTRACE_UNWIND_OFFLINE_H
+#define _LIBBACKTRACE_UNWIND_OFFLINE_H
+
+#include <libunwind.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include <backtrace/Backtrace.h>
+
+struct Space {
+ uint64_t start;
+ uint64_t end;
+ const uint8_t* data;
+
+ Space() {
+ Clear();
+ }
+
+ void Clear();
+ size_t Read(uint64_t addr, uint8_t* buffer, size_t size);
+};
+
+struct DebugFrameInfo;
+
+class BacktraceOffline : public Backtrace {
+ public:
+ BacktraceOffline(pid_t pid, pid_t tid, BacktraceMap* map, const backtrace_stackinfo_t& stack,
+ bool cache_file)
+ : Backtrace(pid, tid, map),
+ cache_file_(cache_file),
+ context_(nullptr) {
+ stack_space_.start = stack.start;
+ stack_space_.end = stack.end;
+ stack_space_.data = stack.data;
+ }
+
+ virtual ~BacktraceOffline() = default;
+
+ bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
+
+ bool ReadWord(uintptr_t ptr, word_t* out_value) override;
+
+ size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override;
+
+ bool FindProcInfo(unw_addr_space_t addr_space, uint64_t ip, unw_proc_info_t* proc_info,
+ int need_unwind_info);
+
+ bool ReadReg(size_t reg_index, uint64_t* value);
+
+ protected:
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
+ DebugFrameInfo* GetDebugFrameInFile(const std::string& filename);
+
+ bool cache_file_;
+ ucontext_t* context_;
+ Space eh_frame_hdr_space_;
+ Space eh_frame_space_;
+ Space arm_extab_space_;
+ Space arm_exidx_space_;
+ Space stack_space_;
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_OFFLINE_H
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
index 09a721d..6d750ea 100644
--- a/libbacktrace/GetPss.cpp
+++ b/libbacktrace/GetPss.cpp
@@ -24,7 +24,7 @@
// This is an extremely simplified version of libpagemap.
-#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1))
+#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1))
#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1))
@@ -33,7 +33,7 @@
#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5))
-static bool ReadData(int fd, unsigned long place, uint64_t *data) {
+static bool ReadData(int fd, off_t place, uint64_t *data) {
if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
return false;
}
@@ -71,12 +71,13 @@
total_pss = 0;
break;
}
- for (size_t page = start/pagesize; page < end/pagesize; page++) {
+ for (off_t page = static_cast<off_t>(start/pagesize);
+ page < static_cast<off_t>(end/pagesize); page++) {
uint64_t data;
if (ReadData(pagemap_fd, page, &data)) {
if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
uint64_t count;
- if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) {
+ if (ReadData(pagecount_fd, static_cast<off_t>(PAGEMAP_PFN(data)), &count)) {
total_pss += (count >= 1) ? pagesize / count : 0;
}
}
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 67e583f..666c481 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -70,6 +70,7 @@
int ret = unw_getcontext(&context_);
if (ret < 0) {
BACK_LOGW("unw_getcontext failed %d", ret);
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
} else {
@@ -81,6 +82,7 @@
int ret = unw_init_local(cursor.get(), &context_);
if (ret < 0) {
BACK_LOGW("unw_init_local failed %d", ret);
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 879fea5..af79562 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -33,14 +34,18 @@
// of maps using the same map cursor.
//-------------------------------------------------------------------------
UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
+ unw_map_cursor_clear(&map_cursor_);
}
-UnwindMap::~UnwindMap() {
+UnwindMapRemote::UnwindMapRemote(pid_t pid) : UnwindMap(pid) {
+}
+
+UnwindMapRemote::~UnwindMapRemote() {
unw_map_cursor_destroy(&map_cursor_);
unw_map_cursor_clear(&map_cursor_);
}
-bool UnwindMap::GenerateMap() {
+bool UnwindMapRemote::GenerateMap() {
// Use the map_cursor information to construct the BacktraceMap data
// rather than reparsing /proc/self/maps.
unw_map_cursor_reset(&map_cursor_);
@@ -63,11 +68,12 @@
return true;
}
-bool UnwindMap::Build() {
+bool UnwindMapRemote::Build() {
return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap();
}
UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
+ pthread_rwlock_init(&map_lock_, nullptr);
}
UnwindMapLocal::~UnwindMapLocal() {
@@ -78,12 +84,18 @@
}
bool UnwindMapLocal::GenerateMap() {
+ // Lock so that multiple threads cannot modify the maps data at the
+ // same time.
+ pthread_rwlock_wrlock(&map_lock_);
+
// It's possible for the map to be regenerated while this loop is occurring.
// If that happens, get the map again, but only try at most three times
// before giving up.
+ bool generated = false;
for (int i = 0; i < 3; i++) {
maps_.clear();
+ // Save the map data retrieved so we can tell if it changes.
unw_map_local_cursor_get(&map_cursor_);
unw_map_t unw_map;
@@ -105,12 +117,17 @@
}
// Check to see if the map changed while getting the data.
if (ret != -UNW_EINVAL) {
- return true;
+ generated = true;
+ break;
}
}
- BACK_LOGW("Unable to generate the map.");
- return false;
+ pthread_rwlock_unlock(&map_lock_);
+
+ if (!generated) {
+ BACK_LOGW("Unable to generate the map.");
+ }
+ return generated;
}
bool UnwindMapLocal::Build() {
@@ -142,7 +159,7 @@
} else if (pid == getpid()) {
map = new UnwindMapLocal();
} else {
- map = new UnwindMap(pid);
+ map = new UnwindMapRemote(pid);
}
if (!map->Build()) {
delete map;
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index e292016..d5bec06 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -17,6 +17,7 @@
#ifndef _LIBBACKTRACE_UNWIND_MAP_H
#define _LIBBACKTRACE_UNWIND_MAP_H
+#include <pthread.h>
#include <stdint.h>
#include <sys/types.h>
@@ -28,32 +29,43 @@
class UnwindMap : public BacktraceMap {
public:
- UnwindMap(pid_t pid);
- virtual ~UnwindMap();
-
- virtual bool Build();
+ explicit UnwindMap(pid_t pid);
unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
protected:
- virtual bool GenerateMap();
-
unw_map_cursor_t map_cursor_;
};
+class UnwindMapRemote : public UnwindMap {
+public:
+ explicit UnwindMapRemote(pid_t pid);
+ virtual ~UnwindMapRemote();
+
+ bool Build() override;
+
+private:
+ bool GenerateMap();
+};
+
class UnwindMapLocal : public UnwindMap {
public:
UnwindMapLocal();
virtual ~UnwindMapLocal();
- virtual bool Build();
+ bool Build() override;
- virtual void FillIn(uintptr_t addr, backtrace_map_t* map);
+ void FillIn(uintptr_t addr, backtrace_map_t* map) override;
-protected:
- virtual bool GenerateMap();
+ void LockIterator() override { pthread_rwlock_rdlock(&map_lock_); }
+ void UnlockIterator() override { pthread_rwlock_unlock(&map_lock_); }
+
+private:
+ bool GenerateMap();
bool map_created_;
+
+ pthread_rwlock_t map_lock_;
};
#endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 07c2430..306d2ac 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -50,17 +50,22 @@
bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
if (GetMap() == nullptr) {
// Without a map object, we can't do anything.
+ error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
return false;
}
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
+
if (ucontext) {
BACK_LOGW("Unwinding from a specified context not supported yet.");
+ error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
return false;
}
addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
if (!addr_space_) {
BACK_LOGW("unw_create_addr_space failed.");
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
@@ -70,6 +75,7 @@
upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
if (!upt_info_) {
BACK_LOGW("Failed to create upt info.");
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
@@ -77,6 +83,7 @@
int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
if (ret < 0) {
BACK_LOGW("unw_init_remote failed %d", ret);
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
new file mode 100644
index 0000000..49fcb29
--- /dev/null
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <libunwind.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#include <cutils/threads.h>
+
+#include <gtest/gtest.h>
+
+extern "C" {
+// Prototypes for functions in the test library.
+int test_level_one(int, int, int, int, void (*)(void*), void*);
+int test_level_two(int, int, int, int, void (*)(void*), void*);
+int test_level_three(int, int, int, int, void (*)(void*), void*);
+int test_level_four(int, int, int, int, void (*)(void*), void*);
+int test_recursive_call(int, void (*)(void*), void*);
+void test_get_context_and_wait(unw_context_t* unw_context, volatile int* exit_flag);
+}
+
+static ucontext_t GetUContextFromUnwContext(const unw_context_t& unw_context) {
+ ucontext_t ucontext;
+ memset(&ucontext, 0, sizeof(ucontext));
+#if defined(__arm__)
+ ucontext.uc_mcontext.arm_r0 = unw_context.regs[0];
+ ucontext.uc_mcontext.arm_r1 = unw_context.regs[1];
+ ucontext.uc_mcontext.arm_r2 = unw_context.regs[2];
+ ucontext.uc_mcontext.arm_r3 = unw_context.regs[3];
+ ucontext.uc_mcontext.arm_r4 = unw_context.regs[4];
+ ucontext.uc_mcontext.arm_r5 = unw_context.regs[5];
+ ucontext.uc_mcontext.arm_r6 = unw_context.regs[6];
+ ucontext.uc_mcontext.arm_r7 = unw_context.regs[7];
+ ucontext.uc_mcontext.arm_r8 = unw_context.regs[8];
+ ucontext.uc_mcontext.arm_r9 = unw_context.regs[9];
+ ucontext.uc_mcontext.arm_r10 = unw_context.regs[10];
+ ucontext.uc_mcontext.arm_fp = unw_context.regs[11];
+ ucontext.uc_mcontext.arm_ip = unw_context.regs[12];
+ ucontext.uc_mcontext.arm_sp = unw_context.regs[13];
+ ucontext.uc_mcontext.arm_lr = unw_context.regs[14];
+ ucontext.uc_mcontext.arm_pc = unw_context.regs[15];
+#else
+ ucontext.uc_mcontext = unw_context.uc_mcontext;
+#endif
+ return ucontext;
+}
+
+struct FunctionSymbol {
+ std::string name;
+ uintptr_t start;
+ uintptr_t end;
+};
+
+static std::vector<FunctionSymbol> GetFunctionSymbols() {
+ std::vector<FunctionSymbol> symbols = {
+ {"unknown_start", 0, 0},
+ {"test_level_one", reinterpret_cast<uintptr_t>(&test_level_one), 0},
+ {"test_level_two", reinterpret_cast<uintptr_t>(&test_level_two), 0},
+ {"test_level_three", reinterpret_cast<uintptr_t>(&test_level_three), 0},
+ {"test_level_four", reinterpret_cast<uintptr_t>(&test_level_four), 0},
+ {"test_recursive_call", reinterpret_cast<uintptr_t>(&test_recursive_call), 0},
+ {"test_get_context_and_wait", reinterpret_cast<uintptr_t>(&test_get_context_and_wait), 0},
+ {"unknown_end", static_cast<uintptr_t>(-1), static_cast<uintptr_t>(-1)},
+ };
+ std::sort(
+ symbols.begin(), symbols.end(),
+ [](const FunctionSymbol& s1, const FunctionSymbol& s2) { return s1.start < s2.start; });
+ for (size_t i = 0; i + 1 < symbols.size(); ++i) {
+ symbols[i].end = symbols[i + 1].start;
+ }
+ return symbols;
+}
+
+static std::string RawDataToHexString(const void* data, size_t size) {
+ const uint8_t* p = static_cast<const uint8_t*>(data);
+ std::string s;
+ for (size_t i = 0; i < size; ++i) {
+ s += android::base::StringPrintf("%02x", p[i]);
+ }
+ return s;
+}
+
+static void HexStringToRawData(const char* s, void* data, size_t size) {
+ uint8_t* p = static_cast<uint8_t*>(data);
+ for (size_t i = 0; i < size; ++i) {
+ int value;
+ sscanf(s, "%02x", &value);
+ *p++ = static_cast<uint8_t>(value);
+ s += 2;
+ }
+}
+
+struct OfflineThreadArg {
+ unw_context_t unw_context;
+ pid_t tid;
+ volatile int exit_flag;
+};
+
+static void* OfflineThreadFunc(void* arg) {
+ OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
+ fn_arg->tid = gettid();
+ test_get_context_and_wait(&fn_arg->unw_context, &fn_arg->exit_flag);
+ return nullptr;
+}
+
+// This test is disable because it is for generating test data.
+TEST(libbacktrace, DISABLED_generate_offline_testdata) {
+ // Create a thread to generate the needed stack and registers information.
+ const size_t stack_size = 16 * 1024;
+ void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, stack);
+ uintptr_t stack_addr = reinterpret_cast<uintptr_t>(stack);
+ pthread_attr_t attr;
+ ASSERT_EQ(0, pthread_attr_init(&attr));
+ ASSERT_EQ(0, pthread_attr_setstack(&attr, reinterpret_cast<void*>(stack), stack_size));
+ pthread_t thread;
+ OfflineThreadArg arg;
+ arg.exit_flag = 0;
+ ASSERT_EQ(0, pthread_create(&thread, &attr, OfflineThreadFunc, &arg));
+ // Wait for the offline thread to generate the stack and unw_context information.
+ sleep(1);
+ // Copy the stack information.
+ std::vector<uint8_t> stack_data(reinterpret_cast<uint8_t*>(stack),
+ reinterpret_cast<uint8_t*>(stack) + stack_size);
+ arg.exit_flag = 1;
+ ASSERT_EQ(0, pthread_join(thread, nullptr));
+ ASSERT_EQ(0, munmap(stack, stack_size));
+
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
+ ASSERT_TRUE(map != nullptr);
+
+ backtrace_stackinfo_t stack_info;
+ stack_info.start = stack_addr;
+ stack_info.end = stack_addr + stack_size;
+ stack_info.data = stack_data.data();
+
+ // Generate offline testdata.
+ std::string testdata;
+ // 1. Dump pid, tid
+ testdata += android::base::StringPrintf("pid: %d tid: %d\n", getpid(), arg.tid);
+ // 2. Dump maps
+ for (auto it = map->begin(); it != map->end(); ++it) {
+ testdata += android::base::StringPrintf(
+ "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR
+ " load_base: %" PRIxPTR " flags: %d name: %s\n",
+ it->start, it->end, it->offset, it->load_base, it->flags, it->name.c_str());
+ }
+ // 3. Dump registers
+ testdata += android::base::StringPrintf("registers: %zu ", sizeof(arg.unw_context));
+ testdata += RawDataToHexString(&arg.unw_context, sizeof(arg.unw_context));
+ testdata.push_back('\n');
+
+ // 4. Dump stack
+ testdata += android::base::StringPrintf(
+ "stack: start: %" PRIx64 " end: %" PRIx64 " size: %zu ",
+ stack_info.start, stack_info.end, stack_data.size());
+ testdata += RawDataToHexString(stack_data.data(), stack_data.size());
+ testdata.push_back('\n');
+
+ // 5. Dump function symbols
+ std::vector<FunctionSymbol> function_symbols = GetFunctionSymbols();
+ for (const auto& symbol : function_symbols) {
+ testdata += android::base::StringPrintf(
+ "function: start: %" PRIxPTR " end: %" PRIxPTR" name: %s\n",
+ symbol.start, symbol.end, symbol.name.c_str());
+ }
+
+ ASSERT_TRUE(android::base::WriteStringToFile(testdata, "offline_testdata"));
+}
+
+// Return the name of the function which matches the address. Although we don't know the
+// exact end of each function, it is accurate enough for the tests.
+static std::string FunctionNameForAddress(uintptr_t addr,
+ const std::vector<FunctionSymbol>& symbols) {
+ for (auto& symbol : symbols) {
+ if (addr >= symbol.start && addr < symbol.end) {
+ return symbol.name;
+ }
+ }
+ return "";
+}
+
+static std::string GetArch() {
+#if defined(__arm__)
+ return "arm";
+#elif defined(__aarch64__)
+ return "aarch64";
+#elif defined(__i386__)
+ return "x86";
+#elif defined(__x86_64__)
+ return "x86_64";
+#else
+ return "";
+#endif
+}
+
+static void BacktraceOfflineTest(const std::string& testlib_name) {
+ const std::string arch = GetArch();
+ if (arch.empty()) {
+ GTEST_LOG_(INFO) << "This test does nothing on current arch.";
+ return;
+ }
+ const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata";
+ std::string testdata;
+ ASSERT_TRUE(android::base::ReadFileToString(offline_testdata_path, &testdata));
+
+ const std::string testlib_path = "testdata/" + arch + "/" + testlib_name;
+ struct stat st;
+ if (stat(testlib_path.c_str(), &st) == -1) {
+ GTEST_LOG_(INFO) << "This test is skipped as " << testlib_path << " doesn't exist.";
+ return;
+ }
+
+ // Parse offline_testdata.
+ std::vector<std::string> lines = android::base::Split(testdata, "\n");
+ int pid;
+ int tid;
+ std::vector<backtrace_map_t> maps;
+ unw_context_t unw_context;
+ backtrace_stackinfo_t stack_info;
+ std::vector<uint8_t> stack;
+ std::vector<FunctionSymbol> symbols;
+ for (const auto& line : lines) {
+ if (android::base::StartsWith(line, "pid:")) {
+ sscanf(line.c_str(), "pid: %d tid: %d", &pid, &tid);
+ } else if (android::base::StartsWith(line, "map:")) {
+ maps.resize(maps.size() + 1);
+ int pos;
+ sscanf(line.c_str(),
+ "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR
+ " load_base: %" SCNxPTR " flags: %d name: %n",
+ &maps.back().start, &maps.back().end, &maps.back().offset,
+ &maps.back().load_base, &maps.back().flags, &pos);
+ maps.back().name = android::base::Trim(line.substr(pos));
+ } else if (android::base::StartsWith(line, "registers:")) {
+ size_t size;
+ int pos;
+ sscanf(line.c_str(), "registers: %zu %n", &size, &pos);
+ ASSERT_EQ(sizeof(unw_context), size);
+ HexStringToRawData(&line[pos], &unw_context, size);
+ } else if (android::base::StartsWith(line, "stack:")) {
+ size_t size;
+ int pos;
+ sscanf(line.c_str(),
+ "stack: start: %" SCNx64 " end: %" SCNx64 " size: %zu %n",
+ &stack_info.start, &stack_info.end, &size, &pos);
+ stack.resize(size);
+ HexStringToRawData(&line[pos], &stack[0], size);
+ stack_info.data = stack.data();
+ } else if (android::base::StartsWith(line, "function:")) {
+ symbols.resize(symbols.size() + 1);
+ int pos;
+ sscanf(line.c_str(),
+ "function: start: %" SCNxPTR " end: %" SCNxPTR " name: %n",
+ &symbols.back().start, &symbols.back().end,
+ &pos);
+ symbols.back().name = line.substr(pos);
+ }
+ }
+
+ // Fix path of libbacktrace_testlib.so.
+ for (auto& map : maps) {
+ if (map.name.find("libbacktrace_test.so") != std::string::npos) {
+ map.name = testlib_path;
+ }
+ }
+
+ // Do offline backtrace.
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid, maps));
+ ASSERT_TRUE(map != nullptr);
+
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::CreateOffline(pid, tid, map.get(), stack_info));
+ ASSERT_TRUE(backtrace != nullptr);
+
+ ucontext_t ucontext = GetUContextFromUnwContext(unw_context);
+ ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+
+ // Collect pc values of the call stack frames.
+ std::vector<uintptr_t> pc_values;
+ for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
+ pc_values.push_back(backtrace->GetFrame(i)->pc);
+ }
+
+ size_t test_one_index = 0;
+ for (size_t i = 0; i < pc_values.size(); ++i) {
+ if (FunctionNameForAddress(pc_values[i], symbols) == "test_level_one") {
+ test_one_index = i;
+ break;
+ }
+ }
+
+ ASSERT_GE(test_one_index, 3u);
+ ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], symbols));
+ ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1], symbols));
+ ASSERT_EQ("test_level_three", FunctionNameForAddress(pc_values[test_one_index - 2], symbols));
+ ASSERT_EQ("test_level_four", FunctionNameForAddress(pc_values[test_one_index - 3], symbols));
+}
+
+TEST(libbacktrace, offline_eh_frame) {
+ BacktraceOfflineTest("libbacktrace_test_eh_frame.so");
+}
+
+TEST(libbacktrace, offline_debug_frame) {
+ BacktraceOfflineTest("libbacktrace_test_debug_frame.so");
+}
+
+TEST(libbacktrace, offline_gnu_debugdata) {
+ BacktraceOfflineTest("libbacktrace_test_gnu_debugdata.so");
+}
+
+TEST(libbacktrace, offline_arm_exidx) {
+ BacktraceOfflineTest("libbacktrace_test_arm_exidx.so");
+}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 6bd7529..e25c8e9 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -42,7 +42,7 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <cutils/atomic.h>
#include <cutils/threads.h>
@@ -162,6 +162,7 @@
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyLevelDump(backtrace.get());
}
@@ -183,6 +184,7 @@
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyMaxDump(backtrace.get());
}
@@ -200,6 +202,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), tid));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyFunc(backtrace.get());
}
@@ -220,6 +223,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
ASSERT_TRUE(backtrace->NumFrames() != 0);
for (const auto& frame : *backtrace ) {
@@ -267,16 +271,19 @@
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(all.get() != nullptr);
ASSERT_TRUE(all->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
std::unique_ptr<Backtrace> ign1(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
std::unique_ptr<Backtrace> ign2(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
}
@@ -314,6 +321,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
if (ReadyFunc(backtrace.get())) {
VerifyFunc(backtrace.get());
verified = true;
@@ -372,10 +380,12 @@
std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
}
@@ -404,17 +414,16 @@
char task_path[128];
snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
- DIR* tasks_dir = opendir(task_path);
+ std::unique_ptr<DIR, decltype(&closedir)> tasks_dir(opendir(task_path), closedir);
ASSERT_TRUE(tasks_dir != nullptr);
struct dirent* entry;
- while ((entry = readdir(tasks_dir)) != nullptr) {
+ while ((entry = readdir(tasks_dir.get())) != nullptr) {
char* end;
pid_t tid = strtoul(entry->d_name, &end, 10);
if (*end == '\0') {
threads->push_back(tid);
}
}
- closedir(tasks_dir);
}
TEST(libbacktrace, ptrace_threads) {
@@ -463,6 +472,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyLevelDump(backtrace.get());
}
@@ -475,6 +485,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyMaxDump(backtrace.get());
}
@@ -516,6 +527,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyLevelDump(backtrace.get());
@@ -555,14 +567,17 @@
std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(all.get() != nullptr);
ASSERT_TRUE(all->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
@@ -593,6 +608,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyMaxDump(backtrace.get());
@@ -719,18 +735,21 @@
Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
ASSERT_TRUE(back1 != nullptr);
EXPECT_TRUE(back1->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back1->GetError());
delete back1;
delete map1;
Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
ASSERT_TRUE(back2 != nullptr);
EXPECT_TRUE(back2->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back2->GetError());
delete back2;
delete map2;
Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
ASSERT_TRUE(back3 != nullptr);
EXPECT_TRUE(back3->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back3->GetError());
delete back3;
delete map3;
}
@@ -775,16 +794,29 @@
backtrace->FormatFrameData(&frame));
// Check map name empty, but exists.
- frame.map.start = 1;
- frame.map.end = 1;
+ frame.pc = 0xb0020;
+ frame.map.start = 0xb0000;
+ frame.map.end = 0xbffff;
frame.map.load_base = 0;
#if defined(__LP64__)
- EXPECT_EQ("#01 pc 0000000000000001 <unknown>",
+ EXPECT_EQ("#01 pc 0000000000000020 <anonymous:00000000000b0000>",
#else
- EXPECT_EQ("#01 pc 00000001 <unknown>",
+ EXPECT_EQ("#01 pc 00000020 <anonymous:000b0000>",
#endif
backtrace->FormatFrameData(&frame));
+ // Check map name begins with a [.
+ frame.pc = 0xc0020;
+ frame.map.start = 0xc0000;
+ frame.map.end = 0xcffff;
+ frame.map.load_base = 0;
+ frame.map.name = "[anon:thread signal stack]";
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000000000020 [anon:thread signal stack:00000000000c0000]",
+#else
+ EXPECT_EQ("#01 pc 00000020 [anon:thread signal stack:000c0000]",
+#endif
+ backtrace->FormatFrameData(&frame));
// Check relative pc is set and map name is set.
frame.pc = 0x12345679;
@@ -825,6 +857,15 @@
EXPECT_EQ("#01 pc 123456dc MapFake (ProcFake+645)",
#endif
backtrace->FormatFrameData(&frame));
+
+ // Check a non-zero map offset.
+ frame.map.offset = 0x1000;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 00000000123456dc MapFake (offset 0x1000) (ProcFake+645)",
+#else
+ EXPECT_EQ("#01 pc 123456dc MapFake (offset 0x1000) (ProcFake+645)",
+#endif
+ backtrace->FormatFrameData(&frame));
}
struct map_test_t {
@@ -854,6 +895,7 @@
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
// Basic test that verifies that the map is in the expected order.
+ ScopedBacktraceMapIteratorLock lock(map.get());
std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
ASSERT_TRUE(test_it != test_maps.end());
@@ -1134,7 +1176,7 @@
int fd = open(tmp_so_name, O_RDONLY);
ASSERT_TRUE(fd != -1);
- void* map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ void* map = mmap(NULL, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
ASSERT_TRUE(map != MAP_FAILED);
close(fd);
ASSERT_TRUE(unlink(tmp_so_name) != -1);
@@ -1184,7 +1226,7 @@
exit(0);
}
- void* map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ void* map = mmap(NULL, 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);
@@ -1287,6 +1329,7 @@
BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
size_t frame_num;
ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num));
@@ -1343,6 +1386,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
size_t frame_num;
if (FindFuncFrameInBacktrace(backtrace.get(),
@@ -1366,10 +1410,18 @@
ASSERT_TRUE(done) << "Test function never found in unwind.";
}
+TEST(libbacktrace, unwind_thread_doesnt_exist) {
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_FALSE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError());
+}
+
#if defined(ENABLE_PSS_TESTS)
#include "GetPss.h"
-#define MAX_LEAK_BYTES 32*1024UL
+#define MAX_LEAK_BYTES (32*1024UL)
void CheckForLeak(pid_t pid, pid_t tid) {
// Do a few runs to get the PSS stable.
@@ -1377,6 +1429,7 @@
Backtrace* backtrace = Backtrace::Create(pid, tid);
ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
delete backtrace;
}
size_t stable_pss = GetPssBytes();
@@ -1387,13 +1440,14 @@
Backtrace* backtrace = Backtrace::Create(pid, tid);
ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
delete backtrace;
}
size_t new_pss = GetPssBytes();
ASSERT_TRUE(new_pss != 0);
- size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
- // As long as the new pss is within a certain amount, consider everything okay.
- ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
+ if (new_pss > stable_pss) {
+ ASSERT_LE(new_pss - stable_pss, MAX_LEAK_BYTES);
+ }
}
TEST(libbacktrace, check_for_leak_local) {
@@ -1439,4 +1493,3 @@
ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
}
#endif
-
diff --git a/libbacktrace/backtrace_testlib.c b/libbacktrace/backtrace_testlib.c
index d4d15db..6f6b535 100644
--- a/libbacktrace/backtrace_testlib.c
+++ b/libbacktrace/backtrace_testlib.c
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <libunwind.h>
#include <stdio.h>
int test_level_four(int one, int two, int three, int four,
@@ -53,3 +54,23 @@
}
return 0;
}
+
+typedef struct {
+ unw_context_t* unw_context;
+ volatile int* exit_flag;
+} GetContextArg;
+
+static void GetContextAndExit(void* data) {
+ GetContextArg* arg = (GetContextArg*)data;
+ unw_getcontext(arg->unw_context);
+ // Don't touch the stack anymore.
+ while (*arg->exit_flag == 0) {
+ }
+}
+
+void test_get_context_and_wait(unw_context_t* unw_context, volatile int* exit_flag) {
+ GetContextArg arg;
+ arg.unw_context = unw_context;
+ arg.exit_flag = exit_flag;
+ test_level_one(1, 2, 3, 4, GetContextAndExit, &arg);
+}
diff --git a/libbacktrace/map_info.c b/libbacktrace/map_info.c
deleted file mode 100644
index 073b24a..0000000
--- a/libbacktrace/map_info.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <log/log.h>
-#include <sys/time.h>
-
-#include <backtrace/backtrace.h>
-
-#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
-static backtrace_map_info_t* parse_vmmap_line(const char* line) {
- unsigned long int start;
- unsigned long int end;
- char permissions[4];
- int name_pos;
- if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
- &start, &end, permissions, &name_pos) != 3) {
- return NULL;
- }
-
- const char* name = line + name_pos;
- size_t name_len = strlen(name);
-
- backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len);
- if (mi != NULL) {
- mi->start = start;
- mi->end = end;
- mi->is_readable = permissions[0] == 'r';
- mi->is_writable = permissions[1] == 'w';
- mi->is_executable = permissions[2] == 'x';
- memcpy(mi->name, name, name_len);
- mi->name[name_len - 1] = '\0';
- ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
- "is_readable=%d, is_writable=%d is_executable=%d, name=%s",
- mi->start, mi->end,
- mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
- }
- return mi;
-}
-
-backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid) {
- char cmd[1024];
- if (pid < 0) {
- pid = getpid();
- }
- snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid);
- FILE* fp = popen(cmd, "r");
- if (fp == NULL) {
- return NULL;
- }
-
- char line[1024];
- backtrace_map_info_t* milist = NULL;
- while (fgets(line, sizeof(line), fp) != NULL) {
- backtrace_map_info_t* mi = parse_vmmap_line(line);
- if (mi != NULL) {
- mi->next = milist;
- milist = mi;
- }
- }
- pclose(fp);
- return milist;
-}
-
-#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
-static backtrace_map_info_t* parse_maps_line(const char* line)
-{
- unsigned long int start;
- unsigned long int end;
- char permissions[5];
- int name_pos;
- if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end,
- permissions, &name_pos) != 3) {
- return NULL;
- }
-
- while (isspace(line[name_pos])) {
- name_pos += 1;
- }
- const char* name = line + name_pos;
- size_t name_len = strlen(name);
- if (name_len && name[name_len - 1] == '\n') {
- name_len -= 1;
- }
-
- backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len + 1);
- if (mi) {
- mi->start = start;
- mi->end = end;
- mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r';
- mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w';
- mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
- memcpy(mi->name, name, name_len);
- mi->name[name_len] = '\0';
- ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
- "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
- mi->start, mi->end,
- mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
- }
- return mi;
-}
-
-backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid) {
- char path[PATH_MAX];
- char line[1024];
- FILE* fp;
- backtrace_map_info_t* milist = NULL;
-
- if (tid < 0) {
- tid = getpid();
- }
- snprintf(path, PATH_MAX, "/proc/%d/maps", tid);
- fp = fopen(path, "r");
- if (fp) {
- while(fgets(line, sizeof(line), fp)) {
- backtrace_map_info_t* mi = parse_maps_line(line);
- if (mi) {
- mi->next = milist;
- milist = mi;
- }
- }
- fclose(fp);
- }
- return milist;
-}
-
-#endif
-
-void backtrace_destroy_map_info_list(backtrace_map_info_t* milist) {
- while (milist) {
- backtrace_map_info_t* next = milist->next;
- free(milist);
- milist = next;
- }
-}
-
-const backtrace_map_info_t* backtrace_find_map_info(
- const backtrace_map_info_t* milist, uintptr_t addr) {
- const backtrace_map_info_t* mi = milist;
- while (mi && !(addr >= mi->start && addr < mi->end)) {
- mi = mi->next;
- }
- return mi;
-}
diff --git a/libbacktrace/testdata/aarch64/libbacktrace_test_eh_frame.so b/libbacktrace/testdata/aarch64/libbacktrace_test_eh_frame.so
new file mode 100755
index 0000000..880f337
--- /dev/null
+++ b/libbacktrace/testdata/aarch64/libbacktrace_test_eh_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/aarch64/offline_testdata b/libbacktrace/testdata/aarch64/offline_testdata
new file mode 100644
index 0000000..ba44e09
--- /dev/null
+++ b/libbacktrace/testdata/aarch64/offline_testdata
@@ -0,0 +1,107 @@
+pid: 32438 tid: 32439
+map: start: 557066e000 end: 55706ee000 offset: 0 load_base: 0 flags: 5 name: /data/backtrace_test64
+map: start: 55706ef000 end: 55706f2000 offset: 80000 load_base: 0 flags: 1 name: /data/backtrace_test64
+map: start: 55706f2000 end: 55706f3000 offset: 83000 load_base: 0 flags: 3 name: /data/backtrace_test64
+map: start: 7014200000 end: 7014600000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
+map: start: 701464c000 end: 701465c000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libcutils.so
+map: start: 701465c000 end: 701465d000 offset: 0 load_base: 0 flags: 0 name:
+map: start: 701465d000 end: 701465e000 offset: 10000 load_base: 0 flags: 1 name: /system/lib64/libcutils.so
+map: start: 701465e000 end: 701465f000 offset: 11000 load_base: 0 flags: 3 name: /system/lib64/libcutils.so
+map: start: 7014691000 end: 70146b5000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/liblzma.so
+map: start: 70146b5000 end: 70146b6000 offset: 23000 load_base: 0 flags: 1 name: /system/lib64/liblzma.so
+map: start: 70146b6000 end: 70146b7000 offset: 24000 load_base: 0 flags: 3 name: /system/lib64/liblzma.so
+map: start: 70146b7000 end: 70146bc000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: 70146c9000 end: 70158b5000 offset: 0 load_base: af000 flags: 5 name: /system/lib64/libLLVM.so
+map: start: 70158b5000 end: 701596b000 offset: 11eb000 load_base: 0 flags: 1 name: /system/lib64/libLLVM.so
+map: start: 701596b000 end: 701596c000 offset: 12a1000 load_base: 0 flags: 3 name: /system/lib64/libLLVM.so
+map: start: 701596c000 end: 701599f000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: 70159c2000 end: 70159f9000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libm.so
+map: start: 70159f9000 end: 70159fa000 offset: 36000 load_base: 0 flags: 1 name: /system/lib64/libm.so
+map: start: 70159fa000 end: 70159fb000 offset: 37000 load_base: 0 flags: 3 name: /system/lib64/libm.so
+map: start: 7015a1e000 end: 7015a2e000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libbacktrace.so
+map: start: 7015a2e000 end: 7015a2f000 offset: f000 load_base: 0 flags: 1 name: /system/lib64/libbacktrace.so
+map: start: 7015a2f000 end: 7015a30000 offset: 10000 load_base: 0 flags: 3 name: /system/lib64/libbacktrace.so
+map: start: 7015a5e000 end: 7015a7d000 offset: 0 load_base: 1000 flags: 5 name: /system/lib64/libutils.so
+map: start: 7015a7d000 end: 7015a7e000 offset: 0 load_base: 0 flags: 0 name:
+map: start: 7015a7e000 end: 7015a7f000 offset: 1f000 load_base: 0 flags: 1 name: /system/lib64/libutils.so
+map: start: 7015a7f000 end: 7015a80000 offset: 20000 load_base: 0 flags: 3 name: /system/lib64/libutils.so
+map: start: 7015a99000 end: 7015b6d000 offset: 0 load_base: 9000 flags: 5 name: /system/lib64/libc++.so
+map: start: 7015b6d000 end: 7015b6e000 offset: 0 load_base: 0 flags: 0 name:
+map: start: 7015b6e000 end: 7015b76000 offset: d4000 load_base: 0 flags: 1 name: /system/lib64/libc++.so
+map: start: 7015b76000 end: 7015b77000 offset: dc000 load_base: 0 flags: 3 name: /system/lib64/libc++.so
+map: start: 7015b77000 end: 7015b7a000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: 7015b81000 end: 7015b92000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/liblog.so
+map: start: 7015b92000 end: 7015b93000 offset: 10000 load_base: 0 flags: 1 name: /system/lib64/liblog.so
+map: start: 7015b93000 end: 7015b94000 offset: 11000 load_base: 0 flags: 3 name: /system/lib64/liblog.so
+map: start: 7015be3000 end: 7015ca3000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libc.so
+map: start: 7015ca3000 end: 7015ca9000 offset: bf000 load_base: 0 flags: 1 name: /system/lib64/libc.so
+map: start: 7015ca9000 end: 7015cab000 offset: c5000 load_base: 0 flags: 3 name: /system/lib64/libc.so
+map: start: 7015cab000 end: 7015cac000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cac000 end: 7015cad000 offset: 0 load_base: 0 flags: 1 name: [anon:.bss]
+map: start: 7015cad000 end: 7015cb4000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cf5000 end: 7015cf6000 offset: 0 load_base: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: 7015cf6000 end: 7015cf7000 offset: 0 load_base: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: 7015cf7000 end: 7015cf8000 offset: 1000 load_base: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: 7015d1f000 end: 7015d39000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libunwind.so
+map: start: 7015d39000 end: 7015d3a000 offset: 19000 load_base: 0 flags: 1 name: /system/lib64/libunwind.so
+map: start: 7015d3a000 end: 7015d3b000 offset: 1a000 load_base: 0 flags: 3 name: /system/lib64/libunwind.so
+map: start: 7015d3b000 end: 7015da4000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: 7015de8000 end: 7015df7000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libbase.so
+map: start: 7015df7000 end: 7015df8000 offset: 0 load_base: 0 flags: 0 name:
+map: start: 7015df8000 end: 7015df9000 offset: f000 load_base: 0 flags: 1 name: /system/lib64/libbase.so
+map: start: 7015df9000 end: 7015dfa000 offset: 10000 load_base: 0 flags: 3 name: /system/lib64/libbase.so
+map: start: 7015e35000 end: 7015e36000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015e4f000 end: 7015e50000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015f11000 end: 7015f13000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libnetd_client.so
+map: start: 7015f13000 end: 7015f14000 offset: 1000 load_base: 0 flags: 1 name: /system/lib64/libnetd_client.so
+map: start: 7015f14000 end: 7015f15000 offset: 2000 load_base: 0 flags: 3 name: /system/lib64/libnetd_client.so
+map: start: 7015f6c000 end: 7015f79000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7015f79000 end: 7015f99000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: 7015f99000 end: 7015f9a000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015f9a000 end: 7015f9b000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fa6000 end: 7015fa7000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fa8000 end: 7015fa9000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015faf000 end: 7015fcf000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 7015fcf000 end: 7015fd0000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd0000 end: 7015fd1000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fd1000 end: 7015fd2000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd4000 end: 7015fd7000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7015fd7000 end: 7015fdb000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 7015fdb000 end: 7015fdc000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015fdc000 end: 7015fdd000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7015fdd000 end: 7015fde000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015fde000 end: 7015ffe000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: 7015ffe000 end: 701601e000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 701601e000 end: 701601f000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 701601f000 end: 7016020000 offset: 0 load_base: 0 flags: 0 name:
+map: start: 7016020000 end: 7016021000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7016021000 end: 7016022000 offset: 0 load_base: 0 flags: 0 name:
+map: start: 7016022000 end: 7016023000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: 7016023000 end: 7016025000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7016025000 end: 7016026000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016026000 end: 7016027000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7016027000 end: 7016028000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016028000 end: 7016029000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016029000 end: 701602a000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 701602a000 end: 701602b000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 701602b000 end: 701602c000 offset: 0 load_base: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: 701602c000 end: 7016030000 offset: 0 load_base: 0 flags: 3 name: [anon:thread signal stack]
+map: start: 7016030000 end: 7016031000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016031000 end: 7016033000 offset: 0 load_base: 0 flags: 5 name: [vdso]
+map: start: 7016033000 end: 70160dd000 offset: 0 load_base: 0 flags: 5 name: /system/bin/linker64
+map: start: 70160dd000 end: 70160e0000 offset: a9000 load_base: 0 flags: 1 name: /system/bin/linker64
+map: start: 70160e0000 end: 70160e1000 offset: ac000 load_base: 0 flags: 3 name: /system/bin/linker64
+map: start: 70160e1000 end: 70160e4000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 70160e4000 end: 70160e5000 offset: 0 load_base: 0 flags: 1 name:
+map: start: 70160e5000 end: 70160e8000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd8baf000 end: 7fd8be6000 offset: 0 load_base: 0 flags: 3 name: [stack]
+registers: 4560 679a0b1670000000f3eb6d705500000090f56e7055000000000000000000000000206f705500000014e0677055000000d038bed87f0000004cde67705500000041000000000000001900000000000000c00f241470000000d3aec914588f4bcd0400000000000000e493af157000000090f56e7055000000060000000000000023c00b1670000000b1d1fd15700000003039bed87f000000c898041670000000304cbed87f000000b8130e1670000000303cbed87f000000f838bed87f0000000e0000000000000015000000000000001c00000000000000ec59cf1570000000b863fd15700000005064fd15700000000000000000000000ec59cf15700000000200000000000000b863fd1570000000144abed87f0000006064fd15700000005064fd157000000000010000000000005826bed87f000000d86fcf15700000006057cf157000000000000000000000005064fd15700000005064fd15700000005064fd1570000000b67e00000000000040fd677055000000d064fd15700000000030fd157000000002000000000000000100000000000000fcb58a56000000000063fd15700000009857cf1570000000c062fd15700000001c5acf157000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000003003167000000001000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000033000000000000000300000000000000003303167000000008330316700000000000000006000000f8320316700000005839bed87f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000c84cbed87f000000e84cbed87f000000984dbed87f00000078170e167000000002fd0000000000001400000000000000ff8100000100000000000000000000000000000000000000000000000000000078145700000000000010000000000000902b000000000000bdd04058000000000000000000000000bdd04058000000000000000000000000ccb58a560000000042487408000000000000000000000000cc57041670000000004704167000000010c0fd157000000010c0fd157000000090c0fd15700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000010000000000000002f48bed87f000000d3aec914588f4bcd2045bed87f0000002045bed87f0000002f48bed87f00000001000000000000002f000080000000005045bed87f0000000045bed87f000000c0a0c315700000006045bed87f0000006045bed87f000000010000000000000001000000000000000344bed87f00000001000000100000009c3fbed87f0000009b3fbed87f0000000344bed87f0000009a3fbed87f0000000100000000000000953fbed87f0000004344bed80100000001000000010000002c48bed87f0000000444bed87f0000004344bed80100000000000000000000000100000000000000b03fbed87f000000000000000200000000000000000000000000000000000000d3aec914588f4bcd000000000100000000000000000000000100000000000000f03fbed87f000000000000000200000000000000000000000000000000000000d3aec914588f4bcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a03fbed87f0000000000000000000000000000000000000000000000000000000000000000000000d3aec914588f4bcd08226f70550000000000000000000000000000000000000000000000000000001048bed87f0000008047bed87f0000006047bed87f000000e0ffffff80ffffff03000000000000000000000000000000891d6e7055000000d3aec914588f4bcd5047bed87f0000005047bed87f000000861d6e705500000003000000000000002f00008000000000f0a6ca15700000004047bed87f000000c0a0c31570000000891d6e7055000000d3aec914000000000100000000000000a047bed800000000f9006e70550000000100000000000000dc41bed87f000000db41bed87f0000004346bed87f000000da41bed87f0000000100000000000000d541bed87f00000001000000010000000100000001000000861d6e70550000004446bed87f0000002c42bed80300000000000000000000000100000000000000f041bed87f0000009b1e6e700100000000000000000000000000000000000000d3aec914588f4bcd8e1e6e70550000000d000000000000002f00008000000000f0a6ca15700000003048bed87f000000c0a0c31570000000661e6e700100000000000000000000002f000000000000001000000000000000e21e6e7055000000d3aec914588f4bcdb048bed87f000000b048bed87f000000e21e6e70550000002f000000000000002f00008000000000f0a6ca1570000000a048bed87f000000c0a0c315700000008e1e6e7055000000000000000000000022000000000000000000000000000000205827147000000022000000000000003c43bed87f0000003b43bed87f000000a347bed87f0000003a43bed87f00000001000000000000003543bed87f000000f048bed8010000000100000001000000dd1e6e7055000000a447bed87f0000008c43bed82f000000000000000000000001000000000000005043bed87f000000861d6e700300000000000000000000000000000000000000d3aec914588f4bcd721e6e7055000000f447bed87f000000981123141800000000000000000000000100000000000000a043bed87f000000861d6e70030000000000000000000000d042bed87f0000000000000000000000981123147000000098112314700000009811231470000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000504abed87f000000b049bed87f0000008049bed87f00000000000000000000004043bed87f0000000000000000000000991123147000000000000000000000008e1e6e70550000000d00000000000000a04abed87f000000000000000000000000000000000000000000000000000000504abed87f000000e049bed87f000000a049bed87f000000c8ffffff80ffffff591e6e70550000000d0000000000000098112314700000000000000000000000df1e6e7055000000010000000000000020582714700000002200000000000000f049bed87f000000c8ffffff80ffffff9811231470000000980023147000000098112314700000009811231470000000741e6e7055000000060000000000000009f12914700000000c00000000000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b149bed87f000000b149bed87f000000f049be317f000000f049bed87f000000f049bed87f000000b149bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000f149bed87f000000f049bed87f000000d3aec914588f4bcdfcb58a56000000006cbb687055000000160000000000000098112314700000009911231470000000991123147000000098112314700000009911231470000000fcb58a5600000000010000000000000000000000000000000100000000000000604a050000000000e845bed87f0000006048bed87f000000a046bed87f00000017000000000000002c48bed87f0000009046bed87f000000ac73c515700000009911231470000000d3aec914588f4bcd1048bed87f0000008047bed87f0000006047bed87f000000e8ffffff80ffffffffffffffffffffff99112314700000006148bed87f000000981123141500000008020000ffffffff6048bed87f000000160000000000000058112314700000001700000000000000a849bed87f0000000000000000000000284abed87f0000001700000000000000284abed87f000000284abed87f000000d249bed87f00000001000000000000009a112314700000001600000000000000284abed87f000000ffffffffffffffff284abed87f000000284abed87f000000284abed87f000000284abed87f0000001700000000000000ffffffffffffffff284abed87f000000284abed87f000000ff49bed87f000000284abed87f00000000000000000000000000000000000000d049bed87f000000d049bed87f000000004abed87f000000d049bed87f0000000100000000000000d049bed87f000000d049bed87f000000d049bed87f00000017000000000000000100000000000000ffffffffffffffff991123147000000001000000000000003448bed87f0000009911231470000000b0ca687055000000ffffffffffffffff010000000000000099112314700000007047bed87f000000d3aec914588f4bcdfcb58a56000000000100000000000000000000000000000050226f70550000000000000000000000b44b05000000000000102a1470000000861d6e70550000000300000000000000f0a6ca1570000000a047bed87f0000006c79c31570000000f048bed87f0000008048bed87f0000004048bed87f000000c8ffffff80ffffff0000000000000000d3aec914588f4bcdf849bed87f000000f0a6ca15700000000200000000000000085b0e1670000000e048bed87f0000002c6fc515700000009048bed87f000000d3aec914588f4bcd0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a89912314700000000000000000000000e9216f70550000008991231470000000e449bed87f0000008991231470000000000000000000000000000000000000008891231470000000899123147000000089912314700000008891231470000000170000000000000088912314700000008891231470000000d3aec914588f4bcd899123147000000016000000000000000000000000000000e9216f705500000088912314700000008891231470000000889123147000000088912314700000008891231470000000f0a6ca15700000000049bed87f0000006c79c31570000000889123147000000088912314700000008891231470000000889123147000000088912314700000008891231470000000889123147000000089912314700000008991231470000000085b0e1670000000404abed87f0000002c6fc51570000000c80a6f7055000000d3aec914588f4bcd0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a3b3325736d001b5b6d002c20776865726520002573203d202573000a526570650000000000000000000000000000000000000000000000008891231470000000000000000000000088912314700000008891231470000000889123147000000088912314700000000000000000000000889123147000000088912314700000008891231470000000470000000000000000502a1470000000d3aec914588f4bcdf05727147000000000b2221470000000604abed87f0000005c716c7055000000fcb58a56000000007c706c7055000000fcb58a5600000000ea4b050000000000
+stack: start: 7015fd3000 end: 7015fd7000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f838bed87f0000004038bed87f000000b863fd1570000000b863fd1570000000b863fd1570000000ec59cf15700000001c000000150000000e000000070000003063fd15700000001c58cf1570000000b863fd1570000000ec59cf1570000000100000000c00000008000000040000006063fd15700000007c58cf1570000000b863fd1570000000ec59cf1570000000080000000600000004000000020000009063fd1570000000dc58cf1570000000b863fd1570000000ec59cf157000000004000000030000000200000001000000d063fd1570000000c459cf15700000000100000000000000144abed87f0000004038bed87f0000004038bed87f000000144abed87f000000d3aec914588f4bcd1064fd157000000074fd6770550000004038bed87f0000004038bed87f000000ec84c41570000000e484c41570000000c484c4157000000000000000000000004064fd15700000004015c01570000000b67e0000000000000000000000000000705a0e1670000000185b0e167000000000000000000000000000000000000000705a0e16700000000000000000000000b77e0000b67e000000000000550000000030fd157000000050340000000000000010000000000000000000000000000000b222147000000000102a14700000000000000000000000000000000000000040fd6770550000004038bed87f000000000000000000000000a0fa1570000000010000000000000000000000000000000000000000000000e864fd15700000005064fd1570000000000000000000000000000000000000000000000000000000d3aec914588f4bcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+function: start: 0 end: 7015cf5760 name: unknown_start
+function: start: 7015cf5760 end: 7015cf57cc name: test_level_four
+function: start: 7015cf57cc end: 7015cf582c name: test_level_three
+function: start: 7015cf582c end: 7015cf588c name: test_level_two
+function: start: 7015cf588c end: 7015cf58ec name: test_level_one
+function: start: 7015cf58ec end: 7015cf5968 name: test_recursive_call
+function: start: 7015cf5968 end: ffffffffffffffff name: test_get_context_and_wait
+function: start: ffffffffffffffff end: ffffffffffffffff name: unknown_end
diff --git a/libbacktrace/testdata/arm/libbacktrace_test_arm_exidx.so b/libbacktrace/testdata/arm/libbacktrace_test_arm_exidx.so
new file mode 100755
index 0000000..454b032
--- /dev/null
+++ b/libbacktrace/testdata/arm/libbacktrace_test_arm_exidx.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/libbacktrace_test_debug_frame.so b/libbacktrace/testdata/arm/libbacktrace_test_debug_frame.so
new file mode 100755
index 0000000..787f2cb
--- /dev/null
+++ b/libbacktrace/testdata/arm/libbacktrace_test_debug_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/libbacktrace_test_gnu_debugdata.so b/libbacktrace/testdata/arm/libbacktrace_test_gnu_debugdata.so
new file mode 100755
index 0000000..9340d98
--- /dev/null
+++ b/libbacktrace/testdata/arm/libbacktrace_test_gnu_debugdata.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/offline_testdata b/libbacktrace/testdata/arm/offline_testdata
new file mode 100644
index 0000000..43d305a
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata
@@ -0,0 +1,105 @@
+pid: 32232 tid: 32233
+map: start: aad19000 end: aad6c000 offset: 0 load_base: 0 flags: 5 name: /data/backtrace_test32
+map: start: aad6c000 end: aad6e000 offset: 52000 load_base: 0 flags: 1 name: /data/backtrace_test32
+map: start: aad6e000 end: aad6f000 offset: 54000 load_base: 0 flags: 3 name: /data/backtrace_test32
+map: start: e7380000 end: e7400000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e745f000 end: e7463000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libnetd_client.so
+map: start: e7463000 end: e7464000 offset: 3000 load_base: 0 flags: 1 name: /system/lib/libnetd_client.so
+map: start: e7464000 end: e7465000 offset: 4000 load_base: 0 flags: 3 name: /system/lib/libnetd_client.so
+map: start: e7480000 end: e7500000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e7558000 end: e756c000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libunwind.so
+map: start: e756c000 end: e756d000 offset: 0 load_base: 0 flags: 0 name:
+map: start: e756d000 end: e756e000 offset: 14000 load_base: 0 flags: 1 name: /system/lib/libunwind.so
+map: start: e756e000 end: e756f000 offset: 15000 load_base: 0 flags: 3 name: /system/lib/libunwind.so
+map: start: e756f000 end: e75b5000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: e75d4000 end: e75e1000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libbase.so
+map: start: e75e1000 end: e75e2000 offset: c000 load_base: 0 flags: 1 name: /system/lib/libbase.so
+map: start: e75e2000 end: e75e3000 offset: d000 load_base: 0 flags: 3 name: /system/lib/libbase.so
+map: start: e7600000 end: e7616000 offset: 0 load_base: 0 flags: 5 name: /system/lib/liblzma.so
+map: start: e7616000 end: e7617000 offset: 15000 load_base: 0 flags: 1 name: /system/lib/liblzma.so
+map: start: e7617000 end: e7618000 offset: 16000 load_base: 0 flags: 3 name: /system/lib/liblzma.so
+map: start: e7618000 end: e761d000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: e7647000 end: e7656000 offset: 0 load_base: 0 flags: 5 name: /system/lib/liblog.so
+map: start: e7656000 end: e7657000 offset: e000 load_base: 0 flags: 1 name: /system/lib/liblog.so
+map: start: e7657000 end: e7658000 offset: f000 load_base: 0 flags: 3 name: /system/lib/liblog.so
+map: start: e7681000 end: e76a2000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libm.so
+map: start: e76a2000 end: e76a3000 offset: 20000 load_base: 0 flags: 1 name: /system/lib/libm.so
+map: start: e76a3000 end: e76a4000 offset: 21000 load_base: 0 flags: 3 name: /system/lib/libm.so
+map: start: e76eb000 end: e76ee000 offset: 0 load_base: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: e76ee000 end: e76ef000 offset: 2000 load_base: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: e76ef000 end: e76f0000 offset: 3000 load_base: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: e7712000 end: e771f000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libbacktrace.so
+map: start: e771f000 end: e7720000 offset: 0 load_base: 0 flags: 0 name:
+map: start: e7720000 end: e7721000 offset: d000 load_base: 0 flags: 1 name: /system/lib/libbacktrace.so
+map: start: e7721000 end: e7722000 offset: e000 load_base: 0 flags: 3 name: /system/lib/libbacktrace.so
+map: start: e7761000 end: e7778000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libutils.so
+map: start: e7778000 end: e7779000 offset: 16000 load_base: 0 flags: 1 name: /system/lib/libutils.so
+map: start: e7779000 end: e777a000 offset: 17000 load_base: 0 flags: 3 name: /system/lib/libutils.so
+map: start: e77a5000 end: e782d000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libc.so
+map: start: e782d000 end: e7831000 offset: 87000 load_base: 0 flags: 1 name: /system/lib/libc.so
+map: start: e7831000 end: e7833000 offset: 8b000 load_base: 0 flags: 3 name: /system/lib/libc.so
+map: start: e7833000 end: e7834000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: e7834000 end: e7835000 offset: 0 load_base: 0 flags: 1 name: [anon:.bss]
+map: start: e7835000 end: e783b000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: e7845000 end: e8437000 offset: 0 load_base: 2b000 flags: 5 name: /system/lib/libLLVM.so
+map: start: e8437000 end: e8438000 offset: 0 load_base: 0 flags: 0 name:
+map: start: e8438000 end: e848a000 offset: bf2000 load_base: 0 flags: 1 name: /system/lib/libLLVM.so
+map: start: e848a000 end: e848b000 offset: c44000 load_base: 0 flags: 3 name: /system/lib/libLLVM.so
+map: start: e848b000 end: e84a1000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: e84eb000 end: e84f7000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libcutils.so
+map: start: e84f7000 end: e84f8000 offset: 0 load_base: 0 flags: 0 name:
+map: start: e84f8000 end: e84f9000 offset: c000 load_base: 0 flags: 1 name: /system/lib/libcutils.so
+map: start: e84f9000 end: e84fa000 offset: d000 load_base: 0 flags: 3 name: /system/lib/libcutils.so
+map: start: e852e000 end: e85b3000 offset: 0 load_base: 2000 flags: 5 name: /system/lib/libc++.so
+map: start: e85b3000 end: e85b4000 offset: 0 load_base: 0 flags: 0 name:
+map: start: e85b4000 end: e85b8000 offset: 85000 load_base: 0 flags: 1 name: /system/lib/libc++.so
+map: start: e85b8000 end: e85b9000 offset: 89000 load_base: 0 flags: 3 name: /system/lib/libc++.so
+map: start: e85b9000 end: e85ba000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
+map: start: e85ce000 end: e85cf000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e85e4000 end: e85e5000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e8607000 end: e8608000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e8680000 end: e8700000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e870d000 end: e8719000 offset: 0 load_base: 0 flags: 3 name:
+map: start: e8719000 end: e871b000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e871b000 end: e873b000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: e873b000 end: e875b000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e875b000 end: e875c000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e875c000 end: e875d000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
+map: start: e875d000 end: e875e000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e875e000 end: e875f000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e875f000 end: e877f000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: e877f000 end: e879f000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e879f000 end: e87a0000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a0000 end: e87a1000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a1000 end: e87a2000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a2000 end: e87a3000 offset: 0 load_base: 0 flags: 0 name:
+map: start: e87a3000 end: e87a4000 offset: 0 load_base: 0 flags: 3 name:
+map: start: e87a4000 end: e87a5000 offset: 0 load_base: 0 flags: 0 name:
+map: start: e87a5000 end: e87a6000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: e87a6000 end: e87a7000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e87a7000 end: e87a8000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a8000 end: e87a9000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a9000 end: e87aa000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87aa000 end: e87ab000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87ab000 end: e87ac000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e87ac000 end: e87ad000 offset: 0 load_base: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: e87ad000 end: e87af000 offset: 0 load_base: 0 flags: 3 name: [anon:thread signal stack]
+map: start: e87af000 end: e87b0000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
+map: start: e87b0000 end: e880d000 offset: 0 load_base: 0 flags: 5 name: /system/bin/linker
+map: start: e880d000 end: e880f000 offset: 5c000 load_base: 0 flags: 1 name: /system/bin/linker
+map: start: e880f000 end: e8810000 offset: 5e000 load_base: 0 flags: 3 name: /system/bin/linker
+map: start: e8810000 end: e8812000 offset: 0 load_base: 0 flags: 3 name:
+map: start: e8812000 end: e8813000 offset: 0 load_base: 0 flags: 1 name:
+map: start: e8813000 end: e8815000 offset: 0 load_base: 0 flags: 3 name:
+map: start: ff886000 end: ff8a9000 offset: 0 load_base: 0 flags: 3 name: [stack]
+map: start: ffff0000 end: ffff1000 offset: 0 load_base: 0 flags: 5 name: [vectors]
+registers: 64 34868affdc8871e8150000001c0000001c000000150000000e00000007000000e08771e834868aff2354d2aa24f9ffffdc8871e88c8771e875b86ee778ba6ee7
+stack: start: e8715000 end: e8719000 size: 16384 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc8871e87dba6ee734868affdc8871e8dc8871e85dba6ee7070000000e000000150000001c000000dc8871e85dba6ee71c000000150000000e00000007000000100000000c0000000800000004000000ddb86ee75dba6ee7dc8871e804000000080000000c00000010000000dc8871e85dba6ee7100000000c000000080000000400000008000000060000000400000002000000288871e835b96ee75dba6ee7dc8871e802000000040000000600000008000000dc8871e85dba6ee70800000006000000040000000200000004000000030000000200000001000000708871e88db96ee75dba6ee7dc8871e801000000020000000300000004000000dc8871e85dba6ee70400000003000000020000000100000004000000208971e8208971e878000000e87d00003dba6ee75dba6ee7dc8871e878000000c5807ce7fc7183e734868aff78868aff78868aff34868aff34868aff78868affe0879437208971e84154d2aa0020000034868aff34868aff34868aff78000000c9b87ee7b1b87ee7a3f47be7288971e8b1b87ee7208971e800000000f83481e800000000e97d0000e87d000000000000005071e82039000000100000000000000000000000000000000000002354d2aa34868aff00000000002071e801000000000000000000000000000000708971e8208971e8000000000000000000000000e0879437000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+function: start: 0 end: e76eb835 name: unknown_start
+function: start: e76eb835 end: e76eb88d name: test_level_four
+function: start: e76eb88d end: e76eb8e5 name: test_level_three
+function: start: e76eb8e5 end: e76eb93d name: test_level_two
+function: start: e76eb93d end: e76eb995 name: test_level_one
+function: start: e76eb995 end: e76eb9f1 name: test_recursive_call
+function: start: e76eb9f1 end: ffffffff name: test_get_context_and_wait
+function: start: ffffffff end: ffffffff name: unknown_end
diff --git a/libbacktrace/testdata/x86/libbacktrace_test_debug_frame.so b/libbacktrace/testdata/x86/libbacktrace_test_debug_frame.so
new file mode 100755
index 0000000..a6f3b29
--- /dev/null
+++ b/libbacktrace/testdata/x86/libbacktrace_test_debug_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/x86/libbacktrace_test_gnu_debugdata.so b/libbacktrace/testdata/x86/libbacktrace_test_gnu_debugdata.so
new file mode 100755
index 0000000..ea58dfb
--- /dev/null
+++ b/libbacktrace/testdata/x86/libbacktrace_test_gnu_debugdata.so
Binary files differ
diff --git a/libbacktrace/testdata/x86/offline_testdata b/libbacktrace/testdata/x86/offline_testdata
new file mode 100644
index 0000000..3e4e06c
--- /dev/null
+++ b/libbacktrace/testdata/x86/offline_testdata
@@ -0,0 +1,82 @@
+pid: 34545 tid: 34546
+map: start: f705a000 end: f705c000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f705c000 end: f707f000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f707f000 end: f7080000 offset: 22000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7080000 end: f7081000 offset: 23000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7081000 end: f7088000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f7088000 end: f7230000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7230000 end: f7231000 offset: 1a8000 load_base: 0 flags: 0 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7231000 end: f7233000 offset: 1a8000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7233000 end: f7234000 offset: 1aa000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7234000 end: f7237000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f7237000 end: f727b000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727b000 end: f727c000 offset: 43000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727c000 end: f727d000 offset: 44000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727d000 end: f7299000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f7299000 end: f729a000 offset: 1b000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f729a000 end: f72b8000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b8000 end: f72b9000 offset: 1e000 load_base: 0 flags: 0 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b9000 end: f72bb000 offset: 1e000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bb000 end: f72bc000 offset: 20000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bc000 end: f72bd000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f72bd000 end: f72e0000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e0000 end: f72e1000 offset: 22000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e1000 end: f72e2000 offset: 23000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e2000 end: f72e5000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e5000 end: f72e6000 offset: 2000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e6000 end: f72e7000 offset: 3000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e7000 end: f72ee000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ee000 end: f72ef000 offset: 6000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ef000 end: f72f0000 offset: 7000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72f0000 end: f7308000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7308000 end: f7309000 offset: 18000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7309000 end: f730a000 offset: 19000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f730a000 end: f730c000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f732f000 end: f7331000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f7331000 end: f7425000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7425000 end: f7426000 offset: f4000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7426000 end: f742a000 offset: f4000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742a000 end: f742b000 offset: f8000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742b000 end: f742d000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f742d000 end: f7446000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7446000 end: f7447000 offset: 18000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7447000 end: f7448000 offset: 19000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7448000 end: f7457000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f7457000 end: f745c000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745c000 end: f745d000 offset: 4000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745d000 end: f745e000 offset: 5000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745e000 end: f7467000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7467000 end: f7468000 offset: 9000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7468000 end: f7469000 offset: 9000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7469000 end: f746a000 offset: a000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f746a000 end: f7477000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7477000 end: f7478000 offset: c000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7478000 end: f7479000 offset: d000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7479000 end: f7489000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f7489000 end: f748a000 offset: f000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748a000 end: f748b000 offset: 10000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748b000 end: f748c000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f748c000 end: f748d000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748d000 end: f748e000 offset: 0 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748e000 end: f748f000 offset: 1000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748f000 end: f7491000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f7491000 end: f74b1000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b1000 end: f74b2000 offset: 1f000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b2000 end: f74b3000 offset: 20000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b3000 end: f77c6000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77c6000 end: f77c7000 offset: 0 load_base: ffffe000 flags: 5 name: [vdso]
+map: start: f77c7000 end: f77d4000 offset: 313000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d4000 end: f77d5000 offset: 320000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d5000 end: f77d6000 offset: 0 load_base: 0 flags: 3 name:
+map: start: f7ec6000 end: f7ee7000 offset: 0 load_base: 0 flags: 3 name: [heap]
+map: start: ffe4e000 end: ffe70000 offset: 0 load_base: 0 flags: 3 name: [stack]
+registers: 348 00000000abdae6fff83b7df704000000a6ec77f7abdae6ff00000000afdae6ff78dae6ff150000001c000000b8f132f7a0f132f7d0df48f7a0ca48f728d9e6ff000000008c8decf78c8decf7ceca48f7a8dae6ff8d8decf70a0000000000000014dae6ff8c8decf78c8decf78c8decf78c8decf78c8decf7a9dae6ff06000000c03a23f78c8decf78c8decf78c8decf78c8decf78c8decf78c8decf78c8decf78d8decf78d8decf7c03a23f7543b23f7000033f75f5f0ff7c03a23f7503423f77000000098000000020000000f2700006c0000000e00000080000000000000008c8decf7000000008c8decf77f03ffff0000ffffffffffff0000000000000000000000000000ffff040000000f27000008000000003023f78d8decf7f069ec000046bdaa308decf715537df7f83b7df78c8decf74c1c71f78c8decf715537df70000000000000000f069ecf7f83b7df75064ecf792e671f7f069ecf7
+stack: start: f732c000 end: f7330000 size: 16384 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d9d49f761b009f71e382ff7000000000000000000000000000000000000000002000000f6372ff704c82bf70000000000204bf7f0a908f7ceca48f728d9e6fff4a449f70000000020f332f720f332f7d0df48f7f8f132f794c748f720f332f70c144205978142a8d4be08f7d0df48f720f332f7a0ca48f71c000000150000000e000000070000001c000000a0ca48f7d0df48f748f232f739c848f7070000000e000000150000001c000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7100000000c000000080000000400000010000000a0ca48f7d0df48f798f232f7c9c848f704000000080000000c00000010000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f70800000006000000040000000200000008000000a0ca48f7d0df48f7e8f232f759c948f702000000040000000600000008000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7040000000300000002000000010000000046bdaa00000000d0df48f738f332f77cca48f701000000020000000300000004000000a0ca48f720f332f700000000f83b7df7696d4df7d0df48f788dae6ff28d9e6ff28d9e6ff88dae6ff58f332f70046bdaa40fb32f7f83b7df758f332f78d6d4df728d9e6ff88dae6fff83b7df728d9e6ff28d9e6ff009030f728f432f7726f2ff728d9e6ff40fb32f740fb32f740fb32f790f332f700000000000000000000000000000000000000000000000000000000009030f740fb32f7000f3d0028f432f703b12c75032f144e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a06e2ff700000000000f3d00000000008e3f17f740fb32f7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a03823f7c4fd32f700000000e0341df7e02e1df7e03d1df70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040fb32f7188eecf740fb32f70100000030647cf70046bdaa28658876000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060a705f7a4b130f7f2860000f1860000b0fb32f7ecffffff000000000000000090f332f7000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ccfb32f70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c2638cfa7f3e1d0000000000000000000000000000000000406d4df728d9e6ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c032f7004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+function: start: 0 end: f748c740 name: unknown_start
+function: start: f748c740 end: f748c7c0 name: test_level_four
+function: start: f748c7c0 end: f748c850 name: test_level_three
+function: start: f748c850 end: f748c8e0 name: test_level_two
+function: start: f748c8e0 end: f748c970 name: test_level_one
+function: start: f748c970 end: f748ca10 name: test_recursive_call
+function: start: f748ca10 end: ffffffff name: test_get_context_and_wait
+function: start: ffffffff end: ffffffff name: unknown_end
diff --git a/libbacktrace/testdata/x86_64/libbacktrace_test_eh_frame.so b/libbacktrace/testdata/x86_64/libbacktrace_test_eh_frame.so
new file mode 100755
index 0000000..f116658
--- /dev/null
+++ b/libbacktrace/testdata/x86_64/libbacktrace_test_eh_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/x86_64/offline_testdata b/libbacktrace/testdata/x86_64/offline_testdata
new file mode 100644
index 0000000..baf6450
--- /dev/null
+++ b/libbacktrace/testdata/x86_64/offline_testdata
@@ -0,0 +1,93 @@
+pid: 25683 tid: 25692
+map: start: 7fd5aa784000 end: 7fd5aa93e000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aa93e000 end: 7fd5aab3e000 offset: 1ba000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab3e000 end: 7fd5aab42000 offset: 1ba000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab42000 end: 7fd5aab44000 offset: 1be000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab44000 end: 7fd5aab49000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5aab49000 end: 7fd5aac4e000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aac4e000 end: 7fd5aae4d000 offset: 105000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4d000 end: 7fd5aae4e000 offset: 104000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4e000 end: 7fd5aae4f000 offset: 105000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4f000 end: 7fd5aae65000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5aae65000 end: 7fd5ab064000 offset: 16000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab064000 end: 7fd5ab065000 offset: 15000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab065000 end: 7fd5ab08a000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab08a000 end: 7fd5ab289000 offset: 25000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab289000 end: 7fd5ab28d000 offset: 24000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28d000 end: 7fd5ab28e000 offset: 28000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28e000 end: 7fd5ab2b0000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab2b0000 end: 7fd5ab4af000 offset: 22000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4af000 end: 7fd5ab4b0000 offset: 21000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b0000 end: 7fd5ab4b1000 offset: 22000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b1000 end: 7fd5ab4b4000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab4b4000 end: 7fd5ab6b3000 offset: 3000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b3000 end: 7fd5ab6b4000 offset: 2000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b4000 end: 7fd5ab6b5000 offset: 3000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b5000 end: 7fd5ab6bc000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab6bc000 end: 7fd5ab8bb000 offset: 7000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bb000 end: 7fd5ab8bc000 offset: 6000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bc000 end: 7fd5ab8bd000 offset: 7000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bd000 end: 7fd5ab8d6000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5ab8d6000 end: 7fd5abad5000 offset: 19000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad5000 end: 7fd5abad6000 offset: 18000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad6000 end: 7fd5abad7000 offset: 19000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad7000 end: 7fd5abadb000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5abadb000 end: 7fd5abafe000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abb17000 end: 7fd5abb1a000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5abb1a000 end: 7fd5abb40000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb40000 end: 7fd5abb41000 offset: 25000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb41000 end: 7fd5abb42000 offset: 26000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb42000 end: 7fd5abb4b000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5abb6a000 end: 7fd5abb70000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5abb70000 end: 7fd5abc62000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc62000 end: 7fd5abc63000 offset: f2000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc63000 end: 7fd5abc6b000 offset: f2000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6b000 end: 7fd5abc6c000 offset: fa000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6c000 end: 7fd5abc70000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5abc70000 end: 7fd5abc8d000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8d000 end: 7fd5abc8e000 offset: 1c000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8e000 end: 7fd5abc8f000 offset: 1d000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8f000 end: 7fd5abcb8000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5abcb8000 end: 7fd5abcbe000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbe000 end: 7fd5abcbf000 offset: 6000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbf000 end: 7fd5abcc0000 offset: 6000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc0000 end: 7fd5abcc1000 offset: 7000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc1000 end: 7fd5abcc2000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5abcc2000 end: 7fd5abccd000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccd000 end: 7fd5abcce000 offset: b000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcce000 end: 7fd5abccf000 offset: b000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccf000 end: 7fd5abcd0000 offset: c000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcd0000 end: 7fd5abcdf000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abcdf000 end: 7fd5abce0000 offset: f000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce0000 end: 7fd5abce1000 offset: f000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce1000 end: 7fd5abce2000 offset: 10000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce2000 end: 7fd5abcf5000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf5000 end: 7fd5abcf6000 offset: 12000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf6000 end: 7fd5abcf7000 offset: 13000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf7000 end: 7fd5abcf8000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf8000 end: 7fd5abcf9000 offset: 1000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf9000 end: 7fd5abcfa000 offset: 1000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfa000 end: 7fd5abcfb000 offset: 2000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfb000 end: 7fd5abcfd000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5abcfd000 end: 7fd5abcfe000 offset: 22000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcfe000 end: 7fd5abcff000 offset: 23000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcff000 end: 7fd5abd00000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5abd00000 end: 7fd5ac053000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac053000 end: 7fd5ac054000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5ac054000 end: 7fd5ac06f000 offset: 353000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac06f000 end: 7fd5ac070000 offset: 36e000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac070000 end: 7fd5ac071000 offset: 0 load_base: 0 flags: 3 name:
+map: start: 7fd5ad54e000 end: 7fd5ad56f000 offset: 0 load_base: 0 flags: 3 name: [heap]
+map: start: 7ffcf47ed000 end: 7ffcf480f000 offset: 0 load_base: 0 flags: 3 name: [stack]
+map: start: 7ffcf48d5000 end: 7ffcf48d7000 offset: 0 load_base: ffffffffff700000 flags: 5 name: [vdso]
+map: start: ffffffffff600000 end: ffffffffff601000 offset: 0 load_base: 0 flags: 5 name: [vsyscall]
+registers: 936 010000000000000028b480f4fc7f000028b480f4fc7f000028b480f4fc7f0000b92455add57f0000b07bcfabd57f000098deb6abd57f0000b82455add57f0000010000000000000000000000000000000000000000000000c0e354add57f000000e7b6abd57f0000c8b080f4fc7f00000e0000000000000080ddb6abd57f000000000000000000001500000000000000b07bcfabd57f00001c0000000000000060ddb6abd57f0000d07bcfabd57f0000a0b180f4fc7f000028b480f4fc7f000028b480f4fc7f000028b480f4fc7f000078b480f4fc7f000078b480f4fc7f00007f03ffff0000ffffffffffff000000000000000000000000801f0000fc7f000078b480f4fc7f000060b280f4fc7f000000006a80f3f73cf110b480f4fc7f0000de66d6abd57f00000000000000000000000000000000000000006a80f3f73cf128b480f4fc7f0000782455add57f0000fd96d6abd57f0000092555add57f0000000000000000000057b480f4fc7f0000092555add57f000060b480f4fc7f00006994d6abd57f0000b0e0c6abd57f000071b280f4fc7f000070b280f4fc7f0000256c640000000000a8b180f4fc7f000090b480f4fc7f0000082555add57f0000092555add57f0000092555add57f0000082555add57f00001700000000000000082555add57f0000082555add57f0000f93cfcabd57f0000092555add57f000016000000000000000000000000000000090c07acd57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f000010b380f4fc7f000078b480f4fc7f00000000000000000000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000092555add57f0000092555add57f0000b92455add57f000028b480f4fc7f0000c800000000000000014c7f0b0380ffff0d000000fc7f0000030000000000000033000000d57f000000b480f4fc7f00000000000000000000a0e154ad5b000000000000000000000000000000000000006e000000770000000000000000000000082555add57f00000000000000000000082555add57f0000082555add57f0000082555add57f0000082555add57f00000000000000000000082555add57f0000082555add57f0000082555add57f00006027b4aad57f0000c800000000000000a0e154add57f0000c0e354add57f0000092555add57f000000006a80f3f73cf1e0e054add57f0000e0e554add57f0000d14df6abd57f0000
+stack: start: 7fd5abb6b000 end: 7fd5abb6f000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c4eaeabd57f00000000000000000000978142a8000000000f0000000000000012000000000000003888b4abd57f0000e657aeabd57f00000000000000000000a0dcb6abd57f0000307d78aad57f0000b0ddb6abd57f000028d978aad57f0000060aa10200000000a0ddb6abd57f0000000000000000000000000000000000002091b1abd57f0000e094b4abd57f000017008cabd57f0000c84d79aad57f000028e28babd57f00000000000005000000d503000001000000000000000000000068deb6abd57f000040deb6abd57f00002091b1abd57f00000100000000000000e0f3c6abd57f000088f0c6abd57f00006159aeabd57f000000000000000000002091b1abd57f000005000000000000000000000000000000010000000000000088f0c6abd57f00000000000000000000d07bcfabd57f00000000000000000000000000000000000098deb6abd57f000098deb6abd57f0000b0ddb6abd57f00006179cfabd57f000098deb6abd57f0000b07bcfabd57f00001c000000150000000e00000007000000f0ddb6abd57f0000e179cfabd57f00000000000000000000150000001c00000098deb6abd57f0000b07bcfabd57f0000100000000c000000080000000400000030deb6abd57f0000417acfabd57f000000000000000000000c0000001000000098deb6abd57f0000b07bcfabd57f00000800000006000000040000000200000070deb6abd57f0000a17acfabd57f00000000000000000000060000000800000098deb6abd57f0000b07bcfabd57f000004000000030000000200000001000000b0deb6abd57f0000817bcfabd57f0000000000000000000074b480f4fc7f0000c8b080f4fc7f0000c8b080f4fc7f000074b480f4fc7f000000006a80f3f73cf1d0deb6abd57f00002a52d5abd57f0000c8b080f4fc7f0000c8b080f4fc7f0000000000000000000084518cabd57f0000000000000000000000e7b6abd57f000000e7b6abd57f00008f990b1e3bad5a6700000000000000000000000000000000c0e354add57f000000e7b6abd57f00008f99cba356faf1988f9991bc23faf1980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f00007de387aad57f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006030b4aad57f0000b8edb6abd57f00000000000000000000000000000000000080b88eaad57f0000000000000000000080b28eaad57f0000000000000000000080c18eaad57f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f0000402555add57f000000e7b6abd57f00000100000000000000000000000000000000006a80f3f73cf1058f9d56adb3c7cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000407ab1abd57f000030a3adabd57f00005c64000053640000e0e9b6abd57f0000e0e9b6abd57f0000e0ffffffffffffff00000000000000000000000000000000f0deb6abd57f00000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010eab6abd57f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c46ad90f52391d00000000000000000000000000000000000000000000000000f051d5abd57f0000c8b080f4fc7f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0b6abd57f000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+function: start: 0 end: 7fd5abcf7930 name: unknown_start
+function: start: 7fd5abcf7930 end: 7fd5abcf7990 name: test_level_four
+function: start: 7fd5abcf7990 end: 7fd5abcf79f0 name: test_level_three
+function: start: 7fd5abcf79f0 end: 7fd5abcf7a50 name: test_level_two
+function: start: 7fd5abcf7a50 end: 7fd5abcf7ab0 name: test_level_one
+function: start: 7fd5abcf7ab0 end: 7fd5abcf7b30 name: test_recursive_call
+function: start: 7fd5abcf7b30 end: ffffffffffffffff name: test_get_context_and_wait
+function: start: ffffffffffffffff end: ffffffffffffffff name: unknown_end
diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h
index df83581..9590657 100644
--- a/libbacktrace/thread_utils.h
+++ b/libbacktrace/thread_utils.h
@@ -19,6 +19,10 @@
#include <unistd.h>
+#if !defined(__ANDROID__)
+#include <cutils/threads.h>
+#endif
+
__BEGIN_DECLS
int tgkill(int tgid, int tid, int sig);
diff --git a/libbinderwrapper/Android.mk b/libbinderwrapper/Android.mk
new file mode 100644
index 0000000..b38d262
--- /dev/null
+++ b/libbinderwrapper/Android.mk
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+binderwrapperCommonCFlags := -Wall -Werror -Wno-unused-parameter
+binderwrapperCommonCFlags += -Wno-sign-promo # for libchrome
+binderwrapperCommonExportCIncludeDirs := $(LOCAL_PATH)/include
+binderwrapperCommonCIncludes := $(LOCAL_PATH)/include
+binderwrapperCommonSharedLibraries := \
+ libbinder \
+ libchrome \
+ libutils \
+
+# libbinderwrapper shared library
+# ========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbinderwrapper
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
+LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
+LOCAL_SHARED_LIBRARIES := $(binderwrapperCommonSharedLibraries)
+LOCAL_SRC_FILES := \
+ binder_wrapper.cc \
+ real_binder_wrapper.cc \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# libbinderwrapper_test_support shared library
+# ========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbinderwrapper_test_support
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
+LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
+LOCAL_STATIC_LIBRARIES := libgtest
+LOCAL_SHARED_LIBRARIES := \
+ $(binderwrapperCommonSharedLibraries) \
+ libbinderwrapper \
+
+LOCAL_SRC_FILES := \
+ binder_test_base.cc \
+ stub_binder_wrapper.cc \
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libbinderwrapper/binder_test_base.cc b/libbinderwrapper/binder_test_base.cc
new file mode 100644
index 0000000..af93a04
--- /dev/null
+++ b/libbinderwrapper/binder_test_base.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binderwrapper/binder_test_base.h>
+
+#include <binderwrapper/binder_wrapper.h>
+#include <binderwrapper/stub_binder_wrapper.h>
+
+namespace android {
+
+BinderTestBase::BinderTestBase() : binder_wrapper_(new StubBinderWrapper()) {
+ // Pass ownership.
+ BinderWrapper::InitForTesting(binder_wrapper_);
+}
+
+BinderTestBase::~BinderTestBase() {
+ BinderWrapper::Destroy();
+}
+
+} // namespace android
diff --git a/libbinderwrapper/binder_wrapper.cc b/libbinderwrapper/binder_wrapper.cc
new file mode 100644
index 0000000..ca9c1df
--- /dev/null
+++ b/libbinderwrapper/binder_wrapper.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binderwrapper/binder_wrapper.h>
+
+#include <base/logging.h>
+
+#include "real_binder_wrapper.h"
+
+namespace android {
+
+// Singleton instance.
+BinderWrapper* BinderWrapper::instance_ = nullptr;
+
+// static
+void BinderWrapper::Create() {
+ CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
+ instance_ = new RealBinderWrapper();
+}
+
+// static
+void BinderWrapper::InitForTesting(BinderWrapper* wrapper) {
+ CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
+ instance_ = wrapper;
+}
+
+// static
+void BinderWrapper::Destroy() {
+ CHECK(instance_) << "Not initialized; missing call to Create()?";
+ delete instance_;
+ instance_ = nullptr;
+}
+
+// static
+BinderWrapper* BinderWrapper::Get() {
+ CHECK(instance_) << "Not initialized; missing call to Create()?";
+ return instance_;
+}
+
+// static
+BinderWrapper* BinderWrapper::GetOrCreateInstance() {
+ if (!instance_)
+ instance_ = new RealBinderWrapper();
+ return instance_;
+}
+
+} // namespace android
diff --git a/libbinderwrapper/include/binderwrapper/binder_test_base.h b/libbinderwrapper/include/binderwrapper/binder_test_base.h
new file mode 100644
index 0000000..06543de
--- /dev/null
+++ b/libbinderwrapper/include/binderwrapper/binder_test_base.h
@@ -0,0 +1,45 @@
+/*
+ * 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 SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
+
+#include <base/macros.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class StubBinderWrapper;
+
+// Class that can be inherited from (or aliased via typedef/using) when writing
+// tests that use StubBinderManager.
+class BinderTestBase : public ::testing::Test {
+ public:
+ BinderTestBase();
+ ~BinderTestBase() override;
+
+ StubBinderWrapper* binder_wrapper() { return binder_wrapper_; }
+
+ protected:
+ StubBinderWrapper* binder_wrapper_; // Not owned.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BinderTestBase);
+};
+
+} // namespace android
+
+#endif // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
diff --git a/libbinderwrapper/include/binderwrapper/binder_wrapper.h b/libbinderwrapper/include/binderwrapper/binder_wrapper.h
new file mode 100644
index 0000000..a104bff
--- /dev/null
+++ b/libbinderwrapper/include/binderwrapper/binder_wrapper.h
@@ -0,0 +1,88 @@
+/*
+ * 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 SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+#include <base/callback.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class BBinder;
+class IBinder;
+
+// Wraps libbinder to make it testable.
+// NOTE: Static methods of this class are not thread-safe.
+class BinderWrapper {
+ public:
+ virtual ~BinderWrapper() {}
+
+ // Creates and initializes the singleton (using a wrapper that communicates
+ // with the real binder system).
+ static void Create();
+
+ // Initializes |wrapper| as the singleton, taking ownership of it. Tests that
+ // want to inject their own wrappers should call this instead of Create().
+ static void InitForTesting(BinderWrapper* wrapper);
+
+ // Destroys the singleton. Must be called before calling Create() or
+ // InitForTesting() a second time.
+ static void Destroy();
+
+ // Returns the singleton instance previously created by Create() or set by
+ // InitForTesting().
+ static BinderWrapper* Get();
+
+ // Returns the singleton instance if it was previously created by Create() or
+ // set by InitForTesting(), or creates a new one by calling Create().
+ static BinderWrapper* GetOrCreateInstance();
+
+ // Gets the binder for communicating with the service identified by
+ // |service_name|, returning null immediately if it doesn't exist.
+ virtual sp<IBinder> GetService(const std::string& service_name) = 0;
+
+ // Registers |binder| as |service_name| with the service manager.
+ virtual bool RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) = 0;
+
+ // Creates a local binder object.
+ virtual sp<BBinder> CreateLocalBinder() = 0;
+
+ // Registers |callback| to be invoked when |binder| dies. If another callback
+ // is currently registered for |binder|, it will be replaced.
+ virtual bool RegisterForDeathNotifications(
+ const sp<IBinder>& binder,
+ const ::base::Closure& callback) = 0;
+
+ // Unregisters the callback, if any, for |binder|.
+ virtual bool UnregisterForDeathNotifications(const sp<IBinder>& binder) = 0;
+
+ // When called while in a transaction, returns the caller's UID or PID.
+ virtual uid_t GetCallingUid() = 0;
+ virtual pid_t GetCallingPid() = 0;
+
+ private:
+ static BinderWrapper* instance_;
+};
+
+} // namespace android
+
+#endif // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h b/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
new file mode 100644
index 0000000..9d4578e
--- /dev/null
+++ b/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
@@ -0,0 +1,133 @@
+/*
+ * 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 SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binderwrapper/binder_wrapper.h>
+
+namespace android {
+
+// Stub implementation of BinderWrapper for testing.
+//
+// Example usage:
+//
+// First, assuming a base IFoo binder interface, create a stub class that
+// derives from BnFoo to implement the receiver side of the communication:
+//
+// class StubFoo : public BnFoo {
+// public:
+// ...
+// status_t doSomething(int arg) override {
+// // e.g. save passed-in value for later inspection by tests.
+// return OK;
+// }
+// };
+//
+// Next, from your test code, inject a StubBinderManager either directly or by
+// inheriting from the BinderTestBase class:
+//
+// StubBinderWrapper* wrapper = new StubBinderWrapper();
+// BinderWrapper::InitForTesting(wrapper); // Takes ownership.
+//
+// Also from your test, create a StubFoo and register it with the wrapper:
+//
+// StubFoo* foo = new StubFoo();
+// sp<IBinder> binder(foo);
+// wrapper->SetBinderForService("foo", binder);
+//
+// The code being tested can now use the wrapper to get the stub and call it:
+//
+// sp<IBinder> binder = BinderWrapper::Get()->GetService("foo");
+// CHECK(binder.get());
+// sp<IFoo> foo = interface_cast<IFoo>(binder);
+// CHECK_EQ(foo->doSomething(3), OK);
+//
+// To create a local BBinder object, production code can call
+// CreateLocalBinder(). Then, a test can get the BBinder's address via
+// local_binders() to check that they're passed as expected in binder calls.
+//
+class StubBinderWrapper : public BinderWrapper {
+ public:
+ StubBinderWrapper();
+ ~StubBinderWrapper() override;
+
+ const std::vector<sp<BBinder>>& local_binders() const {
+ return local_binders_;
+ }
+ void clear_local_binders() { local_binders_.clear(); }
+
+ void set_calling_uid(uid_t uid) { calling_uid_ = uid; }
+ void set_calling_pid(pid_t pid) { calling_pid_ = pid; }
+
+ // Sets the binder to return when |service_name| is passed to GetService() or
+ // WaitForService().
+ void SetBinderForService(const std::string& service_name,
+ const sp<IBinder>& binder);
+
+ // Returns the binder previously registered for |service_name| via
+ // RegisterService(), or null if the service hasn't been registered.
+ sp<IBinder> GetRegisteredService(const std::string& service_name) const;
+
+ // Run the calback in |death_callbacks_| corresponding to |binder|.
+ void NotifyAboutBinderDeath(const sp<IBinder>& binder);
+
+ // BinderWrapper:
+ sp<IBinder> GetService(const std::string& service_name) override;
+ bool RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) override;
+ sp<BBinder> CreateLocalBinder() override;
+ bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+ const ::base::Closure& callback) override;
+ bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+ uid_t GetCallingUid() override;
+ pid_t GetCallingPid() override;
+
+ private:
+ using ServiceMap = std::map<std::string, sp<IBinder>>;
+
+ // Map from service name to associated binder handle. Used by GetService() and
+ // WaitForService().
+ ServiceMap services_to_return_;
+
+ // Map from service name to associated binder handle. Updated by
+ // RegisterService().
+ ServiceMap registered_services_;
+
+ // Local binders returned by CreateLocalBinder().
+ std::vector<sp<BBinder>> local_binders_;
+
+ // Map from binder handle to the callback that should be invoked on binder
+ // death.
+ std::map<sp<IBinder>, ::base::Closure> death_callbacks_;
+
+ // Values to return from GetCallingUid() and GetCallingPid();
+ uid_t calling_uid_;
+ pid_t calling_pid_;
+
+ DISALLOW_COPY_AND_ASSIGN(StubBinderWrapper);
+};
+
+} // namespace android
+
+#endif // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/real_binder_wrapper.cc b/libbinderwrapper/real_binder_wrapper.cc
new file mode 100644
index 0000000..f93f183
--- /dev/null
+++ b/libbinderwrapper/real_binder_wrapper.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "real_binder_wrapper.h"
+
+#include <base/logging.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// Class that handles binder death notifications. libbinder wants the recipient
+// to be wrapped in sp<>, so registering RealBinderWrapper as a recipient would
+// be awkward.
+class RealBinderWrapper::DeathRecipient : public IBinder::DeathRecipient {
+ public:
+ explicit DeathRecipient(const ::base::Closure& callback)
+ : callback_(callback) {}
+ ~DeathRecipient() = default;
+
+ // IBinder::DeathRecipient:
+ void binderDied(const wp<IBinder>& who) override {
+ callback_.Run();
+ }
+
+ private:
+ // Callback to run in response to binder death.
+ ::base::Closure callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeathRecipient);
+};
+
+RealBinderWrapper::RealBinderWrapper() = default;
+
+RealBinderWrapper::~RealBinderWrapper() = default;
+
+sp<IBinder> RealBinderWrapper::GetService(const std::string& service_name) {
+ sp<IServiceManager> service_manager = defaultServiceManager();
+ if (!service_manager.get()) {
+ LOG(ERROR) << "Unable to get service manager";
+ return sp<IBinder>();
+ }
+ sp<IBinder> binder =
+ service_manager->checkService(String16(service_name.c_str()));
+ if (!binder.get())
+ LOG(ERROR) << "Unable to get \"" << service_name << "\" service";
+ return binder;
+}
+
+bool RealBinderWrapper::RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) {
+ sp<IServiceManager> service_manager = defaultServiceManager();
+ if (!service_manager.get()) {
+ LOG(ERROR) << "Unable to get service manager";
+ return false;
+ }
+ status_t status = defaultServiceManager()->addService(
+ String16(service_name.c_str()), binder);
+ if (status != OK) {
+ LOG(ERROR) << "Failed to register \"" << service_name << "\" with service "
+ << "manager";
+ return false;
+ }
+ return true;
+}
+
+sp<BBinder> RealBinderWrapper::CreateLocalBinder() {
+ return sp<BBinder>(new BBinder());
+}
+
+bool RealBinderWrapper::RegisterForDeathNotifications(
+ const sp<IBinder>& binder,
+ const ::base::Closure& callback) {
+ sp<DeathRecipient> recipient(new DeathRecipient(callback));
+ if (binder->linkToDeath(recipient) != OK) {
+ LOG(ERROR) << "Failed to register for death notifications on "
+ << binder.get();
+ return false;
+ }
+ death_recipients_[binder] = recipient;
+ return true;
+}
+
+bool RealBinderWrapper::UnregisterForDeathNotifications(
+ const sp<IBinder>& binder) {
+ auto it = death_recipients_.find(binder);
+ if (it == death_recipients_.end()) {
+ LOG(ERROR) << "Not registered for death notifications on " << binder.get();
+ return false;
+ }
+ if (binder->unlinkToDeath(it->second) != OK) {
+ LOG(ERROR) << "Failed to unregister for death notifications on "
+ << binder.get();
+ return false;
+ }
+ death_recipients_.erase(it);
+ return true;
+}
+
+uid_t RealBinderWrapper::GetCallingUid() {
+ return IPCThreadState::self()->getCallingUid();
+}
+
+pid_t RealBinderWrapper::GetCallingPid() {
+ return IPCThreadState::self()->getCallingPid();
+}
+
+} // namespace android
diff --git a/libbinderwrapper/real_binder_wrapper.h b/libbinderwrapper/real_binder_wrapper.h
new file mode 100644
index 0000000..fa05383
--- /dev/null
+++ b/libbinderwrapper/real_binder_wrapper.h
@@ -0,0 +1,58 @@
+/*
+ * 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 SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
+
+#include <map>
+
+#include <base/macros.h>
+#include <binderwrapper/binder_wrapper.h>
+
+namespace android {
+
+class IBinder;
+
+// Real implementation of BinderWrapper.
+class RealBinderWrapper : public BinderWrapper {
+ public:
+ RealBinderWrapper();
+ ~RealBinderWrapper() override;
+
+ // BinderWrapper:
+ sp<IBinder> GetService(const std::string& service_name) override;
+ bool RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) override;
+ sp<BBinder> CreateLocalBinder() override;
+ bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+ const ::base::Closure& callback) override;
+ bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+ uid_t GetCallingUid() override;
+ pid_t GetCallingPid() override;
+
+ private:
+ class DeathRecipient;
+
+ // Map from binder handle to object that should be notified of the binder's
+ // death.
+ std::map<sp<IBinder>, sp<DeathRecipient>> death_recipients_;
+
+ DISALLOW_COPY_AND_ASSIGN(RealBinderWrapper);
+};
+
+} // namespace android
+
+#endif // SYSTEM_CORE_LIBBINDER_WRAPPER_REAL_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/stub_binder_wrapper.cc b/libbinderwrapper/stub_binder_wrapper.cc
new file mode 100644
index 0000000..8e75f62
--- /dev/null
+++ b/libbinderwrapper/stub_binder_wrapper.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binderwrapper/stub_binder_wrapper.h>
+
+#include <base/logging.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+
+namespace android {
+
+StubBinderWrapper::StubBinderWrapper()
+ : calling_uid_(-1),
+ calling_pid_(-1) {}
+
+StubBinderWrapper::~StubBinderWrapper() = default;
+
+void StubBinderWrapper::SetBinderForService(const std::string& service_name,
+ const sp<IBinder>& binder) {
+ services_to_return_[service_name] = binder;
+}
+
+sp<IBinder> StubBinderWrapper::GetRegisteredService(
+ const std::string& service_name) const {
+ const auto it = registered_services_.find(service_name);
+ return it != registered_services_.end() ? it->second : sp<IBinder>();
+}
+
+void StubBinderWrapper::NotifyAboutBinderDeath(const sp<IBinder>& binder) {
+ const auto it = death_callbacks_.find(binder);
+ if (it != death_callbacks_.end())
+ it->second.Run();
+}
+
+sp<IBinder> StubBinderWrapper::GetService(const std::string& service_name) {
+ const auto it = services_to_return_.find(service_name);
+ return it != services_to_return_.end() ? it->second : sp<IBinder>();
+}
+
+bool StubBinderWrapper::RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) {
+ registered_services_[service_name] = binder;
+ return true;
+}
+
+sp<BBinder> StubBinderWrapper::CreateLocalBinder() {
+ sp<BBinder> binder(new BBinder());
+ local_binders_.push_back(binder);
+ return binder;
+}
+
+bool StubBinderWrapper::RegisterForDeathNotifications(
+ const sp<IBinder>& binder,
+ const ::base::Closure& callback) {
+ death_callbacks_[binder] = callback;
+ return true;
+}
+
+bool StubBinderWrapper::UnregisterForDeathNotifications(
+ const sp<IBinder>& binder) {
+ death_callbacks_.erase(binder);
+ return true;
+}
+
+uid_t StubBinderWrapper::GetCallingUid() {
+ return calling_uid_;
+}
+
+pid_t StubBinderWrapper::GetCallingPid() {
+ return calling_pid_;
+}
+
+} // namespace android
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
new file mode 100644
index 0000000..f2560e6
--- /dev/null
+++ b/libcrypto_utils/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library {
+ name: "libcrypto_utils",
+ host_supported: true,
+ srcs: [
+ "android_pubkey.c",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ shared_libs: ["libcrypto"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libcrypto_utils/android_pubkey.c b/libcrypto_utils/android_pubkey.c
new file mode 100644
index 0000000..3052e52
--- /dev/null
+++ b/libcrypto_utils/android_pubkey.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <crypto_utils/android_pubkey.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+
+// Better safe than sorry.
+#if (ANDROID_PUBKEY_MODULUS_SIZE % 4) != 0
+#error RSA modulus size must be multiple of the word size!
+#endif
+
+// Size of the RSA modulus in words.
+#define ANDROID_PUBKEY_MODULUS_SIZE_WORDS (ANDROID_PUBKEY_MODULUS_SIZE / 4)
+
+// This file implements encoding and decoding logic for Android's custom RSA
+// public key binary format. Public keys are stored as a sequence of
+// little-endian 32 bit words. Note that Android only supports little-endian
+// processors, so we don't do any byte order conversions when parsing the binary
+// struct.
+typedef struct RSAPublicKey {
+ // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
+ uint32_t modulus_size_words;
+
+ // Precomputed montgomery parameter: -1 / n[0] mod 2^32
+ uint32_t n0inv;
+
+ // RSA modulus as a little-endian array.
+ uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
+
+ // Montgomery parameter R^2 as a little-endian array of little-endian words.
+ uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
+
+ // RSA modulus: 3 or 65537
+ uint32_t exponent;
+} RSAPublicKey;
+
+// Reverses byte order in |buffer|.
+static void reverse_bytes(uint8_t* buffer, size_t size) {
+ for (size_t i = 0; i < (size + 1) / 2; ++i) {
+ uint8_t tmp = buffer[i];
+ buffer[i] = buffer[size - i - 1];
+ buffer[size - i - 1] = tmp;
+ }
+}
+
+bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {
+ const RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
+ bool ret = false;
+ uint8_t modulus_buffer[ANDROID_PUBKEY_MODULUS_SIZE];
+ RSA* new_key = RSA_new();
+ if (!new_key) {
+ goto cleanup;
+ }
+
+ // Check |size| is large enough and the modulus size is correct.
+ if (size < sizeof(RSAPublicKey)) {
+ goto cleanup;
+ }
+ if (key_struct->modulus_size_words != ANDROID_PUBKEY_MODULUS_SIZE_WORDS) {
+ goto cleanup;
+ }
+
+ // Convert the modulus to big-endian byte order as expected by BN_bin2bn.
+ memcpy(modulus_buffer, key_struct->modulus, sizeof(modulus_buffer));
+ reverse_bytes(modulus_buffer, sizeof(modulus_buffer));
+ new_key->n = BN_bin2bn(modulus_buffer, sizeof(modulus_buffer), NULL);
+ if (!new_key->n) {
+ goto cleanup;
+ }
+
+ // Read the exponent.
+ new_key->e = BN_new();
+ if (!new_key->e || !BN_set_word(new_key->e, key_struct->exponent)) {
+ goto cleanup;
+ }
+
+ // Note that we don't extract the montgomery parameters n0inv and rr from
+ // the RSAPublicKey structure. They assume a word size of 32 bits, but
+ // BoringSSL may use a word size of 64 bits internally, so we're lacking the
+ // top 32 bits of n0inv in general. For now, we just ignore the parameters
+ // and have BoringSSL recompute them internally. More sophisticated logic can
+ // be added here if/when we want the additional speedup from using the
+ // pre-computed montgomery parameters.
+
+ *key = new_key;
+ ret = true;
+
+cleanup:
+ if (!ret && new_key) {
+ RSA_free(new_key);
+ }
+ return ret;
+}
+
+static bool android_pubkey_encode_bignum(const BIGNUM* num, uint8_t* buffer) {
+ if (!BN_bn2bin_padded(buffer, ANDROID_PUBKEY_MODULUS_SIZE, num)) {
+ return false;
+ }
+
+ reverse_bytes(buffer, ANDROID_PUBKEY_MODULUS_SIZE);
+ return true;
+}
+
+bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {
+ RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
+ bool ret = false;
+ BN_CTX* ctx = BN_CTX_new();
+ BIGNUM* r32 = BN_new();
+ BIGNUM* n0inv = BN_new();
+ BIGNUM* rr = BN_new();
+
+ if (sizeof(RSAPublicKey) > size ||
+ RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
+ goto cleanup;
+ }
+
+ // Store the modulus size.
+ key_struct->modulus_size_words = ANDROID_PUBKEY_MODULUS_SIZE_WORDS;
+
+ // Compute and store n0inv = -1 / N[0] mod 2^32.
+ if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) ||
+ !BN_mod(n0inv, key->n, r32, ctx) ||
+ !BN_mod_inverse(n0inv, n0inv, r32, ctx) || !BN_sub(n0inv, r32, n0inv)) {
+ goto cleanup;
+ }
+ key_struct->n0inv = (uint32_t)BN_get_word(n0inv);
+
+ // Store the modulus.
+ if (!android_pubkey_encode_bignum(key->n, key_struct->modulus)) {
+ goto cleanup;
+ }
+
+ // Compute and store rr = (2^(rsa_size)) ^ 2 mod N.
+ if (!ctx || !rr || !BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8) ||
+ !BN_mod_sqr(rr, rr, key->n, ctx) ||
+ !android_pubkey_encode_bignum(rr, key_struct->rr)) {
+ goto cleanup;
+ }
+
+ // Store the exponent.
+ key_struct->exponent = (uint32_t)BN_get_word(key->e);
+
+ ret = true;
+
+cleanup:
+ BN_free(rr);
+ BN_free(n0inv);
+ BN_free(r32);
+ BN_CTX_free(ctx);
+ return ret;
+}
diff --git a/libcrypto_utils/include/crypto_utils/android_pubkey.h b/libcrypto_utils/include/crypto_utils/android_pubkey.h
new file mode 100644
index 0000000..1045eba
--- /dev/null
+++ b/libcrypto_utils/include/crypto_utils/android_pubkey.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRYPTO_UTILS_ANDROID_PUBKEY_H
+#define CRYPTO_UTILS_ANDROID_PUBKEY_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <openssl/rsa.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Size of an RSA modulus such as an encrypted block or a signature.
+#define ANDROID_PUBKEY_MODULUS_SIZE (2048 / 8)
+
+// Size of an encoded RSA key.
+#define ANDROID_PUBKEY_ENCODED_SIZE \
+ (3 * sizeof(uint32_t) + 2 * ANDROID_PUBKEY_MODULUS_SIZE)
+
+/* Allocates a new RSA |key| object, decodes a public RSA key stored in
+ * Android's custom binary format from |key_buffer| and sets the key parameters
+ * in |key|. |size| specifies the size of the key buffer and must be at least
+ * |ANDROID_PUBKEY_ENCODED_SIZE|. The resulting |*key| can be used with the
+ * standard BoringSSL API to perform public operations.
+ *
+ * Returns true if successful, in which case the caller receives ownership of
+ * the |*key| object, i.e. needs to call RSA_free() when done with it. If there
+ * is an error, |key| is left untouched and the return value will be false.
+ */
+bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key);
+
+/* Encodes |key| in the Android RSA public key binary format and stores the
+ * bytes in |key_buffer|. |key_buffer| should be of size at least
+ * |ANDROID_PUBKEY_ENCODED_SIZE|.
+ *
+ * Returns true if successful, false on error.
+ */
+bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // CRYPTO_UTILS_ANDROID_PUBKEY_H
diff --git a/libcrypto_utils/tests/Android.mk b/libcrypto_utils/tests/Android.mk
new file mode 100644
index 0000000..ef3d0cf
--- /dev/null
+++ b/libcrypto_utils/tests/Android.mk
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcrypto_utils_test
+LOCAL_SRC_FILES := android_pubkey_test.cpp
+LOCAL_CFLAGS := -Wall -Werror -Wextra
+LOCAL_SHARED_LIBRARIES := libcrypto_utils libcrypto
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcrypto_utils/tests/android_pubkey_test.cpp b/libcrypto_utils/tests/android_pubkey_test.cpp
new file mode 100644
index 0000000..f8c2e0c
--- /dev/null
+++ b/libcrypto_utils/tests/android_pubkey_test.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <crypto_utils/android_pubkey.h>
+
+#include <string.h>
+
+#include <memory>
+
+#include <openssl/obj_mac.h>
+#include <openssl/rsa.h>
+
+#include <gtest/gtest.h>
+
+// Test digest to verify.
+const uint8_t kDigest[] = {
+ 0x31, 0x5f, 0x5b, 0xdb, 0x76, 0xd0, 0x78, 0xc4, 0x3b, 0x8a, 0xc0,
+ 0x06, 0x4e, 0x4a, 0x01, 0x64, 0x61, 0x2b, 0x1f, 0xce, 0x77, 0xc8,
+ 0x69, 0x34, 0x5b, 0xfc, 0x94, 0xc7, 0x58, 0x94, 0xed, 0xd3,
+};
+
+// 2048 RSA test key.
+const uint8_t kKey2048[ANDROID_PUBKEY_ENCODED_SIZE] = {
+ 0x40, 0x00, 0x00, 0x00, 0x05, 0x75, 0x61, 0xd1, 0x33, 0xf0, 0x2d, 0x12,
+ 0x45, 0xfb, 0xae, 0x07, 0x02, 0x15, 0x4f, 0x3a, 0x2b, 0xa3, 0xbc, 0x49,
+ 0xbd, 0x14, 0x07, 0xa0, 0xc0, 0x9f, 0x0c, 0x52, 0x60, 0x77, 0x9f, 0xa2,
+ 0x31, 0xd0, 0xa7, 0xfb, 0x7e, 0xde, 0xfb, 0xc9, 0x05, 0xc0, 0x97, 0xf7,
+ 0x74, 0x99, 0xe6, 0xd1, 0x08, 0xa6, 0xc2, 0x59, 0x5a, 0xd8, 0x37, 0x1d,
+ 0xe0, 0x48, 0x5e, 0x63, 0x44, 0x04, 0x8b, 0x05, 0x20, 0xf6, 0x25, 0x67,
+ 0x38, 0xb2, 0xb6, 0xf9, 0xbe, 0xb6, 0x1d, 0x7f, 0x1b, 0x71, 0x8a, 0xeb,
+ 0xb7, 0xf8, 0x01, 0xc1, 0x5e, 0xf7, 0xfe, 0x48, 0x08, 0x27, 0x0f, 0x27,
+ 0x2a, 0x64, 0x1a, 0x43, 0x8d, 0xcf, 0x5a, 0x33, 0x5c, 0x18, 0xc5, 0xf4,
+ 0xe7, 0xfe, 0xee, 0xd3, 0x12, 0x62, 0xad, 0x61, 0x78, 0x9a, 0x03, 0xb0,
+ 0xaf, 0xab, 0x91, 0x57, 0x46, 0xbf, 0x18, 0xc6, 0xbc, 0x0c, 0x6b, 0x55,
+ 0xcd, 0xda, 0xc4, 0xcc, 0x98, 0x46, 0x91, 0x99, 0xbc, 0xa3, 0xca, 0x6c,
+ 0x86, 0xa6, 0x1c, 0x8f, 0xca, 0xf8, 0xf6, 0x8a, 0x00, 0x8e, 0x05, 0xd7,
+ 0x13, 0x43, 0xe2, 0xf2, 0x1a, 0x13, 0xf3, 0x50, 0x13, 0xa4, 0xf2, 0x4e,
+ 0x41, 0xb1, 0x36, 0x78, 0x55, 0x4c, 0x5e, 0x27, 0xc5, 0xc0, 0x4b, 0xd8,
+ 0x93, 0xaa, 0x7e, 0xf0, 0x90, 0x08, 0x10, 0x26, 0x72, 0x6d, 0xb9, 0x21,
+ 0xae, 0x4d, 0x01, 0x4b, 0x55, 0x1d, 0xe7, 0x1e, 0x5e, 0x31, 0x6e, 0x62,
+ 0xd1, 0x33, 0x26, 0xcb, 0xdb, 0xfe, 0x72, 0x98, 0xc8, 0x06, 0x1c, 0x12,
+ 0xdf, 0xfc, 0x74, 0xe5, 0x7a, 0x6f, 0xf5, 0xa3, 0x63, 0x08, 0xe3, 0x02,
+ 0x68, 0x4d, 0x7c, 0x70, 0x05, 0xec, 0x95, 0x7e, 0x24, 0xa4, 0xbc, 0x4c,
+ 0xcd, 0x39, 0x14, 0xb5, 0x2a, 0x8f, 0xc1, 0xe3, 0x4e, 0xfa, 0xf8, 0x70,
+ 0x50, 0x8f, 0xd5, 0x8e, 0xc7, 0xb5, 0x32, 0x89, 0x4d, 0xbb, 0x6a, 0xc1,
+ 0xc1, 0xa2, 0x42, 0x57, 0x57, 0xbd, 0x2a, 0xdc, 0xa6, 0xfd, 0xc8, 0x86,
+ 0x44, 0x6a, 0x03, 0x5d, 0x4d, 0x28, 0xe1, 0xde, 0xb4, 0xa9, 0xa5, 0x03,
+ 0x61, 0x7a, 0x5f, 0xb1, 0x09, 0x17, 0x2b, 0x9c, 0xa2, 0x54, 0x28, 0xad,
+ 0x34, 0xc9, 0x5f, 0x6c, 0x9f, 0xb8, 0xd2, 0xa9, 0x78, 0xa7, 0xaa, 0xb3,
+ 0x11, 0x2f, 0x65, 0x9b, 0x4e, 0x67, 0x0c, 0xcc, 0x20, 0x36, 0xbf, 0x26,
+ 0x2b, 0x4e, 0xc0, 0xd4, 0xbd, 0x22, 0x64, 0xc4, 0x1c, 0x56, 0x69, 0xdb,
+ 0x5f, 0x89, 0xe1, 0x75, 0x68, 0x8d, 0x0e, 0xab, 0x1c, 0x10, 0x1a, 0xc0,
+ 0x12, 0x5d, 0x6f, 0xbd, 0x09, 0xbb, 0x47, 0xcb, 0xe7, 0x34, 0xef, 0x56,
+ 0xab, 0xea, 0xc3, 0xe9, 0x7f, 0x9a, 0x3d, 0xe9, 0x2d, 0x14, 0x61, 0x25,
+ 0x37, 0x5c, 0x3b, 0x4b, 0xaf, 0x5a, 0x4b, 0xc8, 0x99, 0x1a, 0x32, 0x8f,
+ 0x54, 0x07, 0xd3, 0x57, 0x8a, 0x3d, 0x2a, 0xf7, 0x9e, 0x7e, 0x92, 0x2a,
+ 0x50, 0xe9, 0xd8, 0xdb, 0xd6, 0x03, 0xd3, 0x8e, 0x54, 0x32, 0xce, 0x87,
+ 0x93, 0x92, 0xe7, 0x75, 0xe1, 0x6b, 0x78, 0x1a, 0x85, 0xc2, 0x46, 0xa1,
+ 0x31, 0xbb, 0xc7, 0xb9, 0x1d, 0xd1, 0x71, 0xe0, 0xe2, 0x9b, 0x9c, 0x0d,
+ 0xa3, 0xcf, 0x93, 0x4d, 0x87, 0x7b, 0x65, 0xd9, 0xda, 0x4c, 0xd9, 0x6a,
+ 0xa6, 0x36, 0xc2, 0xc7, 0xe3, 0x33, 0xe2, 0xc3, 0x83, 0xd1, 0x72, 0x54,
+ 0x30, 0x81, 0x5e, 0x34, 0x2c, 0x61, 0xee, 0xf4, 0x48, 0x97, 0xb6, 0xaa,
+ 0x47, 0x6a, 0x05, 0x09, 0xd8, 0x4d, 0x90, 0xaf, 0xa8, 0x4e, 0x82, 0xe4,
+ 0x8e, 0xb5, 0xe2, 0x65, 0x86, 0x67, 0xe9, 0x5b, 0x4b, 0x9a, 0x68, 0x08,
+ 0x30, 0xf6, 0x25, 0x8b, 0x20, 0xda, 0x26, 0x6f, 0xbd, 0x0d, 0xa5, 0xd8,
+ 0x6a, 0x7b, 0x01, 0x2f, 0xab, 0x7b, 0xb5, 0xfe, 0x62, 0x37, 0x2d, 0x94,
+ 0x43, 0x2f, 0x4d, 0x16, 0x01, 0x00, 0x01, 0x00,
+};
+
+// 2048 bit RSA signature.
+const uint8_t kSignature2048[ANDROID_PUBKEY_MODULUS_SIZE] = {
+ 0x3a, 0x11, 0x84, 0x40, 0xc1, 0x2f, 0x13, 0x8c, 0xde, 0xb0, 0xc3, 0x89,
+ 0x8a, 0x63, 0xb2, 0x50, 0x93, 0x58, 0xc0, 0x0c, 0xb7, 0x08, 0xe7, 0x6c,
+ 0x52, 0x87, 0x4e, 0x78, 0x89, 0xa3, 0x9a, 0x47, 0xeb, 0x11, 0x57, 0xbc,
+ 0xb3, 0x97, 0xf8, 0x34, 0xf1, 0xf7, 0xbf, 0x3a, 0xfa, 0x1c, 0x6b, 0xdc,
+ 0xd1, 0x02, 0xde, 0x9a, 0x0d, 0x72, 0xe7, 0x19, 0x63, 0x81, 0x46, 0x68,
+ 0x1e, 0x63, 0x64, 0xc6, 0x59, 0xe7, 0x7c, 0x39, 0xed, 0x32, 0xd2, 0xd1,
+ 0xd5, 0x1f, 0x13, 0x9b, 0x52, 0xdf, 0x34, 0xa3, 0xc0, 0xc4, 0x9a, 0x63,
+ 0x9b, 0x9c, 0xbe, 0x22, 0xc8, 0xd8, 0x14, 0x2f, 0x4c, 0x78, 0x36, 0xdb,
+ 0x16, 0x41, 0x67, 0xc1, 0x21, 0x8a, 0x73, 0xb2, 0xe5, 0xb0, 0xd3, 0x80,
+ 0x91, 0x7a, 0xbf, 0xf9, 0x59, 0x4a, 0x4d, 0x78, 0x45, 0x44, 0xa1, 0x52,
+ 0x86, 0x29, 0x48, 0x4d, 0xf0, 0x5d, 0xf2, 0x55, 0xa7, 0xcd, 0xc5, 0x2b,
+ 0x7b, 0xe0, 0xb1, 0xf6, 0x2a, 0xd5, 0x61, 0xba, 0x1e, 0x1e, 0x3a, 0xf0,
+ 0x55, 0xbc, 0x8c, 0x44, 0x41, 0xfc, 0xb8, 0x8c, 0x76, 0xbf, 0x80, 0x58,
+ 0x82, 0x35, 0x4b, 0x0c, 0xfd, 0xef, 0xd5, 0x70, 0xd1, 0x64, 0xcb, 0x46,
+ 0x58, 0x37, 0xbc, 0xa9, 0x7d, 0xd4, 0x70, 0xac, 0xce, 0xec, 0xca, 0x48,
+ 0xcb, 0x0a, 0x40, 0x77, 0x04, 0x59, 0xca, 0x9c, 0x7d, 0x1a, 0x0b, 0xf0,
+ 0xb5, 0xdd, 0xde, 0x71, 0x18, 0xb8, 0xef, 0x90, 0x2a, 0x09, 0x42, 0x39,
+ 0x74, 0xff, 0x45, 0xa1, 0x39, 0x17, 0x50, 0x89, 0xa6, 0x5f, 0xbc, 0x9c,
+ 0x0c, 0x9b, 0x47, 0x25, 0x79, 0x3e, 0xe3, 0xaa, 0xaf, 0xbe, 0x73, 0x6b,
+ 0xcb, 0xe7, 0x35, 0xc1, 0x27, 0x09, 0xcd, 0xeb, 0xd7, 0xcf, 0x63, 0x83,
+ 0x64, 0x8c, 0x45, 0x1c, 0x1d, 0x58, 0xcc, 0xd2, 0xf8, 0x2b, 0x4c, 0x4e,
+ 0x14, 0x89, 0x2d, 0x70,
+};
+
+struct AndroidPubkeyTest : public ::testing::Test {
+ void SetUp() override {
+ RSA* new_key = nullptr;
+ ASSERT_TRUE(android_pubkey_decode(kKey2048, sizeof(kKey2048), &new_key));
+ key_.reset(new_key);
+ }
+
+ std::unique_ptr<RSA, void(*)(RSA*)> key_ = {nullptr, RSA_free};
+};
+
+TEST_F(AndroidPubkeyTest, Decode) {
+ // Make sure the decoded key successfully verifies a valid signature.
+ EXPECT_TRUE(RSA_verify(NID_sha256, kDigest, sizeof(kDigest), kSignature2048,
+ sizeof(kSignature2048), key_.get()));
+}
+
+TEST_F(AndroidPubkeyTest, Encode) {
+ uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
+ ASSERT_TRUE(android_pubkey_encode(key_.get(), key_data, sizeof(key_data)));
+ ASSERT_EQ(0, memcmp(kKey2048, key_data, sizeof(kKey2048)));
+}
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
new file mode 100644
index 0000000..8ba7452
--- /dev/null
+++ b/libcutils/Android.bp
@@ -0,0 +1,161 @@
+//
+// 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// some files must not be compiled when building against Mingw
+// they correspond to features not used by our host development tools
+// which are also hard or even impossible to port to native Win32
+libcutils_nonwindows_sources = [
+ "android_get_control_file.cpp",
+ "fs.c",
+ "multiuser.c",
+ "socket_inaddr_any_server_unix.c",
+ "socket_local_client_unix.c",
+ "socket_local_server_unix.c",
+ "socket_loopback_server_unix.c",
+ "socket_network_client_unix.c",
+ "sockets_unix.cpp",
+ "str_parms.c",
+]
+
+cc_library_headers {
+ name: "libcutils_vndk_headers",
+ host_supported: true,
+ export_include_dirs: ["include_vndk"],
+}
+
+cc_library_headers {
+ name: "libcutils_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+}
+
+cc_library {
+ name: "libcutils",
+ host_supported: true,
+ srcs: [
+ "config_utils.c",
+ "fs_config.c",
+ "canned_fs_config.c",
+ "hashmap.c",
+ "iosched_policy.c",
+ "load_file.c",
+ "native_handle.c",
+ "open_memstream.c",
+ "record_stream.c",
+ "sched_policy.c",
+ "sockets.cpp",
+ "strdup16to8.c",
+ "strdup8to16.c",
+ "strlcpy.c",
+ "threads.c",
+ ],
+
+
+ target: {
+ host: {
+ srcs: ["dlmalloc_stubs.c"],
+ },
+ not_windows: {
+ srcs: libcutils_nonwindows_sources + [
+ "ashmem-host.c",
+ "trace-host.c",
+ ],
+ },
+ windows: {
+ srcs: [
+ "socket_inaddr_any_server_windows.c",
+ "socket_network_client_windows.c",
+ "sockets_windows.cpp",
+ ],
+
+ enabled: true,
+ shared: {
+ enabled: false,
+ },
+ },
+
+ android: {
+ srcs: libcutils_nonwindows_sources + [
+ "android_reboot.c",
+ "ashmem-dev.c",
+ "klog.cpp",
+ "partition_utils.c",
+ "properties.cpp",
+ "qtaguid.c",
+ "trace-dev.c",
+ "uevent.c",
+ ],
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ },
+
+ android_arm: {
+ srcs: ["arch-arm/memset32.S"],
+ },
+ android_arm64: {
+ srcs: ["arch-arm64/android_memset.S"],
+ },
+
+ android_mips: {
+ srcs: ["arch-mips/android_memset.c"],
+ },
+ android_mips64: {
+ srcs: ["arch-mips/android_memset.c"],
+ },
+
+ android_x86: {
+ srcs: [
+ "arch-x86/android_memset16.S",
+ "arch-x86/android_memset32.S",
+ ],
+ },
+
+ android_x86_64: {
+ srcs: [
+ "arch-x86_64/android_memset16.S",
+ "arch-x86_64/android_memset32.S",
+ ],
+ },
+ },
+
+ shared_libs: ["liblog"],
+ header_libs: ["libcutils_headers"],
+ export_header_lib_headers: ["libcutils_headers"],
+
+ product_variables: {
+ cpusets: {
+ cflags: ["-DUSE_CPUSETS"],
+ },
+ schedboost: {
+ cflags: ["-DUSE_SCHEDBOOST"],
+ },
+ },
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wextra",
+ ],
+
+ clang: true,
+}
+
+subdirs = ["tests"]
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
deleted file mode 100644
index 5330949..0000000
--- a/libcutils/Android.mk
+++ /dev/null
@@ -1,138 +0,0 @@
-#
-# 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.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(my-dir)
-include $(CLEAR_VARS)
-
-commonSources := \
- hashmap.c \
- atomic.c.arm \
- native_handle.c \
- config_utils.c \
- load_file.c \
- strlcpy.c \
- open_memstream.c \
- strdup16to8.c \
- strdup8to16.c \
- record_stream.c \
- process_name.c \
- threads.c \
- sched_policy.c \
- iosched_policy.c \
- str_parms.c \
- fs_config.c
-
-# some files must not be compiled when building against Mingw
-# they correspond to features not used by our host development tools
-# which are also hard or even impossible to port to native Win32
-WINDOWS_HOST_ONLY :=
-ifeq ($(HOST_OS),windows)
- ifeq ($(strip $(USE_CYGWIN)),)
- WINDOWS_HOST_ONLY := 1
- endif
-endif
-# USE_MINGW is defined when we build against Mingw on Linux
-ifneq ($(strip $(USE_MINGW)),)
- WINDOWS_HOST_ONLY := 1
-endif
-
-ifneq ($(WINDOWS_HOST_ONLY),1)
- commonSources += \
- fs.c \
- multiuser.c \
- socket_inaddr_any_server.c \
- socket_local_client.c \
- socket_local_server.c \
- socket_loopback_client.c \
- socket_loopback_server.c \
- socket_network_client.c \
- sockets.c \
-
- commonHostSources += \
- ashmem-host.c \
- trace-host.c
-
-endif
-
-
-# Shared and static library for host
-# ========================================================
-LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
-LOCAL_STATIC_LIBRARIES := liblog
-ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror -Wall -Wextra
-endif
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
-LOCAL_SHARED_LIBRARIES := liblog
-ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror -Wall -Wextra
-endif
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-
-# Shared and static library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) \
- android_reboot.c \
- ashmem-dev.c \
- debugger.c \
- klog.c \
- partition_utils.c \
- properties.c \
- qtaguid.c \
- trace-dev.c \
- uevent.c \
-
-LOCAL_SRC_FILES_arm += arch-arm/memset32.S
-LOCAL_SRC_FILES_arm64 += arch-arm64/android_memset.S
-
-LOCAL_SRC_FILES_mips += arch-mips/android_memset.c
-LOCAL_SRC_FILES_mips64 += arch-mips/android_memset.c
-
-LOCAL_SRC_FILES_x86 += \
- arch-x86/android_memset16.S \
- arch-x86/android_memset32.S \
-
-LOCAL_SRC_FILES_x86_64 += \
- arch-x86_64/android_memset16.S \
- arch-x86_64/android_memset32.S \
-
-LOCAL_C_INCLUDES := $(libcutils_c_includes)
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils
-# TODO: remove liblog as whole static library, once we don't have prebuilt that requires
-# liblog symbols present in libcutils.
-LOCAL_WHOLE_STATIC_LIBRARIES := libcutils liblog
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_CFLAGS += -Werror -Wall -Wextra
-LOCAL_C_INCLUDES := $(libcutils_c_includes)
-include $(BUILD_SHARED_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libcutils/android_get_control_env.h b/libcutils/android_get_control_env.h
new file mode 100644
index 0000000..638c831
--- /dev/null
+++ b/libcutils/android_get_control_env.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_ANDROID_GET_CONTROL_ENV_H
+#define __CUTILS_ANDROID_GET_CONTROL_ENV_H
+
+/* To declare library function hidden and internal */
+#define LIBCUTILS_HIDDEN __attribute__((visibility("hidden")))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBCUTILS_HIDDEN int __android_get_control_from_env(const char* prefix,
+ const char* name);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_ANDROID_GET_CONTROL_ENV_H */
diff --git a/libcutils/android_get_control_file.cpp b/libcutils/android_get_control_file.cpp
new file mode 100644
index 0000000..780d9f1
--- /dev/null
+++ b/libcutils/android_get_control_file.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/android_get_control_file.h>
+
+#include "android_get_control_env.h"
+
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(exp) (exp) // KISS implementation
+#endif
+
+LIBCUTILS_HIDDEN int __android_get_control_from_env(const char* prefix,
+ const char* name) {
+ if (!prefix || !name) return -1;
+
+ char *key = NULL;
+ if (asprintf(&key, "%s%s", prefix, name) < 0) return -1;
+ if (!key) return -1;
+
+ char *cp = key;
+ while (*cp) {
+ if (!isalnum(*cp)) *cp = '_';
+ ++cp;
+ }
+
+ const char* val = getenv(key);
+ free(key);
+ if (!val) return -1;
+
+ errno = 0;
+ long fd = strtol(val, NULL, 10);
+ if (errno) return -1;
+
+ // validity checking
+ if ((fd < 0) || (fd > INT_MAX)) return -1;
+
+ // Since we are inheriting an fd, it could legitimately exceed _SC_OPEN_MAX
+
+ // Still open?
+#if defined(F_GETFD) // Lowest overhead
+ if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1;
+#elif defined(F_GETFL) // Alternate lowest overhead
+ if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)) < 0) return -1;
+#else // Hail Mary pass
+ struct stat s;
+ if (TEMP_FAILURE_RETRY(fstat(fd, &s)) < 0) return -1;
+#endif
+
+ return static_cast<int>(fd);
+}
+
+int android_get_control_file(const char* path) {
+ int fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
+
+#if defined(__linux__)
+ // Find file path from /proc and make sure it is correct
+ char *proc = NULL;
+ if (asprintf(&proc, "/proc/self/fd/%d", fd) < 0) return -1;
+ if (!proc) return -1;
+
+ size_t len = strlen(path);
+ // readlink() does not guarantee a nul byte, len+2 so we catch truncation.
+ char *buf = static_cast<char *>(calloc(1, len + 2));
+ if (!buf) {
+ free(proc);
+ return -1;
+ }
+ ssize_t ret = TEMP_FAILURE_RETRY(readlink(proc, buf, len + 1));
+ free(proc);
+ int cmp = (len != static_cast<size_t>(ret)) || strcmp(buf, path);
+ free(buf);
+ if (ret < 0) return -1;
+ if (cmp != 0) return -1;
+ // It is what we think it is
+#endif
+
+ return fd;
+}
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index af7e189..159a9d4 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -42,24 +42,6 @@
struct mntent entry;
} mntent_list;
-static bool has_mount_option(const char* opts, const char* opt_to_find)
-{
- bool ret = false;
- char* copy = NULL;
- char* opt;
- char* rem;
-
- while ((opt = strtok_r(copy ? NULL : (copy = strdup(opts)), ",", &rem))) {
- if (!strcmp(opt, opt_to_find)) {
- ret = true;
- break;
- }
- }
-
- free(copy);
- return ret;
-}
-
static bool is_block_device(const char* fsname)
{
return !strncmp(fsname, "/dev/block", 10);
@@ -78,8 +60,7 @@
return;
}
while ((mentry = getmntent(fp)) != NULL) {
- if (is_block_device(mentry->mnt_fsname) &&
- has_mount_option(mentry->mnt_opts, "rw")) {
+ if (is_block_device(mentry->mnt_fsname) && hasmntopt(mentry, "rw")) {
mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
item->entry = *mentry;
item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
@@ -170,8 +151,7 @@
goto out;
}
while ((mentry = getmntent(fp)) != NULL) {
- if (!is_block_device(mentry->mnt_fsname) ||
- !has_mount_option(mentry->mnt_opts, "ro")) {
+ if (!is_block_device(mentry->mnt_fsname) || !hasmntopt(mentry, "ro")) {
continue;
}
mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
diff --git a/libcutils/arch-mips/android_memset.c b/libcutils/arch-mips/android_memset.c
index a6b7496..c0fe3d1 100644
--- a/libcutils/arch-mips/android_memset.c
+++ b/libcutils/arch-mips/android_memset.c
@@ -30,6 +30,9 @@
#include <cutils/memory.h>
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
void android_memset16(uint16_t* dst, uint16_t value, size_t size)
{
/* optimized version of
@@ -46,7 +49,7 @@
}
/* dst is now 32-bit-aligned */
/* fill body with 32-bit pairs */
- uint32_t value32 = (value << 16) | value;
+ uint32_t value32 = (((uint32_t)value) << 16) | ((uint32_t)value);
android_memset32((uint32_t*) dst, value32, size<<1);
if (size & 1) {
dst[size-1] = value; /* fill unpaired last elem */
@@ -54,6 +57,9 @@
}
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
void android_memset32(uint32_t* dst, uint32_t value, size_t size)
{
/* optimized version of
@@ -70,7 +76,7 @@
}
/* dst is now 64-bit aligned */
/* fill body with 64-bit pairs */
- uint64_t value64 = (((uint64_t)value)<<32) | value;
+ uint64_t value64 = (((uint64_t)value) << 32) | ((uint64_t)value);
uint64_t* dst64 = (uint64_t*)dst;
while (size >= 12) {
@@ -86,7 +92,8 @@
/* fill remainder with original 32-bit single-elem loop */
dst = (uint32_t*) dst64;
- while (size--) {
+ while (size != 0) {
+ size--;
*dst++ = value;
}
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index 3089a94..92717c0 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -19,18 +19,125 @@
* ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
* used by the simulator.
*/
+#define LOG_TAG "ashmem"
-#include <unistd.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
+#include <errno.h>
#include <fcntl.h>
-
#include <linux/ashmem.h>
-#include <cutils/ashmem.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
-#define ASHMEM_DEVICE "/dev/ashmem"
+#include <cutils/ashmem.h>
+#include <log/log.h>
+
+#define ASHMEM_DEVICE "/dev/ashmem"
+
+/* ashmem identity */
+static dev_t __ashmem_rdev;
+/*
+ * If we trigger a signal handler in the middle of locked activity and the
+ * signal handler calls ashmem, we could get into a deadlock state.
+ */
+static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* logistics of getting file descriptor for ashmem */
+static int __ashmem_open_locked()
+{
+ int ret;
+ struct stat st;
+
+ int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR));
+ if (fd < 0) {
+ return fd;
+ }
+
+ ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
+ if (ret < 0) {
+ int save_errno = errno;
+ close(fd);
+ errno = save_errno;
+ return ret;
+ }
+ if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
+ close(fd);
+ errno = ENOTTY;
+ return -1;
+ }
+
+ __ashmem_rdev = st.st_rdev;
+ return fd;
+}
+
+static int __ashmem_open()
+{
+ int fd;
+
+ pthread_mutex_lock(&__ashmem_lock);
+ fd = __ashmem_open_locked();
+ pthread_mutex_unlock(&__ashmem_lock);
+
+ return fd;
+}
+
+/* Make sure file descriptor references ashmem, negative number means false */
+static int __ashmem_is_ashmem(int fd, int fatal)
+{
+ dev_t rdev;
+ struct stat st;
+
+ if (TEMP_FAILURE_RETRY(fstat(fd, &st)) < 0) {
+ return -1;
+ }
+
+ rdev = 0; /* Too much complexity to sniff __ashmem_rdev */
+ if (S_ISCHR(st.st_mode) && st.st_rdev) {
+ pthread_mutex_lock(&__ashmem_lock);
+ rdev = __ashmem_rdev;
+ if (rdev) {
+ pthread_mutex_unlock(&__ashmem_lock);
+ } else {
+ int fd = __ashmem_open_locked();
+ if (fd < 0) {
+ pthread_mutex_unlock(&__ashmem_lock);
+ return -1;
+ }
+ rdev = __ashmem_rdev;
+ pthread_mutex_unlock(&__ashmem_lock);
+
+ close(fd);
+ }
+
+ if (st.st_rdev == rdev) {
+ return 0;
+ }
+ }
+
+ if (fatal) {
+ if (rdev) {
+ LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o %d:%d",
+ fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP,
+ major(rdev), minor(rdev));
+ } else {
+ LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o",
+ fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP);
+ }
+ /* NOTREACHED */
+ }
+
+ errno = ENOTTY;
+ return -1;
+}
+
+int ashmem_valid(int fd)
+{
+ return __ashmem_is_ashmem(fd, 0) >= 0;
+}
/*
* ashmem_create_region - creates a new ashmem region and returns the file
@@ -41,50 +148,77 @@
*/
int ashmem_create_region(const char *name, size_t size)
{
- int fd, ret;
+ int ret, save_errno;
- fd = open(ASHMEM_DEVICE, O_RDWR);
- if (fd < 0)
- return fd;
+ int fd = __ashmem_open();
+ if (fd < 0) {
+ return fd;
+ }
- if (name) {
- char buf[ASHMEM_NAME_LEN] = {0};
+ if (name) {
+ char buf[ASHMEM_NAME_LEN] = {0};
- strlcpy(buf, name, sizeof(buf));
- ret = ioctl(fd, ASHMEM_SET_NAME, buf);
- if (ret < 0)
- goto error;
- }
+ strlcpy(buf, name, sizeof(buf));
+ ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
+ if (ret < 0) {
+ goto error;
+ }
+ }
- ret = ioctl(fd, ASHMEM_SET_SIZE, size);
- if (ret < 0)
- goto error;
+ ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
+ if (ret < 0) {
+ goto error;
+ }
- return fd;
+ return fd;
error:
- close(fd);
- return ret;
+ save_errno = errno;
+ close(fd);
+ errno = save_errno;
+ return ret;
}
int ashmem_set_prot_region(int fd, int prot)
{
- return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+ int ret = __ashmem_is_ashmem(fd, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
}
int ashmem_pin_region(int fd, size_t offset, size_t len)
{
- struct ashmem_pin pin = { offset, len };
- return ioctl(fd, ASHMEM_PIN, &pin);
+ struct ashmem_pin pin = { offset, len };
+
+ int ret = __ashmem_is_ashmem(fd, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
}
int ashmem_unpin_region(int fd, size_t offset, size_t len)
{
- struct ashmem_pin pin = { offset, len };
- return ioctl(fd, ASHMEM_UNPIN, &pin);
+ struct ashmem_pin pin = { offset, len };
+
+ int ret = __ashmem_is_ashmem(fd, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
}
int ashmem_get_size_region(int fd)
{
- return ioctl(fd, ASHMEM_GET_SIZE, NULL);
+ int ret = __ashmem_is_ashmem(fd, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
index abc4f94..1f9f753 100644
--- a/libcutils/ashmem-host.c
+++ b/libcutils/ashmem-host.c
@@ -43,11 +43,16 @@
char template[PATH_MAX];
snprintf(template, sizeof(template), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
int fd = mkstemp(template);
- if (fd != -1 && TEMP_FAILURE_RETRY(ftruncate(fd, size)) != -1 && unlink(template) != -1) {
- return fd;
+ if (fd == -1) return -1;
+
+ unlink(template);
+
+ if (TEMP_FAILURE_RETRY(ftruncate(fd, size)) == -1) {
+ close(fd);
+ return -1;
}
- close(fd);
- return -1;
+
+ return fd;
}
int ashmem_set_prot_region(int fd __unused, int prot __unused)
@@ -57,12 +62,12 @@
int ashmem_pin_region(int fd __unused, size_t offset __unused, size_t len __unused)
{
- return ASHMEM_NOT_PURGED;
+ return 0 /*ASHMEM_NOT_PURGED*/;
}
int ashmem_unpin_region(int fd __unused, size_t offset __unused, size_t len __unused)
{
- return ASHMEM_IS_UNPINNED;
+ return 0 /*ASHMEM_IS_UNPINNED*/;
}
int ashmem_get_size_region(int fd)
@@ -73,8 +78,11 @@
return -1;
}
- // Check if this is an "ashmem" region.
- // TODO: This is very hacky, and can easily break. We need some reliable indicator.
+ /*
+ * Check if this is an "ashmem" region.
+ * TODO: This is very hacky, and can easily break.
+ * We need some reliable indicator.
+ */
if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
errno = ENOTTY;
return -1;
diff --git a/libcutils/atomic.c b/libcutils/atomic.c
deleted file mode 100644
index d34aa00..0000000
--- a/libcutils/atomic.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Generate non-inlined versions of android_atomic functions.
- * Nobody should be using these, but some binary blobs currently (late 2014)
- * are.
- * If you read this in 2015 or later, please try to delete this file.
- */
-
-#define ANDROID_ATOMIC_INLINE
-
-#include <cutils/atomic.h>
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
new file mode 100644
index 0000000..e0e6a34
--- /dev/null
+++ b/libcutils/canned_fs_config.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/canned_fs_config.h>
+
+typedef struct {
+ const char* path;
+ unsigned uid;
+ unsigned gid;
+ unsigned mode;
+ uint64_t capabilities;
+} Path;
+
+static Path* canned_data = NULL;
+static int canned_alloc = 0;
+static int canned_used = 0;
+
+static int path_compare(const void* a, const void* b) {
+ return strcmp(((Path*)a)->path, ((Path*)b)->path);
+}
+
+int load_canned_fs_config(const char* fn) {
+ char line[PATH_MAX + 200];
+ FILE* f;
+
+ f = fopen(fn, "r");
+ if (f == NULL) {
+ fprintf(stderr, "failed to open %s: %s\n", fn, strerror(errno));
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line), f)) {
+ Path* p;
+ char* token;
+
+ while (canned_used >= canned_alloc) {
+ canned_alloc = (canned_alloc+1) * 2;
+ canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
+ }
+ p = canned_data + canned_used;
+ p->path = strdup(strtok(line, " "));
+ p->uid = atoi(strtok(NULL, " "));
+ p->gid = atoi(strtok(NULL, " "));
+ p->mode = strtol(strtok(NULL, " "), NULL, 8); // mode is in octal
+ p->capabilities = 0;
+
+ do {
+ token = strtok(NULL, " ");
+ if (token && strncmp(token, "capabilities=", 13) == 0) {
+ p->capabilities = strtoll(token+13, NULL, 0);
+ break;
+ }
+ } while (token);
+
+ canned_used++;
+ }
+
+ fclose(f);
+
+ qsort(canned_data, canned_used, sizeof(Path), path_compare);
+ printf("loaded %d fs_config entries\n", canned_used);
+
+ return 0;
+}
+
+static const int kDebugCannedFsConfig = 0;
+
+void canned_fs_config(const char* path, int dir, const char* target_out_path,
+ unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
+ Path key, *p;
+
+ key.path = path;
+ if (path[0] == '/') key.path++; // canned paths lack the leading '/'
+ p = (Path*) bsearch(&key, canned_data, canned_used, sizeof(Path), path_compare);
+ if (p == NULL) {
+ fprintf(stderr, "failed to find [%s] in canned fs_config\n", path);
+ exit(1);
+ }
+ *uid = p->uid;
+ *gid = p->gid;
+ *mode = p->mode;
+ *capabilities = p->capabilities;
+
+ if (kDebugCannedFsConfig) {
+ // for debugging, run the built-in fs_config and compare the results.
+
+ unsigned c_uid, c_gid, c_mode;
+ uint64_t c_capabilities;
+
+ fs_config(path, dir, target_out_path, &c_uid, &c_gid, &c_mode, &c_capabilities);
+
+ if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
+ if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
+ if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
+ if (c_capabilities != *capabilities) {
+ printf("%s capabilities %" PRIx64 " %" PRIx64 "\n",
+ path,
+ *capabilities,
+ c_capabilities);
+ }
+ }
+}
diff --git a/libcutils/debugger.c b/libcutils/debugger.c
deleted file mode 100644
index 3407ec3..0000000
--- a/libcutils/debugger.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 <stdbool.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include <cutils/debugger.h>
-#include <cutils/sockets.h>
-
-#define LOG_TAG "DEBUG"
-#include <log/log.h>
-
-static int send_request(int sock_fd, void* msg_ptr, size_t msg_len) {
- int result = 0;
- if (TEMP_FAILURE_RETRY(write(sock_fd, msg_ptr, msg_len)) != (ssize_t) msg_len) {
- result = -1;
- } else {
- char ack;
- if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) != 1) {
- result = -1;
- }
- }
- return result;
-}
-
-static int make_dump_request(debugger_action_t action, pid_t tid, int timeout_secs) {
- debugger_msg_t msg;
- memset(&msg, 0, sizeof(msg));
- msg.tid = tid;
- msg.action = action;
-
- int sock_fd = socket_local_client(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
- SOCK_STREAM | SOCK_CLOEXEC);
- if (sock_fd < 0) {
- return -1;
- }
-
- if (timeout_secs > 0) {
- struct timeval tm;
- tm.tv_sec = timeout_secs;
- tm.tv_usec = 0;
- if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm)) == -1) {
- ALOGE("WARNING: Cannot set receive timeout value on socket: %s", strerror(errno));
- }
-
- if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, &tm, sizeof(tm)) == -1) {
- ALOGE("WARNING: Cannot set send timeout value on socket: %s", strerror(errno));
- }
- }
-
- if (send_request(sock_fd, &msg, sizeof(msg)) < 0) {
- close(sock_fd);
- return -1;
- }
-
- return sock_fd;
-}
-
-int dump_backtrace_to_file(pid_t tid, int fd) {
- return dump_backtrace_to_file_timeout(tid, fd, 0);
-}
-
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
- int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, timeout_secs);
- if (sock_fd < 0) {
- return -1;
- }
-
- /* Write the data read from the socket to the fd. */
- int result = 0;
- char buffer[1024];
- ssize_t n;
- while ((n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
- if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) {
- result = -1;
- break;
- }
- }
- close(sock_fd);
- return result;
-}
-
-int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) {
- return dump_tombstone_timeout(tid, pathbuf, pathlen, 0);
-}
-
-int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs) {
- int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_TOMBSTONE, tid, timeout_secs);
- if (sock_fd < 0) {
- return -1;
- }
-
- /* Read the tombstone file name. */
- char buffer[100]; /* This is larger than the largest tombstone path. */
- int result = 0;
- ssize_t n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer) - 1));
- if (n <= 0) {
- result = -1;
- } else {
- if (pathbuf && pathlen) {
- if (n >= (ssize_t) pathlen) {
- n = pathlen - 1;
- }
- buffer[n] = '\0';
- memcpy(pathbuf, buffer, n + 1);
- }
- }
- close(sock_fd);
- return result;
-}
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
index 2db473d..2cff9dd 100644
--- a/libcutils/dlmalloc_stubs.c
+++ b/libcutils/dlmalloc_stubs.c
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "dlmalloc-stubs"
+
#include "log/log.h"
#define UNUSED __attribute__((__unused__))
diff --git a/libcutils/fs.c b/libcutils/fs.c
index 45c7add..b253b1c 100644
--- a/libcutils/fs.c
+++ b/libcutils/fs.c
@@ -21,25 +21,28 @@
#define _ATFILE_SOURCE 1
#define _GNU_SOURCE 1
-#include <cutils/fs.h>
-#include <cutils/log.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <limits.h>
-#include <stdlib.h>
#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/fs.h>
+#include <log/log.h>
#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
#define BUF_SIZE 64
-int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+static int fs_prepare_path_impl(const char* path, mode_t mode, uid_t uid, gid_t gid,
+ int allow_fixup, int prepare_as_dir) {
// Check if path needs to be created
struct stat sb;
+ int create_result = -1;
if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) {
if (errno == ENOENT) {
goto create;
@@ -50,24 +53,46 @@
}
// Exists, verify status
- if (!S_ISDIR(sb.st_mode)) {
- ALOGE("Not a directory: %s", path);
+ int type_ok = prepare_as_dir ? S_ISDIR(sb.st_mode) : S_ISREG(sb.st_mode);
+ if (!type_ok) {
+ ALOGE("Not a %s: %s", (prepare_as_dir ? "directory" : "regular file"), path);
return -1;
}
- if (((sb.st_mode & ALL_PERMS) == mode) && (sb.st_uid == uid) && (sb.st_gid == gid)) {
- return 0;
- } else {
- goto fixup;
- }
-create:
- if (TEMP_FAILURE_RETRY(mkdir(path, mode)) == -1) {
- if (errno != EEXIST) {
- ALOGE("Failed to mkdir(%s): %s", path, strerror(errno));
+ int owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));
+ int mode_match = ((sb.st_mode & ALL_PERMS) == mode);
+ if (owner_match && mode_match) {
+ return 0;
+ } else if (allow_fixup) {
+ goto fixup;
+ } else {
+ if (!owner_match) {
+ ALOGE("Expected path %s with owner %d:%d but found %d:%d",
+ path, uid, gid, sb.st_uid, sb.st_gid);
return -1;
+ } else {
+ ALOGW("Expected path %s with mode %o but found %o",
+ path, mode, (sb.st_mode & ALL_PERMS));
+ return 0;
}
}
+create:
+ create_result = prepare_as_dir
+ ? TEMP_FAILURE_RETRY(mkdir(path, mode))
+ : TEMP_FAILURE_RETRY(open(path, O_CREAT | O_CLOEXEC | O_NOFOLLOW | O_RDONLY, 0644));
+ if (create_result == -1) {
+ if (errno != EEXIST) {
+ ALOGE("Failed to %s(%s): %s",
+ (prepare_as_dir ? "mkdir" : "open"), path, strerror(errno));
+ return -1;
+ }
+ } else if (!prepare_as_dir) {
+ // For regular files we need to make sure we close the descriptor
+ if (close(create_result) == -1) {
+ ALOGW("Failed to close file after create %s: %s", path, strerror(errno));
+ }
+ }
fixup:
if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) {
ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno));
@@ -81,6 +106,18 @@
return 0;
}
+int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+ return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 1, /*prepare_as_dir*/ 1);
+}
+
+int fs_prepare_dir_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+ return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 1);
+}
+
+int fs_prepare_file_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+ return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 0);
+}
+
int fs_read_atomic_int(const char* path, int* out_value) {
int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));
if (fd == -1) {
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 5f6f8f9..013999a 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -79,6 +79,7 @@
{ 00500, AID_ROOT, AID_ROOT, 0, "config" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral" },
{ 00771, AID_ROOT, AID_ROOT, 0, "data/dalvik-cache" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
{ 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" },
@@ -88,6 +89,9 @@
{ 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
{ 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
{ 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64" },
+ { 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_ROOT, 0, "root" },
@@ -115,7 +119,6 @@
{ 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" },
{ 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" },
{ 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" },
- { 00550, AID_DHCP, AID_SHELL, 0, "system/etc/dhcpcd/dhcpcd-run-hooks" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
{ 00440, AID_ROOT, AID_ROOT, 0, "system/etc/recovery.img" },
@@ -124,20 +127,62 @@
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
{ 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
+ { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral/*" },
{ 00644, AID_APP, AID_APP, 0, "data/data/*" },
+ { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest/tests.txt" },
+ { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest64/tests.txt" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest/*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64/*" },
- /* the following five files are INTENTIONALLY set-uid, but they
+ /* the following two files are INTENTIONALLY set-uid, but they
* are NOT included on user builds. */
{ 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" },
- { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" },
- { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" },
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" },
- { 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" },
/* the following files have enhanced capabilities and ARE included in user builds. */
- { 00750, AID_ROOT, AID_SHELL, (1ULL << CAP_SETUID) | (1ULL << CAP_SETGID), "system/bin/run-as" },
- { 00700, AID_SYSTEM, AID_SHELL, (1ULL << CAP_BLOCK_SUSPEND), "system/bin/inputflinger" },
+ { 00550, AID_LOGD, AID_LOGD, CAP_MASK_LONG(CAP_SYSLOG) |
+ CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
+ CAP_MASK_LONG(CAP_SETGID),
+ "system/bin/logd" },
+ { 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
+ CAP_MASK_LONG(CAP_SETGID),
+ "system/bin/run-as" },
+ { 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
+ "system/bin/inputflinger" },
+ /* Support FIFO scheduling mode in SurfaceFlinger. */
+ { 00755, AID_SYSTEM, AID_GRAPHICS, CAP_MASK_LONG(CAP_SYS_NICE), "system/bin/surfaceflinger" },
+
+ /* Support hostapd administering a network interface. */
+ { 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
+ CAP_MASK_LONG(CAP_NET_RAW),
+ "system/bin/hostapd" },
+
+ /* Support wifi_hal_legacy administering a network interface. */
+ { 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
+ CAP_MASK_LONG(CAP_NET_RAW),
+ "system/bin/hw/android.hardware.wifi@1.0-service" },
+
+ /* Support Bluetooth legacy hal accessing /sys/class/rfkill */
+ { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN),
+ "system/bin/hw/android.hardware.bluetooth@1.0-service" },
+
+ /* A non-privileged zygote that spawns isolated processes for web rendering. */
+ { 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) |
+ CAP_MASK_LONG(CAP_SETGID) |
+ CAP_MASK_LONG(CAP_SETPCAP),
+ "system/bin/webview_zygote32" },
+ { 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) |
+ CAP_MASK_LONG(CAP_SETGID) |
+ CAP_MASK_LONG(CAP_SETPCAP),
+ "system/bin/webview_zygote64" },
+
+ { 00755, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SYS_PTRACE),
+ "system/bin/crash_dump32" },
+ { 00755, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SYS_PTRACE),
+ "system/bin/crash_dump64" },
+
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" },
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" },
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
@@ -145,23 +190,38 @@
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "system/build.prop" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "odm/build.prop" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "default.prop" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "vendor/default.prop" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "odm/default.prop" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
};
-static int fs_config_open(int dir)
+static int fs_config_open(int dir, const char *target_out_path)
{
int fd = -1;
- const char *out = getenv("OUT");
- if (out && *out) {
+ if (target_out_path && *target_out_path) {
+ /* target_out_path is the path to the directory holding content of system partition
+ but as we cannot guaranty it ends with '/system' we need this below skip_len logic */
char *name = NULL;
- if (asprintf(&name, "%s%s", out, dir ? conf_dir : conf_file) != -1) {
+ int target_out_path_len = strlen(target_out_path);
+ int skip_len = strlen("/system");
+
+ if (target_out_path[target_out_path_len] == '/') {
+ skip_len++;
+ }
+ if (asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len) != -1) {
fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
free(name);
}
@@ -191,7 +251,7 @@
return !strncmp(prefix, path, len);
}
-void fs_config(const char *path, int dir,
+void fs_config(const char *path, int dir, const char *target_out_path,
unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities)
{
const struct fs_path_config *pc;
@@ -203,7 +263,7 @@
plen = strlen(path);
- fd = fs_config_open(dir);
+ fd = fs_config_open(dir, target_out_path);
if (fd >= 0) {
struct fs_path_config_from_file header;
diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c
index 65539ea..ede3b98 100644
--- a/libcutils/hashmap.c
+++ b/libcutils/hashmap.c
@@ -77,6 +77,9 @@
/**
* Hashes the given key.
*/
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
static inline int hashKey(Hashmap* map, void* key) {
int h = map->hash(key);
@@ -152,6 +155,10 @@
free(map);
}
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
+/* FIXME: relies on signed integer overflow, which is undefined behavior */
int hashmapHash(void* key, size_t keySize) {
int h = keySize;
char* data = (char*) key;
diff --git a/libcutils/include/cutils/android_get_control_file.h b/libcutils/include/cutils/android_get_control_file.h
new file mode 100644
index 0000000..ed8fbf8
--- /dev/null
+++ b/libcutils/include/cutils/android_get_control_file.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_ANDROID_GET_CONTROL_FILE_H
+#define __CUTILS_ANDROID_GET_CONTROL_FILE_H
+
+#define ANDROID_FILE_ENV_PREFIX "ANDROID_FILE_"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * android_get_control_file - simple helper function to get the file
+ * descriptor of our init-managed file. `path' is the filename path as
+ * given in init.rc. Returns -1 on error.
+ */
+int android_get_control_file(const char* path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_ANDROID_GET_CONTROL_FILE_H */
diff --git a/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
similarity index 100%
rename from include/cutils/android_reboot.h
rename to libcutils/include/cutils/android_reboot.h
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
new file mode 100644
index 0000000..d80caa6
--- /dev/null
+++ b/libcutils/include/cutils/ashmem.h
@@ -0,0 +1,34 @@
+/* cutils/ashmem.h
+ **
+ ** Copyright 2008 The Android Open Source Project
+ **
+ ** This file is dual licensed. It may be redistributed and/or modified
+ ** under the terms of the Apache 2.0 License OR version 2 of the GNU
+ ** General Public License.
+ */
+
+#ifndef _CUTILS_ASHMEM_H
+#define _CUTILS_ASHMEM_H
+
+#include <stddef.h>
+
+#if defined(__BIONIC__)
+#include <linux/ashmem.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ashmem_valid(int fd);
+int ashmem_create_region(const char *name, size_t size);
+int ashmem_set_prot_region(int fd, int prot);
+int ashmem_pin_region(int fd, size_t offset, size_t len);
+int ashmem_unpin_region(int fd, size_t offset, size_t len);
+int ashmem_get_size_region(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CUTILS_ASHMEM_H */
diff --git a/libcutils/include/cutils/atomic.h b/libcutils/include/cutils/atomic.h
new file mode 100644
index 0000000..0c88bfe
--- /dev/null
+++ b/libcutils/include/cutils/atomic.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_H
+#define ANDROID_CUTILS_ATOMIC_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdatomic.h>
+
+#ifndef ANDROID_ATOMIC_INLINE
+#define ANDROID_ATOMIC_INLINE static inline
+#endif
+
+/*
+ * A handful of basic atomic operations.
+ * THESE ARE HERE FOR LEGACY REASONS ONLY. AVOID.
+ *
+ * PREFERRED ALTERNATIVES:
+ * - Use C++/C/pthread locks/mutexes whenever there is not a
+ * convincing reason to do otherwise. Note that very clever and
+ * complicated, but correct, lock-free code is often slower than
+ * using locks, especially where nontrivial data structures
+ * are involved.
+ * - C11 stdatomic.h.
+ * - Where supported, C++11 std::atomic<T> .
+ *
+ * PLEASE STOP READING HERE UNLESS YOU ARE TRYING TO UNDERSTAND
+ * OR UPDATE OLD CODE.
+ *
+ * The "acquire" and "release" terms can be defined intuitively in terms
+ * of the placement of memory barriers in a simple lock implementation:
+ * - wait until compare-and-swap(lock-is-free --> lock-is-held) succeeds
+ * - barrier
+ * - [do work]
+ * - barrier
+ * - store(lock-is-free)
+ * In very crude terms, the initial (acquire) barrier prevents any of the
+ * "work" from happening before the lock is held, and the later (release)
+ * barrier ensures that all of the work happens before the lock is released.
+ * (Think of cached writes, cache read-ahead, and instruction reordering
+ * around the CAS and store instructions.)
+ *
+ * The barriers must apply to both the compiler and the CPU. Note it is
+ * legal for instructions that occur before an "acquire" barrier to be
+ * moved down below it, and for instructions that occur after a "release"
+ * barrier to be moved up above it.
+ *
+ * The ARM-driven implementation we use here is short on subtlety,
+ * and actually requests a full barrier from the compiler and the CPU.
+ * The only difference between acquire and release is in whether they
+ * are issued before or after the atomic operation with which they
+ * are associated. To ease the transition to C/C++ atomic intrinsics,
+ * you should not rely on this, and instead assume that only the minimal
+ * acquire/release protection is provided.
+ *
+ * NOTE: all int32_t* values are expected to be aligned on 32-bit boundaries.
+ * If they are not, atomicity is not guaranteed.
+ */
+
+ANDROID_ATOMIC_INLINE
+volatile atomic_int_least32_t* to_atomic_int_least32_t(volatile const int32_t* addr) {
+#ifdef __cplusplus
+ return reinterpret_cast<volatile atomic_int_least32_t*>(const_cast<volatile int32_t*>(addr));
+#else
+ return (volatile atomic_int_least32_t*)addr;
+#endif
+}
+
+/*
+ * Basic arithmetic and bitwise operations. These all provide a
+ * barrier with "release" ordering, and return the previous value.
+ *
+ * These have the same characteristics (e.g. what happens on overflow)
+ * as the equivalent non-atomic C operations.
+ */
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_inc(volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ /* Int32_t, if it exists, is the same as int_least32_t. */
+ return atomic_fetch_add_explicit(a, 1, memory_order_release);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_dec(volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ return atomic_fetch_sub_explicit(a, 1, memory_order_release);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ return atomic_fetch_add_explicit(a, value, memory_order_release);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ return atomic_fetch_and_explicit(a, value, memory_order_release);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ return atomic_fetch_or_explicit(a, value, memory_order_release);
+}
+
+/*
+ * Perform an atomic load with "acquire" or "release" ordering.
+ *
+ * Note that the notion of a "release" ordering for a load does not
+ * really fit into the C11 or C++11 memory model. The extra ordering
+ * is normally observable only by code using memory_order_relaxed
+ * atomics, or data races. In the rare cases in which such ordering
+ * is called for, use memory_order_relaxed atomics and a leading
+ * atomic_thread_fence (typically with memory_order_acquire,
+ * not memory_order_release!) instead. If you do not understand
+ * this comment, you are in the vast majority, and should not be
+ * using release loads or replacing them with anything other than
+ * locks or default sequentially consistent atomics.
+ */
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_acquire_load(volatile const int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ return atomic_load_explicit(a, memory_order_acquire);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_release_load(volatile const int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ atomic_thread_fence(memory_order_seq_cst);
+ /* Any reasonable clients of this interface would probably prefer */
+ /* something weaker. But some remaining clients seem to be */
+ /* abusing this API in strange ways, e.g. by using it as a fence. */
+ /* Thus we are conservative until we can get rid of remaining */
+ /* clients (and this function). */
+ return atomic_load_explicit(a, memory_order_relaxed);
+}
+
+/*
+ * Perform an atomic store with "acquire" or "release" ordering.
+ *
+ * Note that the notion of an "acquire" ordering for a store does not
+ * really fit into the C11 or C++11 memory model. The extra ordering
+ * is normally observable only by code using memory_order_relaxed
+ * atomics, or data races. In the rare cases in which such ordering
+ * is called for, use memory_order_relaxed atomics and a trailing
+ * atomic_thread_fence (typically with memory_order_release,
+ * not memory_order_acquire!) instead.
+ */
+ANDROID_ATOMIC_INLINE
+void android_atomic_acquire_store(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ atomic_store_explicit(a, value, memory_order_relaxed);
+ atomic_thread_fence(memory_order_seq_cst);
+ /* Again overly conservative to accomodate weird clients. */
+}
+
+ANDROID_ATOMIC_INLINE
+void android_atomic_release_store(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ atomic_store_explicit(a, value, memory_order_release);
+}
+
+/*
+ * Compare-and-set operation with "acquire" or "release" ordering.
+ *
+ * This returns zero if the new value was successfully stored, which will
+ * only happen when *addr == oldvalue.
+ *
+ * (The return value is inverted from implementations on other platforms,
+ * but matches the ARM ldrex/strex result.)
+ *
+ * Implementations that use the release CAS in a loop may be less efficient
+ * than possible, because we re-issue the memory barrier on each iteration.
+ */
+ANDROID_ATOMIC_INLINE
+int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ return !atomic_compare_exchange_strong_explicit(
+ a, &oldvalue, newvalue,
+ memory_order_acquire,
+ memory_order_acquire);
+}
+
+ANDROID_ATOMIC_INLINE
+int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
+ return !atomic_compare_exchange_strong_explicit(
+ a, &oldvalue, newvalue,
+ memory_order_release,
+ memory_order_relaxed);
+}
+
+/*
+ * Fence primitives.
+ */
+ANDROID_ATOMIC_INLINE
+void android_compiler_barrier(void)
+{
+ __asm__ __volatile__ ("" : : : "memory");
+ /* Could probably also be: */
+ /* atomic_signal_fence(memory_order_seq_cst); */
+}
+
+ANDROID_ATOMIC_INLINE
+void android_memory_barrier(void)
+{
+ atomic_thread_fence(memory_order_seq_cst);
+}
+
+/*
+ * Aliases for code using an older version of this header. These are now
+ * deprecated and should not be used. The definitions will be removed
+ * in a future release.
+ */
+#define android_atomic_write android_atomic_release_store
+#define android_atomic_cmpxchg android_atomic_release_cas
+
+#endif // ANDROID_CUTILS_ATOMIC_H
diff --git a/include/cutils/bitops.h b/libcutils/include/cutils/bitops.h
similarity index 100%
rename from include/cutils/bitops.h
rename to libcutils/include/cutils/bitops.h
diff --git a/include/cutils/compiler.h b/libcutils/include/cutils/compiler.h
similarity index 100%
rename from include/cutils/compiler.h
rename to libcutils/include/cutils/compiler.h
diff --git a/include/cutils/config_utils.h b/libcutils/include/cutils/config_utils.h
similarity index 100%
rename from include/cutils/config_utils.h
rename to libcutils/include/cutils/config_utils.h
diff --git a/libcutils/include/cutils/fs.h b/libcutils/include/cutils/fs.h
new file mode 100644
index 0000000..a34ce86
--- /dev/null
+++ b/libcutils/include/cutils/fs.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 __CUTILS_FS_H
+#define __CUTILS_FS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Ensure that directory exists with given mode and owners. If it exists
+ * with a different mode or owners, they are fixed to match the given values.
+ */
+extern int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
+
+/*
+ * Ensure that directory exists with given mode and owners. If it exists
+ * with different owners, they are not fixed and -1 is returned.
+ */
+extern int fs_prepare_dir_strict(const char* path, mode_t mode, uid_t uid, gid_t gid);
+
+/*
+ * Ensure that file exists with given mode and owners. If it exists
+ * with different owners, they are not fixed and -1 is returned.
+ */
+extern int fs_prepare_file_strict(const char* path, mode_t mode, uid_t uid, gid_t gid);
+
+
+/*
+ * Read single plaintext integer from given file, correctly handling files
+ * partially written with fs_write_atomic_int().
+ */
+extern int fs_read_atomic_int(const char* path, int* value);
+
+/*
+ * Write single plaintext integer to given file, creating backup while
+ * in progress.
+ */
+extern int fs_write_atomic_int(const char* path, int value);
+
+/*
+ * Ensure that all directories along given path exist, creating parent
+ * directories as needed. Validates that given path is absolute and that
+ * it contains no relative "." or ".." paths or symlinks. Last path segment
+ * is treated as filename and ignored, unless the path ends with "/".
+ */
+extern int fs_mkdirs(const char* path, mode_t mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_FS_H */
diff --git a/include/cutils/hashmap.h b/libcutils/include/cutils/hashmap.h
similarity index 100%
rename from include/cutils/hashmap.h
rename to libcutils/include/cutils/hashmap.h
diff --git a/include/cutils/iosched_policy.h b/libcutils/include/cutils/iosched_policy.h
similarity index 100%
rename from include/cutils/iosched_policy.h
rename to libcutils/include/cutils/iosched_policy.h
diff --git a/include/cutils/jstring.h b/libcutils/include/cutils/jstring.h
similarity index 100%
rename from include/cutils/jstring.h
rename to libcutils/include/cutils/jstring.h
diff --git a/libcutils/include/cutils/klog.h b/libcutils/include/cutils/klog.h
new file mode 100644
index 0000000..5ae6216
--- /dev/null
+++ b/libcutils/include/cutils/klog.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CUTILS_KLOG_H_
+#define _CUTILS_KLOG_H_
+
+#include <sys/cdefs.h>
+#include <sys/uio.h>
+#include <stdarg.h>
+
+__BEGIN_DECLS
+
+void klog_set_level(int level);
+
+void klog_write(int level, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+void klog_writev(int level, const struct iovec* iov, int iov_count);
+
+__END_DECLS
+
+#define KLOG_ERROR_LEVEL 3
+#define KLOG_WARNING_LEVEL 4
+#define KLOG_NOTICE_LEVEL 5
+#define KLOG_INFO_LEVEL 6
+#define KLOG_DEBUG_LEVEL 7
+
+#define KLOG_ERROR(tag,x...) klog_write(KLOG_ERROR_LEVEL, "<3>" tag ": " x)
+#define KLOG_WARNING(tag,x...) klog_write(KLOG_WARNING_LEVEL, "<4>" tag ": " x)
+#define KLOG_NOTICE(tag,x...) klog_write(KLOG_NOTICE_LEVEL, "<5>" tag ": " x)
+#define KLOG_INFO(tag,x...) klog_write(KLOG_INFO_LEVEL, "<6>" tag ": " x)
+#define KLOG_DEBUG(tag,x...) klog_write(KLOG_DEBUG_LEVEL, "<7>" tag ": " x)
+
+#endif
diff --git a/include/cutils/list.h b/libcutils/include/cutils/list.h
similarity index 100%
rename from include/cutils/list.h
rename to libcutils/include/cutils/list.h
diff --git a/include/cutils/log.h b/libcutils/include/cutils/log.h
similarity index 100%
rename from include/cutils/log.h
rename to libcutils/include/cutils/log.h
diff --git a/include/cutils/memory.h b/libcutils/include/cutils/memory.h
similarity index 100%
rename from include/cutils/memory.h
rename to libcutils/include/cutils/memory.h
diff --git a/include/cutils/misc.h b/libcutils/include/cutils/misc.h
similarity index 100%
rename from include/cutils/misc.h
rename to libcutils/include/cutils/misc.h
diff --git a/libcutils/include/cutils/multiuser.h b/libcutils/include/cutils/multiuser.h
new file mode 100644
index 0000000..5bd9c7b
--- /dev/null
+++ b/libcutils/include/cutils/multiuser.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 __CUTILS_MULTIUSER_H
+#define __CUTILS_MULTIUSER_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uid_t userid_t;
+typedef uid_t appid_t;
+
+extern userid_t multiuser_get_user_id(uid_t uid);
+extern appid_t multiuser_get_app_id(uid_t uid);
+
+extern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);
+
+extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);
+
+/* TODO: switch callers over to multiuser_get_shared_gid() */
+extern gid_t multiuser_get_shared_app_gid(uid_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_MULTIUSER_H */
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
new file mode 100644
index 0000000..7d6a988
--- /dev/null
+++ b/libcutils/include/cutils/native_handle.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 NATIVE_HANDLE_H_
+#define NATIVE_HANDLE_H_
+
+#include <stdalign.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Declare a char array for use with native_handle_init */
+#define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \
+ alignas(native_handle_t) char name[ \
+ sizeof(native_handle_t) + sizeof(int) * (maxFds + maxInts)]
+
+typedef struct native_handle
+{
+ int version; /* sizeof(native_handle_t) */
+ int numFds; /* number of file-descriptors at &data[0] */
+ int numInts; /* number of ints at &data[numFds] */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
+#endif
+ int data[0]; /* numFds + numInts ints */
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+} native_handle_t;
+
+/*
+ * native_handle_close
+ *
+ * closes the file descriptors contained in this native_handle_t
+ *
+ * return 0 on success, or a negative error code on failure
+ *
+ */
+int native_handle_close(const native_handle_t* h);
+
+/*
+ * native_handle_init
+ *
+ * Initializes a native_handle_t from storage. storage must be declared with
+ * NATIVE_HANDLE_DECLARE_STORAGE. numFds and numInts must not respectively
+ * exceed maxFds and maxInts used to declare the storage.
+ */
+native_handle_t* native_handle_init(char* storage, int numFds, int numInts);
+
+/*
+ * native_handle_create
+ *
+ * creates a native_handle_t and initializes it. must be destroyed with
+ * native_handle_delete().
+ *
+ */
+native_handle_t* native_handle_create(int numFds, int numInts);
+
+/*
+ * native_handle_clone
+ *
+ * creates a native_handle_t and initializes it from another native_handle_t.
+ * Must be destroyed with native_handle_delete().
+ *
+ */
+native_handle_t* native_handle_clone(const native_handle_t* handle);
+
+/*
+ * native_handle_delete
+ *
+ * frees a native_handle_t allocated with native_handle_create().
+ * This ONLY frees the memory allocated for the native_handle_t, but doesn't
+ * close the file descriptors; which can be achieved with native_handle_close().
+ *
+ * return 0 on success, or a negative error code on failure
+ *
+ */
+int native_handle_delete(native_handle_t* h);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NATIVE_HANDLE_H_ */
diff --git a/include/cutils/open_memstream.h b/libcutils/include/cutils/open_memstream.h
similarity index 100%
rename from include/cutils/open_memstream.h
rename to libcutils/include/cutils/open_memstream.h
diff --git a/include/cutils/partition_utils.h b/libcutils/include/cutils/partition_utils.h
similarity index 100%
rename from include/cutils/partition_utils.h
rename to libcutils/include/cutils/partition_utils.h
diff --git a/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
new file mode 100644
index 0000000..adf670b
--- /dev/null
+++ b/libcutils/include/cutils/properties.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_PROPERTIES_H
+#define __CUTILS_PROPERTIES_H
+
+#include <sys/cdefs.h>
+#include <stddef.h>
+#include <sys/system_properties.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* System properties are *small* name value pairs managed by the
+** property service. If your data doesn't fit in the provided
+** space it is not appropriate for a system property.
+**
+** WARNING: system/bionic/include/sys/system_properties.h also defines
+** these, but with different names. (TODO: fix that)
+*/
+#define PROPERTY_KEY_MAX PROP_NAME_MAX
+#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
+
+/* property_get: returns the length of the value which will never be
+** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
+** (the length does not include the terminating zero).
+**
+** If the property read fails or returns an empty value, the default
+** value is used (if nonnull).
+*/
+int property_get(const char *key, char *value, const char *default_value);
+
+/* property_get_bool: returns the value of key coerced into a
+** boolean. If the property is not set, then the default value is returned.
+**
+* The following is considered to be true (1):
+** "1", "true", "y", "yes", "on"
+**
+** The following is considered to be false (0):
+** "0", "false", "n", "no", "off"
+**
+** The conversion is whitespace-sensitive (e.g. " off" will not be false).
+**
+** If no property with this key is set (or the key is NULL) or the boolean
+** conversion fails, the default value is returned.
+**/
+int8_t property_get_bool(const char *key, int8_t default_value);
+
+/* property_get_int64: returns the value of key truncated and coerced into a
+** int64_t. If the property is not set, then the default value is used.
+**
+** The numeric conversion is identical to strtoimax with the base inferred:
+** - All digits up to the first non-digit characters are read
+** - The longest consecutive prefix of digits is converted to a long
+**
+** Valid strings of digits are:
+** - An optional sign character + or -
+** - An optional prefix indicating the base (otherwise base 10 is assumed)
+** -- 0 prefix is octal
+** -- 0x / 0X prefix is hex
+**
+** Leading/trailing whitespace is ignored. Overflow/underflow will cause
+** numeric conversion to fail.
+**
+** If no property with this key is set (or the key is NULL) or the numeric
+** conversion fails, the default value is returned.
+**/
+int64_t property_get_int64(const char *key, int64_t default_value);
+
+/* property_get_int32: returns the value of key truncated and coerced into an
+** int32_t. If the property is not set, then the default value is used.
+**
+** The numeric conversion is identical to strtoimax with the base inferred:
+** - All digits up to the first non-digit characters are read
+** - The longest consecutive prefix of digits is converted to a long
+**
+** Valid strings of digits are:
+** - An optional sign character + or -
+** - An optional prefix indicating the base (otherwise base 10 is assumed)
+** -- 0 prefix is octal
+** -- 0x / 0X prefix is hex
+**
+** Leading/trailing whitespace is ignored. Overflow/underflow will cause
+** numeric conversion to fail.
+**
+** If no property with this key is set (or the key is NULL) or the numeric
+** conversion fails, the default value is returned.
+**/
+int32_t property_get_int32(const char *key, int32_t default_value);
+
+/* property_set: returns 0 on success, < 0 on failure
+*/
+int property_set(const char *key, const char *value);
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
+
+#if defined(__BIONIC_FORTIFY) && !defined(__clang__)
+
+extern int __property_get_real(const char *, char *, const char *)
+ __asm__(__USER_LABEL_PREFIX__ "property_get");
+__errordecl(__property_get_too_small_error, "property_get() called with too small of a buffer");
+
+__BIONIC_FORTIFY_INLINE
+int property_get(const char *key, char *value, const char *default_value) {
+ size_t bos = __bos(value);
+ if (bos < PROPERTY_VALUE_MAX) {
+ __property_get_too_small_error();
+ }
+ return __property_get_real(key, value, default_value);
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
similarity index 100%
rename from include/cutils/qtaguid.h
rename to libcutils/include/cutils/qtaguid.h
diff --git a/include/cutils/record_stream.h b/libcutils/include/cutils/record_stream.h
similarity index 100%
rename from include/cutils/record_stream.h
rename to libcutils/include/cutils/record_stream.h
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
new file mode 100644
index 0000000..591bd44
--- /dev/null
+++ b/libcutils/include/cutils/sched_policy.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_SCHED_POLICY_H
+#define __CUTILS_SCHED_POLICY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
+typedef enum {
+ SP_DEFAULT = -1,
+ SP_BACKGROUND = 0,
+ SP_FOREGROUND = 1,
+ SP_SYSTEM = 2, // can't be used with set_sched_policy()
+ SP_AUDIO_APP = 3,
+ SP_AUDIO_SYS = 4,
+ SP_TOP_APP = 5,
+ SP_CNT,
+ SP_MAX = SP_CNT - 1,
+ SP_SYSTEM_DEFAULT = SP_FOREGROUND,
+} SchedPolicy;
+
+extern int set_cpuset_policy(int tid, SchedPolicy policy);
+
+/* Assign thread tid to the cgroup associated with the specified policy.
+ * If the thread is a thread group leader, that is it's gettid() == getpid(),
+ * then the other threads in the same thread group are _not_ affected.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -errno for error.
+ */
+extern int set_sched_policy(int tid, SchedPolicy policy);
+
+/* Return the policy associated with the cgroup of thread tid via policy pointer.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -1 for error and set errno.
+ */
+extern int get_sched_policy(int tid, SchedPolicy *policy);
+
+/* Return a displayable string corresponding to policy.
+ * Return value: non-NULL NUL-terminated name of unspecified length;
+ * the caller is responsible for displaying the useful part of the string.
+ */
+extern const char *get_sched_policy_name(SchedPolicy policy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_SCHED_POLICY_H */
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
new file mode 100644
index 0000000..d724dd6
--- /dev/null
+++ b/libcutils/include/cutils/sockets.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_SOCKETS_H
+#define __CUTILS_SOCKETS_H
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#if defined(_WIN32)
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+typedef int socklen_t;
+typedef SOCKET cutils_socket_t;
+
+#else
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+typedef int cutils_socket_t;
+#define INVALID_SOCKET (-1)
+
+#endif
+
+#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
+#define ANDROID_SOCKET_DIR "/dev/socket"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * android_get_control_socket - simple helper function to get the file
+ * descriptor of our init-managed Unix domain socket. `name' is the name of the
+ * socket, as given in init.rc. Returns -1 on error.
+ */
+int android_get_control_socket(const char* name);
+
+/*
+ * See also android.os.LocalSocketAddress.Namespace
+ */
+// Linux "abstract" (non-filesystem) namespace
+#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
+// Android "reserved" (/dev/socket) namespace
+#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
+// Normal filesystem namespace
+#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
+
+/*
+ * Functions to create sockets for some common usages.
+ *
+ * All these functions are implemented for Unix, but only a few are implemented
+ * for Windows. Those which are can be identified by the cutils_socket_t
+ * return type. The idea is to be able to use this return value with the
+ * standard Unix socket functions on any platform.
+ *
+ * On Unix the returned cutils_socket_t is a standard int file descriptor and
+ * can always be used as normal with all file descriptor functions.
+ *
+ * On Windows utils_socket_t is an unsigned int pointer, and is only valid
+ * with functions that specifically take a socket, e.g. send(), sendto(),
+ * recv(), and recvfrom(). General file descriptor functions such as read(),
+ * write(), and close() will not work with utils_socket_t and will require
+ * special handling.
+ *
+ * These functions return INVALID_SOCKET (-1) on failure for all platforms.
+ */
+cutils_socket_t socket_network_client(const char* host, int port, int type);
+int socket_network_client_timeout(const char* host, int port, int type,
+ int timeout, int* getaddrinfo_error);
+int socket_loopback_server(int port, int type);
+int socket_loopback_server6(int port, int type);
+int socket_local_server(const char* name, int namespaceId, int type);
+int socket_local_server_bind(int s, const char* name, int namespaceId);
+int socket_local_client_connect(int fd, const char *name, int namespaceId,
+ int type);
+int socket_local_client(const char* name, int namespaceId, int type);
+cutils_socket_t socket_inaddr_any_server(int port, int type);
+
+/*
+ * Closes a cutils_socket_t. Windows doesn't allow calling close() on a socket
+ * so this is a cross-platform way to close a cutils_socket_t.
+ *
+ * Returns 0 on success.
+ */
+int socket_close(cutils_socket_t sock);
+
+/*
+ * Sets socket receive timeout using SO_RCVTIMEO. Setting |timeout_ms| to 0
+ * disables receive timeouts.
+ *
+ * Return 0 on success.
+ */
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms);
+
+/*
+ * Returns the local port the socket is bound to or -1 on error.
+ */
+int socket_get_local_port(cutils_socket_t sock);
+
+/*
+ * Sends to a socket from multiple buffers; wraps writev() on Unix or WSASend()
+ * on Windows. This can give significant speedup compared to calling send()
+ * multiple times.
+ *
+ * Example usage:
+ * cutils_socket_buffer_t buffers[2] = { {data0, len0}, {data1, len1} };
+ * socket_send_buffers(sock, buffers, 2);
+ *
+ * If you try to pass more than SOCKET_SEND_BUFFERS_MAX_BUFFERS buffers into
+ * this function it will return -1 without sending anything.
+ *
+ * Returns the number of bytes written or -1 on error.
+ */
+typedef struct {
+ const void* data;
+ size_t length;
+} cutils_socket_buffer_t;
+
+#define SOCKET_SEND_BUFFERS_MAX_BUFFERS 16
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+ const cutils_socket_buffer_t* buffers,
+ size_t num_buffers);
+
+/*
+ * socket_peer_is_trusted - Takes a socket which is presumed to be a
+ * connected local socket (e.g. AF_LOCAL) and returns whether the peer
+ * (the userid that owns the process on the other end of that socket)
+ * is one of the two trusted userids, root or shell.
+ *
+ * Note: This only works as advertised on the Android OS and always
+ * just returns true when called on other operating systems.
+ */
+extern bool socket_peer_is_trusted(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_SOCKETS_H */
diff --git a/include/cutils/str_parms.h b/libcutils/include/cutils/str_parms.h
similarity index 100%
rename from include/cutils/str_parms.h
rename to libcutils/include/cutils/str_parms.h
diff --git a/include/cutils/threads.h b/libcutils/include/cutils/threads.h
similarity index 100%
rename from include/cutils/threads.h
rename to libcutils/include/cutils/threads.h
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
new file mode 100644
index 0000000..fcbdc9b
--- /dev/null
+++ b/libcutils/include/cutils/trace.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_TRACE_H
+#define _LIBS_CUTILS_TRACE_H
+
+#include <inttypes.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/compiler.h>
+
+__BEGIN_DECLS
+
+/**
+ * The ATRACE_TAG macro can be defined before including this header to trace
+ * using one of the tags defined below. It must be defined to one of the
+ * following ATRACE_TAG_* macros. The trace tag is used to filter tracing in
+ * userland to avoid some of the runtime cost of tracing when it is not desired.
+ *
+ * Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always
+ * being enabled - this should ONLY be done for debug code, as userland tracing
+ * has a performance cost even when the trace is not being recorded. Defining
+ * ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result
+ * in the tracing always being disabled.
+ *
+ * ATRACE_TAG_HAL should be bitwise ORed with the relevant tags for tracing
+ * within a hardware module. For example a camera hardware module would set:
+ * #define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
+ *
+ * Keep these in sync with frameworks/base/core/java/android/os/Trace.java.
+ */
+#define ATRACE_TAG_NEVER 0 // This tag is never enabled.
+#define ATRACE_TAG_ALWAYS (1<<0) // This tag is always enabled.
+#define ATRACE_TAG_GRAPHICS (1<<1)
+#define ATRACE_TAG_INPUT (1<<2)
+#define ATRACE_TAG_VIEW (1<<3)
+#define ATRACE_TAG_WEBVIEW (1<<4)
+#define ATRACE_TAG_WINDOW_MANAGER (1<<5)
+#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6)
+#define ATRACE_TAG_SYNC_MANAGER (1<<7)
+#define ATRACE_TAG_AUDIO (1<<8)
+#define ATRACE_TAG_VIDEO (1<<9)
+#define ATRACE_TAG_CAMERA (1<<10)
+#define ATRACE_TAG_HAL (1<<11)
+#define ATRACE_TAG_APP (1<<12)
+#define ATRACE_TAG_RESOURCES (1<<13)
+#define ATRACE_TAG_DALVIK (1<<14)
+#define ATRACE_TAG_RS (1<<15)
+#define ATRACE_TAG_BIONIC (1<<16)
+#define ATRACE_TAG_POWER (1<<17)
+#define ATRACE_TAG_PACKAGE_MANAGER (1<<18)
+#define ATRACE_TAG_SYSTEM_SERVER (1<<19)
+#define ATRACE_TAG_DATABASE (1<<20)
+#define ATRACE_TAG_NETWORK (1<<21)
+#define ATRACE_TAG_ADB (1<<22)
+#define ATRACE_TAG_LAST ATRACE_TAG_ADB
+
+// Reserved for initialization.
+#define ATRACE_TAG_NOT_READY (1ULL<<63)
+
+#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
+
+#ifndef ATRACE_TAG
+#define ATRACE_TAG ATRACE_TAG_NEVER
+#elif ATRACE_TAG > ATRACE_TAG_VALID_MASK
+#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
+#endif
+
+/**
+ * Opens the trace file for writing and reads the property for initial tags.
+ * The atrace.tags.enableflags property sets the tags to trace.
+ * This function should not be explicitly called, the first call to any normal
+ * trace function will cause it to be run safely.
+ */
+void atrace_setup();
+
+/**
+ * If tracing is ready, set atrace_enabled_tags to the system property
+ * debug.atrace.tags.enableflags. Can be used as a sysprop change callback.
+ */
+void atrace_update_tags();
+
+/**
+ * Set whether the process is debuggable. By default the process is not
+ * considered debuggable. If the process is not debuggable then application-
+ * level tracing is not allowed unless the ro.debuggable system property is
+ * set to '1'.
+ */
+void atrace_set_debuggable(bool debuggable);
+
+/**
+ * Set whether tracing is enabled for the current process. This is used to
+ * prevent tracing within the Zygote process.
+ */
+void atrace_set_tracing_enabled(bool enabled);
+
+/**
+ * Flag indicating whether setup has been completed, initialized to 0.
+ * Nonzero indicates setup has completed.
+ * Note: This does NOT indicate whether or not setup was successful.
+ */
+extern atomic_bool atrace_is_ready;
+
+/**
+ * Set of ATRACE_TAG flags to trace for, initialized to ATRACE_TAG_NOT_READY.
+ * A value of zero indicates setup has failed.
+ * Any other nonzero value indicates setup has succeeded, and tracing is on.
+ */
+extern uint64_t atrace_enabled_tags;
+
+/**
+ * Handle to the kernel's trace buffer, initialized to -1.
+ * Any other value indicates setup has succeeded, and is a valid fd for tracing.
+ */
+extern int atrace_marker_fd;
+
+/**
+ * atrace_init readies the process for tracing by opening the trace_marker file.
+ * Calling any trace function causes this to be run, so calling it is optional.
+ * This can be explicitly run to avoid setup delay on first trace function.
+ */
+#define ATRACE_INIT() atrace_init()
+static inline void atrace_init()
+{
+ if (CC_UNLIKELY(!atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+ atrace_setup();
+ }
+}
+
+/**
+ * Get the mask of all tags currently enabled.
+ * It can be used as a guard condition around more expensive trace calculations.
+ * Every trace function calls this, which ensures atrace_init is run.
+ */
+#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
+static inline uint64_t atrace_get_enabled_tags()
+{
+ atrace_init();
+ return atrace_enabled_tags;
+}
+
+/**
+ * Test if a given tag is currently enabled.
+ * Returns nonzero if the tag is enabled, otherwise zero.
+ * It can be used as a guard condition around more expensive trace calculations.
+ */
+#define ATRACE_ENABLED() atrace_is_tag_enabled(ATRACE_TAG)
+static inline uint64_t atrace_is_tag_enabled(uint64_t tag)
+{
+ return atrace_get_enabled_tags() & tag;
+}
+
+/**
+ * Trace the beginning of a context. name is used to identify the context.
+ * This is often used to time function execution.
+ */
+#define ATRACE_BEGIN(name) atrace_begin(ATRACE_TAG, name)
+static inline void atrace_begin(uint64_t tag, const char* name)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_begin_body(const char*);
+ atrace_begin_body(name);
+ }
+}
+
+/**
+ * Trace the end of a context.
+ * This should match up (and occur after) a corresponding ATRACE_BEGIN.
+ */
+#define ATRACE_END() atrace_end(ATRACE_TAG)
+static inline void atrace_end(uint64_t tag)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_end_body();
+ atrace_end_body();
+ }
+}
+
+/**
+ * Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END
+ * contexts, asynchronous events do not need to be nested. The name describes
+ * the event, and the cookie provides a unique identifier for distinguishing
+ * simultaneous events. The name and cookie used to begin an event must be
+ * used to end it.
+ */
+#define ATRACE_ASYNC_BEGIN(name, cookie) \
+ atrace_async_begin(ATRACE_TAG, name, cookie)
+static inline void atrace_async_begin(uint64_t tag, const char* name,
+ int32_t cookie)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_async_begin_body(const char*, int32_t);
+ atrace_async_begin_body(name, cookie);
+ }
+}
+
+/**
+ * Trace the end of an asynchronous event.
+ * This should have a corresponding ATRACE_ASYNC_BEGIN.
+ */
+#define ATRACE_ASYNC_END(name, cookie) atrace_async_end(ATRACE_TAG, name, cookie)
+static inline void atrace_async_end(uint64_t tag, const char* name, int32_t cookie)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_async_end_body(const char*, int32_t);
+ atrace_async_end_body(name, cookie);
+ }
+}
+
+/**
+ * Traces an integer counter value. name is used to identify the counter.
+ * This can be used to track how a value changes over time.
+ */
+#define ATRACE_INT(name, value) atrace_int(ATRACE_TAG, name, value)
+static inline void atrace_int(uint64_t tag, const char* name, int32_t value)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_int_body(const char*, int32_t);
+ atrace_int_body(name, value);
+ }
+}
+
+/**
+ * Traces a 64-bit integer counter value. name is used to identify the
+ * counter. This can be used to track how a value changes over time.
+ */
+#define ATRACE_INT64(name, value) atrace_int64(ATRACE_TAG, name, value)
+static inline void atrace_int64(uint64_t tag, const char* name, int64_t value)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_int64_body(const char*, int64_t);
+ atrace_int64_body(name, value);
+ }
+}
+
+__END_DECLS
+
+#endif // _LIBS_CUTILS_TRACE_H
diff --git a/include/cutils/uevent.h b/libcutils/include/cutils/uevent.h
similarity index 100%
rename from include/cutils/uevent.h
rename to libcutils/include/cutils/uevent.h
diff --git a/libcutils/include_vndk/cutils/android_get_control_file.h b/libcutils/include_vndk/cutils/android_get_control_file.h
new file mode 120000
index 0000000..70d6a3b
--- /dev/null
+++ b/libcutils/include_vndk/cutils/android_get_control_file.h
@@ -0,0 +1 @@
+../../include/cutils/android_get_control_file.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/android_reboot.h b/libcutils/include_vndk/cutils/android_reboot.h
new file mode 120000
index 0000000..9e1bf4c
--- /dev/null
+++ b/libcutils/include_vndk/cutils/android_reboot.h
@@ -0,0 +1 @@
+../../include/cutils/android_reboot.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/ashmem.h b/libcutils/include_vndk/cutils/ashmem.h
new file mode 120000
index 0000000..5c07beb
--- /dev/null
+++ b/libcutils/include_vndk/cutils/ashmem.h
@@ -0,0 +1 @@
+../../include/cutils/ashmem.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/atomic.h b/libcutils/include_vndk/cutils/atomic.h
new file mode 120000
index 0000000..f4f14fe
--- /dev/null
+++ b/libcutils/include_vndk/cutils/atomic.h
@@ -0,0 +1 @@
+../../include/cutils/atomic.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/bitops.h b/libcutils/include_vndk/cutils/bitops.h
new file mode 120000
index 0000000..edbd60c
--- /dev/null
+++ b/libcutils/include_vndk/cutils/bitops.h
@@ -0,0 +1 @@
+../../include/cutils/bitops.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/compiler.h b/libcutils/include_vndk/cutils/compiler.h
new file mode 120000
index 0000000..08ebc10
--- /dev/null
+++ b/libcutils/include_vndk/cutils/compiler.h
@@ -0,0 +1 @@
+../../include/cutils/compiler.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/config_utils.h b/libcutils/include_vndk/cutils/config_utils.h
new file mode 120000
index 0000000..e011738
--- /dev/null
+++ b/libcutils/include_vndk/cutils/config_utils.h
@@ -0,0 +1 @@
+../../include/cutils/config_utils.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/fs.h b/libcutils/include_vndk/cutils/fs.h
new file mode 120000
index 0000000..576bfa3
--- /dev/null
+++ b/libcutils/include_vndk/cutils/fs.h
@@ -0,0 +1 @@
+../../include/cutils/fs.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/hashmap.h b/libcutils/include_vndk/cutils/hashmap.h
new file mode 120000
index 0000000..6b18406
--- /dev/null
+++ b/libcutils/include_vndk/cutils/hashmap.h
@@ -0,0 +1 @@
+../../include/cutils/hashmap.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/iosched_policy.h b/libcutils/include_vndk/cutils/iosched_policy.h
new file mode 120000
index 0000000..26cf333
--- /dev/null
+++ b/libcutils/include_vndk/cutils/iosched_policy.h
@@ -0,0 +1 @@
+../../include/cutils/iosched_policy.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/jstring.h b/libcutils/include_vndk/cutils/jstring.h
new file mode 120000
index 0000000..f3fd546
--- /dev/null
+++ b/libcutils/include_vndk/cutils/jstring.h
@@ -0,0 +1 @@
+../../include/cutils/jstring.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/klog.h b/libcutils/include_vndk/cutils/klog.h
new file mode 120000
index 0000000..8ca85ff
--- /dev/null
+++ b/libcutils/include_vndk/cutils/klog.h
@@ -0,0 +1 @@
+../../include/cutils/klog.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/list.h b/libcutils/include_vndk/cutils/list.h
new file mode 120000
index 0000000..9fa4c90
--- /dev/null
+++ b/libcutils/include_vndk/cutils/list.h
@@ -0,0 +1 @@
+../../include/cutils/list.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/log.h b/libcutils/include_vndk/cutils/log.h
new file mode 100644
index 0000000..ae74024
--- /dev/null
+++ b/libcutils/include_vndk/cutils/log.h
@@ -0,0 +1,21 @@
+/*Special log.h file for VNDK linking modules*/
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+#ifndef _LIBS_CUTIL_LOG_H
+#define _LIBS_CUTIL_LOG_H
+#warning "Deprecated: don't include cutils/log.h, use either android/log.h or log/log.h"
+#include <log/log.h>
+#endif /* _LIBS_CUTIL_LOG_H */
diff --git a/libcutils/include_vndk/cutils/memory.h b/libcutils/include_vndk/cutils/memory.h
new file mode 120000
index 0000000..e0e7abc
--- /dev/null
+++ b/libcutils/include_vndk/cutils/memory.h
@@ -0,0 +1 @@
+../../include/cutils/memory.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/misc.h b/libcutils/include_vndk/cutils/misc.h
new file mode 120000
index 0000000..db09eb5
--- /dev/null
+++ b/libcutils/include_vndk/cutils/misc.h
@@ -0,0 +1 @@
+../../include/cutils/misc.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/multiuser.h b/libcutils/include_vndk/cutils/multiuser.h
new file mode 120000
index 0000000..524111f
--- /dev/null
+++ b/libcutils/include_vndk/cutils/multiuser.h
@@ -0,0 +1 @@
+../../include/cutils/multiuser.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/native_handle.h b/libcutils/include_vndk/cutils/native_handle.h
new file mode 120000
index 0000000..e324d4e
--- /dev/null
+++ b/libcutils/include_vndk/cutils/native_handle.h
@@ -0,0 +1 @@
+../../include/cutils/native_handle.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/open_memstream.h b/libcutils/include_vndk/cutils/open_memstream.h
new file mode 120000
index 0000000..c894084
--- /dev/null
+++ b/libcutils/include_vndk/cutils/open_memstream.h
@@ -0,0 +1 @@
+../../include/cutils/open_memstream.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/partition_utils.h b/libcutils/include_vndk/cutils/partition_utils.h
new file mode 120000
index 0000000..d9734c8
--- /dev/null
+++ b/libcutils/include_vndk/cutils/partition_utils.h
@@ -0,0 +1 @@
+../../include/cutils/partition_utils.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/properties.h b/libcutils/include_vndk/cutils/properties.h
new file mode 120000
index 0000000..d56118e
--- /dev/null
+++ b/libcutils/include_vndk/cutils/properties.h
@@ -0,0 +1 @@
+../../include/cutils/properties.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/qtaguid.h b/libcutils/include_vndk/cutils/qtaguid.h
new file mode 120000
index 0000000..bc02441
--- /dev/null
+++ b/libcutils/include_vndk/cutils/qtaguid.h
@@ -0,0 +1 @@
+../../include/cutils/qtaguid.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/record_stream.h b/libcutils/include_vndk/cutils/record_stream.h
new file mode 120000
index 0000000..8de6494
--- /dev/null
+++ b/libcutils/include_vndk/cutils/record_stream.h
@@ -0,0 +1 @@
+../../include/cutils/record_stream.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/sched_policy.h b/libcutils/include_vndk/cutils/sched_policy.h
new file mode 120000
index 0000000..ddebdd0
--- /dev/null
+++ b/libcutils/include_vndk/cutils/sched_policy.h
@@ -0,0 +1 @@
+../../include/cutils/sched_policy.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/sockets.h b/libcutils/include_vndk/cutils/sockets.h
new file mode 120000
index 0000000..585250c
--- /dev/null
+++ b/libcutils/include_vndk/cutils/sockets.h
@@ -0,0 +1 @@
+../../include/cutils/sockets.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/str_parms.h b/libcutils/include_vndk/cutils/str_parms.h
new file mode 120000
index 0000000..9c79a8f
--- /dev/null
+++ b/libcutils/include_vndk/cutils/str_parms.h
@@ -0,0 +1 @@
+../../include/cutils/str_parms.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/threads.h b/libcutils/include_vndk/cutils/threads.h
new file mode 120000
index 0000000..99330ff
--- /dev/null
+++ b/libcutils/include_vndk/cutils/threads.h
@@ -0,0 +1 @@
+../../include/cutils/threads.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/trace.h b/libcutils/include_vndk/cutils/trace.h
new file mode 120000
index 0000000..b12e140
--- /dev/null
+++ b/libcutils/include_vndk/cutils/trace.h
@@ -0,0 +1 @@
+../../include/cutils/trace.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/uevent.h b/libcutils/include_vndk/cutils/uevent.h
new file mode 120000
index 0000000..451283a
--- /dev/null
+++ b/libcutils/include_vndk/cutils/uevent.h
@@ -0,0 +1 @@
+../../include/cutils/uevent.h
\ No newline at end of file
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c
index 71bc94b..13c2ceb 100644
--- a/libcutils/iosched_policy.c
+++ b/libcutils/iosched_policy.c
@@ -24,7 +24,8 @@
#include <cutils/iosched_policy.h>
#if defined(__ANDROID__)
-#include <linux/ioprio.h>
+#define IOPRIO_WHO_PROCESS (1)
+#define IOPRIO_CLASS_SHIFT (13)
#include <sys/syscall.h>
#define __android_unused
#else
diff --git a/libcutils/klog.c b/libcutils/klog.c
deleted file mode 100644
index 710dc66..0000000
--- a/libcutils/klog.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/klog.h>
-
-static int klog_fd = -1;
-static int klog_level = KLOG_DEFAULT_LEVEL;
-
-int klog_get_level(void) {
- return klog_level;
-}
-
-void klog_set_level(int level) {
- klog_level = level;
-}
-
-void klog_init(void) {
- if (klog_fd >= 0) return; /* Already initialized */
-
- klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
- if (klog_fd >= 0) {
- return;
- }
-
- static const char* name = "/dev/__kmsg__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
- klog_fd = open(name, O_WRONLY | O_CLOEXEC);
- unlink(name);
- }
-}
-
-#define LOG_BUF_MAX 512
-
-void klog_writev(int level, const struct iovec* iov, int iov_count) {
- if (level > klog_level) return;
- if (klog_fd < 0) klog_init();
- if (klog_fd < 0) return;
- TEMP_FAILURE_RETRY(writev(klog_fd, iov, iov_count));
-}
-
-void klog_write(int level, const char* fmt, ...) {
- char buf[LOG_BUF_MAX];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
-
- buf[LOG_BUF_MAX - 1] = 0;
-
- struct iovec iov[1];
- iov[0].iov_base = buf;
- iov[0].iov_len = strlen(buf);
- klog_writev(level, iov, 1);
-}
diff --git a/libcutils/klog.cpp b/libcutils/klog.cpp
new file mode 100644
index 0000000..d301276
--- /dev/null
+++ b/libcutils/klog.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/android_get_control_file.h>
+#include <cutils/klog.h>
+
+static int klog_level = KLOG_INFO_LEVEL;
+
+void klog_set_level(int level) {
+ klog_level = level;
+}
+
+static int __open_klog(void) {
+ static const char kmsg_device[] = "/dev/kmsg";
+
+ int ret = android_get_control_file(kmsg_device);
+ if (ret >= 0) return ret;
+ return TEMP_FAILURE_RETRY(open(kmsg_device, O_WRONLY | O_CLOEXEC));
+}
+
+#define LOG_BUF_MAX 512
+
+void klog_writev(int level, const struct iovec* iov, int iov_count) {
+ if (level > klog_level) return;
+
+ static int klog_fd = __open_klog();
+ if (klog_fd == -1) return;
+ TEMP_FAILURE_RETRY(writev(klog_fd, iov, iov_count));
+}
+
+void klog_write(int level, const char* fmt, ...) {
+ if (level > klog_level) return;
+
+ char buf[LOG_BUF_MAX];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ buf[LOG_BUF_MAX - 1] = 0;
+
+ struct iovec iov[1];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = strlen(buf);
+ klog_writev(level, iov, 1);
+}
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 7c74bb8..08d4d6c 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -15,15 +15,44 @@
*/
#include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
userid_t multiuser_get_user_id(uid_t uid) {
- return uid / MULTIUSER_APP_PER_USER_RANGE;
+ return uid / AID_USER_OFFSET;
}
appid_t multiuser_get_app_id(uid_t uid) {
- return uid % MULTIUSER_APP_PER_USER_RANGE;
+ return uid % AID_USER_OFFSET;
}
-uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
- return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
+ return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
+}
+
+gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
+ } else {
+ return -1;
+ }
+}
+
+gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
+ } else {
+ return -1;
+ }
+}
+
+gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
+ } else {
+ return -1;
+ }
+}
+
+gid_t multiuser_get_shared_app_gid(uid_t uid) {
+ return multiuser_get_shared_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid));
}
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c
index 9a4a5bb..9f4840a 100644
--- a/libcutils/native_handle.c
+++ b/libcutils/native_handle.c
@@ -16,20 +16,40 @@
#define LOG_TAG "NativeHandle"
-#include <stdint.h>
#include <errno.h>
-#include <string.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
-#include <cutils/log.h>
+#include <android/log.h>
#include <cutils/native_handle.h>
+static const int kMaxNativeFds = 1024;
+static const int kMaxNativeInts = 1024;
+
+native_handle_t* native_handle_init(char* storage, int numFds, int numInts)
+{
+ if ((uintptr_t) storage % alignof(native_handle_t)) {
+ return NULL;
+ }
+
+ native_handle_t* handle = (native_handle_t*) storage;
+ handle->version = sizeof(native_handle_t);
+ handle->numFds = numFds;
+ handle->numInts = numInts;
+
+ return handle;
+}
+
native_handle_t* native_handle_create(int numFds, int numInts)
{
- native_handle_t* h = malloc(
- sizeof(native_handle_t) + sizeof(int)*(numFds+numInts));
+ if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+ return NULL;
+ }
+ size_t mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts));
+ native_handle_t* h = malloc(mallocSize);
if (h) {
h->version = sizeof(native_handle_t);
h->numFds = numFds;
@@ -38,6 +58,27 @@
return h;
}
+native_handle_t* native_handle_clone(const native_handle_t* handle)
+{
+ native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
+ int i;
+
+ for (i = 0; i < handle->numFds; i++) {
+ clone->data[i] = dup(handle->data[i]);
+ if (clone->data[i] < 0) {
+ clone->numFds = i;
+ native_handle_close(clone);
+ native_handle_delete(clone);
+ return NULL;
+ }
+ }
+
+ memcpy(&clone->data[handle->numFds], &handle->data[handle->numFds],
+ sizeof(int) * handle->numInts);
+
+ return clone;
+}
+
int native_handle_delete(native_handle_t* h)
{
if (h) {
diff --git a/libcutils/process_name.c b/libcutils/process_name.c
deleted file mode 100644
index 5d28b6f..0000000
--- a/libcutils/process_name.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#if defined(__linux__)
-#include <sys/prctl.h>
-#endif
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/process_name.h>
-#if defined(__ANDROID__)
-#include <cutils/properties.h>
-#endif
-
-#define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name"
-
-static const char* process_name = "unknown";
-#if defined(__ANDROID__)
-static int running_in_emulator = -1;
-#endif
-
-void set_process_name(const char* new_name) {
-#if defined(__ANDROID__)
- char propBuf[PROPERTY_VALUE_MAX];
-#endif
-
- if (new_name == NULL) {
- return;
- }
-
- // We never free the old name. Someone else could be using it.
- int len = strlen(new_name);
- char* copy = (char*) malloc(len + 1);
- strcpy(copy, new_name);
- process_name = (const char*) copy;
-
-#if defined(__linux__)
- if (len < 16) {
- prctl(PR_SET_NAME, (unsigned long) new_name, 0, 0, 0);
- } else {
- prctl(PR_SET_NAME, (unsigned long) new_name + len - 15, 0, 0, 0);
- }
-#endif
-
-#if defined(__ANDROID__)
- // If we know we are not running in the emulator, then return.
- if (running_in_emulator == 0) {
- return;
- }
-
- // If the "running_in_emulator" variable has not been initialized,
- // then do it now.
- if (running_in_emulator == -1) {
- property_get("ro.kernel.qemu", propBuf, "");
- if (propBuf[0] == '1') {
- running_in_emulator = 1;
- } else {
- running_in_emulator = 0;
- return;
- }
- }
-
- // If the emulator was started with the "-trace file" command line option
- // then we want to record the process name in the trace even if we are
- // not currently tracing instructions (so that we will know the process
- // name when we do start tracing instructions). We do not need to execute
- // this code if we are just running in the emulator without the "-trace"
- // command line option, but we don't know that here and this function
- // isn't called frequently enough to bother optimizing that case.
- int fd = open(PROCESS_NAME_DEVICE, O_RDWR);
- if (fd < 0)
- return;
- write(fd, process_name, strlen(process_name) + 1);
- close(fd);
-#endif
-}
-
-const char* get_process_name(void) {
- return process_name;
-}
diff --git a/libcutils/properties.c b/libcutils/properties.c
deleted file mode 100644
index 4e46e02..0000000
--- a/libcutils/properties.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "properties"
-// #define LOG_NDEBUG 0
-
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <cutils/sockets.h>
-#include <errno.h>
-#include <assert.h>
-
-#include <cutils/properties.h>
-#include <stdbool.h>
-#include <inttypes.h>
-#include <log/log.h>
-
-int8_t property_get_bool(const char *key, int8_t default_value) {
- if (!key) {
- return default_value;
- }
-
- int8_t result = default_value;
- char buf[PROPERTY_VALUE_MAX] = {'\0',};
-
- int len = property_get(key, buf, "");
- if (len == 1) {
- char ch = buf[0];
- if (ch == '0' || ch == 'n') {
- result = false;
- } else if (ch == '1' || ch == 'y') {
- result = true;
- }
- } else if (len > 1) {
- if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
- result = false;
- } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
- result = true;
- }
- }
-
- return result;
-}
-
-// Convert string property to int (default if fails); return default value if out of bounds
-static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
- intmax_t default_value) {
- if (!key) {
- return default_value;
- }
-
- intmax_t result = default_value;
- char buf[PROPERTY_VALUE_MAX] = {'\0',};
- char *end = NULL;
-
- int len = property_get(key, buf, "");
- if (len > 0) {
- int tmp = errno;
- errno = 0;
-
- // Infer base automatically
- result = strtoimax(buf, &end, /*base*/0);
- if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) {
- // Over or underflow
- result = default_value;
- ALOGV("%s(%s,%" PRIdMAX ") - overflow", __FUNCTION__, key, default_value);
- } else if (result < lower_bound || result > upper_bound) {
- // Out of range of requested bounds
- result = default_value;
- ALOGV("%s(%s,%" PRIdMAX ") - out of range", __FUNCTION__, key, default_value);
- } else if (end == buf) {
- // Numeric conversion failed
- result = default_value;
- ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed",
- __FUNCTION__, key, default_value);
- }
-
- errno = tmp;
- }
-
- return result;
-}
-
-int64_t property_get_int64(const char *key, int64_t default_value) {
- return (int64_t)property_get_imax(key, INT64_MIN, INT64_MAX, default_value);
-}
-
-int32_t property_get_int32(const char *key, int32_t default_value) {
- return (int32_t)property_get_imax(key, INT32_MIN, INT32_MAX, default_value);
-}
-
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
-int property_set(const char *key, const char *value)
-{
- return __system_property_set(key, value);
-}
-
-int property_get(const char *key, char *value, const char *default_value)
-{
- int len;
-
- len = __system_property_get(key, value);
- if(len > 0) {
- return len;
- }
- if(default_value) {
- len = strlen(default_value);
- if (len >= PROPERTY_VALUE_MAX) {
- len = PROPERTY_VALUE_MAX - 1;
- }
- memcpy(value, default_value, len);
- value[len] = '\0';
- }
- return len;
-}
-
-struct property_list_callback_data
-{
- void (*propfn)(const char *key, const char *value, void *cookie);
- void *cookie;
-};
-
-static void property_list_callback(const prop_info *pi, void *cookie)
-{
- char name[PROP_NAME_MAX];
- char value[PROP_VALUE_MAX];
- struct property_list_callback_data *data = cookie;
-
- __system_property_read(pi, name, value);
- data->propfn(name, value, data->cookie);
-}
-
-int property_list(
- void (*propfn)(const char *key, const char *value, void *cookie),
- void *cookie)
-{
- struct property_list_callback_data data = { propfn, cookie };
- return __system_property_foreach(property_list_callback, &data);
-}
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
new file mode 100644
index 0000000..43ad574
--- /dev/null
+++ b/libcutils/properties.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "properties"
+// #define LOG_NDEBUG 0
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <log/log.h>
+
+int8_t property_get_bool(const char *key, int8_t default_value) {
+ if (!key) {
+ return default_value;
+ }
+
+ int8_t result = default_value;
+ char buf[PROPERTY_VALUE_MAX] = {'\0'};
+
+ int len = property_get(key, buf, "");
+ if (len == 1) {
+ char ch = buf[0];
+ if (ch == '0' || ch == 'n') {
+ result = false;
+ } else if (ch == '1' || ch == 'y') {
+ result = true;
+ }
+ } else if (len > 1) {
+ if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
+ result = false;
+ } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+// Convert string property to int (default if fails); return default value if out of bounds
+static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
+ intmax_t default_value) {
+ if (!key) {
+ return default_value;
+ }
+
+ intmax_t result = default_value;
+ char buf[PROPERTY_VALUE_MAX] = {'\0'};
+ char *end = NULL;
+
+ int len = property_get(key, buf, "");
+ if (len > 0) {
+ int tmp = errno;
+ errno = 0;
+
+ // Infer base automatically
+ result = strtoimax(buf, &end, /*base*/ 0);
+ if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) {
+ // Over or underflow
+ result = default_value;
+ ALOGV("%s(%s,%" PRIdMAX ") - overflow", __FUNCTION__, key, default_value);
+ } else if (result < lower_bound || result > upper_bound) {
+ // Out of range of requested bounds
+ result = default_value;
+ ALOGV("%s(%s,%" PRIdMAX ") - out of range", __FUNCTION__, key, default_value);
+ } else if (end == buf) {
+ // Numeric conversion failed
+ result = default_value;
+ ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed", __FUNCTION__, key,
+ default_value);
+ }
+
+ errno = tmp;
+ }
+
+ return result;
+}
+
+int64_t property_get_int64(const char *key, int64_t default_value) {
+ return (int64_t)property_get_imax(key, INT64_MIN, INT64_MAX, default_value);
+}
+
+int32_t property_get_int32(const char *key, int32_t default_value) {
+ return (int32_t)property_get_imax(key, INT32_MIN, INT32_MAX, default_value);
+}
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+int property_set(const char *key, const char *value) {
+ return __system_property_set(key, value);
+}
+
+int property_get(const char *key, char *value, const char *default_value) {
+ int len = __system_property_get(key, value);
+ if (len > 0) {
+ return len;
+ }
+ if (default_value) {
+ len = strnlen(default_value, PROPERTY_VALUE_MAX - 1);
+ memcpy(value, default_value, len);
+ value[len] = '\0';
+ }
+ return len;
+}
+
+struct callback_data {
+ void (*callback)(const char* name, const char* value, void* cookie);
+ void* cookie;
+};
+
+static void trampoline(void* raw_data, const char* name, const char* value) {
+ callback_data* data = reinterpret_cast<callback_data*>(raw_data);
+ data->callback(name, value, data->cookie);
+}
+
+static void property_list_callback(const prop_info* pi, void* data) {
+ __system_property_read_callback(pi, trampoline, data);
+}
+
+int property_list(void (*fn)(const char* name, const char* value, void* cookie), void* cookie) {
+ callback_data data = { fn, cookie };
+ return __system_property_foreach(property_list_callback, &data);
+}
diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c
index 00e211c..22b8325 100644
--- a/libcutils/qtaguid.c
+++ b/libcutils/qtaguid.c
@@ -26,8 +26,8 @@
#include <string.h>
#include <unistd.h>
-#include <cutils/qtaguid.h>
#include <log/log.h>
+#include <cutils/qtaguid.h>
static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
static const int CTRL_MAX_INPUT_LEN = 128;
@@ -47,10 +47,7 @@
/* Only call once per process. */
void qtaguid_resTrack(void) {
- resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY));
- if (resTrackFd >=0) {
- TEMP_FAILURE_RETRY(fcntl(resTrackFd, F_SETFD, FD_CLOEXEC));
- }
+ resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY | O_CLOEXEC));
}
/*
@@ -63,7 +60,7 @@
ALOGV("write_ctrl(%s)", cmd);
- fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY));
+ fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY | O_CLOEXEC));
if (fd < 0) {
return -errno;
}
@@ -75,7 +72,8 @@
savedErrno = 0;
}
if (res < 0) {
- ALOGI("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno);
+ // ALOGV is enough because all the callers also log failures
+ ALOGV("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno);
}
close(fd);
return -savedErrno;
@@ -85,7 +83,7 @@
int param_fd;
int res;
- param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY));
+ param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY | O_CLOEXEC));
if (param_fd < 0) {
return -errno;
}
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index 103ff66..5c5f3a5 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -1,16 +1,16 @@
/*
** Copyright 2007, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -23,8 +23,8 @@
#include <string.h>
#include <unistd.h>
-#include <cutils/sched_policy.h>
#include <log/log.h>
+#include <cutils/sched_policy.h>
#define UNUSED __attribute__((__unused__))
@@ -45,41 +45,38 @@
#define POLICY_DEBUG 0
-// This prctl is only available in Android kernels.
-#define PR_SET_TIMERSLACK_PID 41
-
// timer slack value in nS enforced when the thread moves to background
#define TIMER_SLACK_BG 40000000
+#define TIMER_SLACK_FG 50000
static pthread_once_t the_once = PTHREAD_ONCE_INIT;
static int __sys_supports_schedgroups = -1;
+static int __sys_supports_timerslack = -1;
// File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
static int bg_cgroup_fd = -1;
static int fg_cgroup_fd = -1;
+#ifdef USE_CPUSETS
+// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
+static int system_bg_cpuset_fd = -1;
+static int bg_cpuset_fd = -1;
+static int fg_cpuset_fd = -1;
+static int ta_cpuset_fd = -1; // special cpuset for top app
+#endif
+
+// File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error
+static int bg_schedboost_fd = -1;
+static int fg_schedboost_fd = -1;
+static int ta_schedboost_fd = -1;
+
/* Add tid to the scheduling group defined by the policy */
-static int add_tid_to_cgroup(int tid, SchedPolicy policy)
+static int add_tid_to_cgroup(int tid, int fd)
{
- int fd;
-
- switch (policy) {
- case SP_BACKGROUND:
- fd = bg_cgroup_fd;
- break;
- case SP_FOREGROUND:
- case SP_AUDIO_APP:
- case SP_AUDIO_SYS:
- fd = fg_cgroup_fd;
- break;
- default:
- fd = -1;
- break;
- }
-
if (fd < 0) {
- SLOGE("add_tid_to_cgroup failed; policy=%d\n", policy);
+ SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd);
+ errno = EINVAL;
return -1;
}
@@ -100,8 +97,9 @@
*/
if (errno == ESRCH)
return 0;
- SLOGW("add_tid_to_cgroup failed to write '%s' (%s); policy=%d\n",
- ptr, strerror(errno), policy);
+ SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n",
+ ptr, strerror(errno), fd);
+ errno = EINVAL;
return -1;
}
@@ -110,7 +108,7 @@
static void __initialize(void) {
char* filename;
- if (!access("/dev/cpuctl/tasks", F_OK)) {
+ if (!access("/dev/cpuctl/tasks", W_OK)) {
__sys_supports_schedgroups = 1;
filename = "/dev/cpuctl/tasks";
@@ -127,10 +125,37 @@
} else {
__sys_supports_schedgroups = 0;
}
+
+#ifdef USE_CPUSETS
+ if (!access("/dev/cpuset/tasks", W_OK)) {
+
+ filename = "/dev/cpuset/foreground/tasks";
+ fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/cpuset/background/tasks";
+ bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/cpuset/system-background/tasks";
+ system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/cpuset/top-app/tasks";
+ ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+
+#ifdef USE_SCHEDBOOST
+ filename = "/dev/stune/top-app/tasks";
+ ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/stune/foreground/tasks";
+ fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/stune/background/tasks";
+ bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+#endif
+ }
+#endif
+
+ char buf[64];
+ snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", getpid());
+ __sys_supports_timerslack = !access(buf, W_OK);
}
/*
- * Try to get the scheduler group.
+ * Returns the path under the requested cgroup subsystem (if it exists)
*
* The data from /proc/<pid>/cgroup looks (something) like:
* 2:cpu:/bg_non_interactive
@@ -140,7 +165,7 @@
* the default cgroup. If the string is longer than "bufLen", the string
* will be truncated.
*/
-static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
+static int getCGroupSubsys(int tid, const char* subsys, char* buf, size_t bufLen)
{
#if defined(__ANDROID__)
char pathBuf[32];
@@ -148,13 +173,13 @@
FILE *fp;
snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
- if (!(fp = fopen(pathBuf, "r"))) {
+ if (!(fp = fopen(pathBuf, "re"))) {
return -1;
}
while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
char *next = lineBuf;
- char *subsys;
+ char *found_subsys;
char *grp;
size_t len;
@@ -163,11 +188,11 @@
goto out_bad_data;
}
- if (!(subsys = strsep(&next, ":"))) {
+ if (!(found_subsys = strsep(&next, ":"))) {
goto out_bad_data;
}
- if (strcmp(subsys, "cpu")) {
+ if (strcmp(found_subsys, subsys)) {
/* Not the subsys we're looking for */
continue;
}
@@ -188,7 +213,7 @@
return 0;
}
- SLOGE("Failed to find cpu subsys");
+ SLOGE("Failed to find subsys %s", subsys);
fclose(fp);
return -1;
out_bad_data:
@@ -210,7 +235,23 @@
if (__sys_supports_schedgroups) {
char grpBuf[32];
- if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0)
+#ifdef USE_CPUSETS
+ if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0)
+ return -1;
+ if (grpBuf[0] == '\0') {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "foreground")) {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "background")) {
+ *policy = SP_BACKGROUND;
+ } else if (!strcmp(grpBuf, "top-app")) {
+ *policy = SP_TOP_APP;
+ } else {
+ errno = ERANGE;
+ return -1;
+ }
+#else
+ if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
return -1;
if (grpBuf[0] == '\0') {
*policy = SP_FOREGROUND;
@@ -220,6 +261,7 @@
errno = ERANGE;
return -1;
}
+#endif
} else {
int rc = sched_getscheduler(tid);
if (rc < 0)
@@ -236,6 +278,75 @@
return 0;
}
+int set_cpuset_policy(int tid, SchedPolicy policy)
+{
+ // in the absence of cpusets, use the old sched policy
+#ifndef USE_CPUSETS
+ return set_sched_policy(tid, policy);
+#else
+ if (tid == 0) {
+ tid = gettid();
+ }
+ policy = _policy(policy);
+ pthread_once(&the_once, __initialize);
+
+ int fd = -1;
+ int boost_fd = -1;
+ switch (policy) {
+ case SP_BACKGROUND:
+ fd = bg_cpuset_fd;
+ boost_fd = bg_schedboost_fd;
+ break;
+ case SP_FOREGROUND:
+ case SP_AUDIO_APP:
+ case SP_AUDIO_SYS:
+ fd = fg_cpuset_fd;
+ boost_fd = fg_schedboost_fd;
+ break;
+ case SP_TOP_APP :
+ fd = ta_cpuset_fd;
+ boost_fd = ta_schedboost_fd;
+ break;
+ case SP_SYSTEM:
+ fd = system_bg_cpuset_fd;
+ break;
+ default:
+ boost_fd = fd = -1;
+ break;
+ }
+
+ if (add_tid_to_cgroup(tid, fd) != 0) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
+
+#ifdef USE_SCHEDBOOST
+ if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
+#endif
+
+ return 0;
+#endif
+}
+
+static void set_timerslack_ns(int tid, unsigned long long slack) {
+ // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
+ // TODO: once we've backported this, log if the open(2) fails.
+ char buf[64];
+ snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
+ int fd = open(buf, O_WRONLY | O_CLOEXEC);
+ if (fd != -1) {
+ int len = snprintf(buf, sizeof(buf), "%llu", slack);
+ if (write(fd, buf, len) != len) {
+ SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
+ }
+ close(fd);
+ return;
+ }
+}
+
int set_sched_policy(int tid, SchedPolicy policy)
{
if (tid == 0) {
@@ -248,12 +359,11 @@
char statfile[64];
char statline[1024];
char thread_name[255];
- int fd;
- sprintf(statfile, "/proc/%d/stat", tid);
+ snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
memset(thread_name, 0, sizeof(thread_name));
- fd = open(statfile, O_RDONLY);
+ int fd = open(statfile, O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
int rc = read(fd, statline, 1023);
close(fd);
@@ -274,6 +384,7 @@
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
+ case SP_TOP_APP:
SLOGD("^^^ tid %d (%s)", tid, thread_name);
break;
case SP_SYSTEM:
@@ -286,21 +397,55 @@
#endif
if (__sys_supports_schedgroups) {
- if (add_tid_to_cgroup(tid, policy)) {
+ int fd = -1;
+ int boost_fd = -1;
+ switch (policy) {
+ case SP_BACKGROUND:
+ fd = bg_cgroup_fd;
+ boost_fd = bg_schedboost_fd;
+ break;
+ case SP_FOREGROUND:
+ case SP_AUDIO_APP:
+ case SP_AUDIO_SYS:
+ fd = fg_cgroup_fd;
+ boost_fd = fg_schedboost_fd;
+ break;
+ case SP_TOP_APP:
+ fd = fg_cgroup_fd;
+ boost_fd = ta_schedboost_fd;
+ break;
+ default:
+ fd = -1;
+ boost_fd = -1;
+ break;
+ }
+
+
+ if (add_tid_to_cgroup(tid, fd) != 0) {
if (errno != ESRCH && errno != ENOENT)
return -errno;
}
+
+#ifdef USE_SCHEDBOOST
+ if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
+#endif
} else {
struct sched_param param;
param.sched_priority = 0;
sched_setscheduler(tid,
(policy == SP_BACKGROUND) ?
- SCHED_BATCH : SCHED_NORMAL,
+ SCHED_BATCH : SCHED_NORMAL,
¶m);
}
- prctl(PR_SET_TIMERSLACK_PID, policy == SP_BACKGROUND ? TIMER_SLACK_BG : 0, tid);
+ if (__sys_supports_timerslack) {
+ set_timerslack_ns(tid, policy == SP_BACKGROUND ?
+ TIMER_SLACK_BG : TIMER_SLACK_FG);
+ }
return 0;
}
@@ -331,10 +476,10 @@
[SP_SYSTEM] = " ",
[SP_AUDIO_APP] = "aa",
[SP_AUDIO_SYS] = "as",
+ [SP_TOP_APP] = "ta",
};
if ((policy < SP_CNT) && (strings[policy] != NULL))
return strings[policy];
else
return "error";
}
-
diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c
deleted file mode 100644
index 7f0ccb8..0000000
--- a/libcutils/socket_inaddr_any_server.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#endif
-
-#include <cutils/sockets.h>
-
-#define LISTEN_BACKLOG 4
-
-/* open listen() port on any interface */
-int socket_inaddr_any_server(int port, int type)
-{
- struct sockaddr_in addr;
- int s, n;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- s = socket(AF_INET, type, 0);
- if(s < 0) return -1;
-
- n = 1;
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
-
- if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(s);
- return -1;
- }
-
- if (type == SOCK_STREAM) {
- int ret;
-
- ret = listen(s, LISTEN_BACKLOG);
-
- if (ret < 0) {
- close(s);
- return -1;
- }
- }
-
- return s;
-}
diff --git a/libcutils/socket_inaddr_any_server_unix.c b/libcutils/socket_inaddr_any_server_unix.c
new file mode 100644
index 0000000..387258f
--- /dev/null
+++ b/libcutils/socket_inaddr_any_server_unix.c
@@ -0,0 +1,66 @@
+/*
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <cutils/sockets.h>
+
+#define LISTEN_BACKLOG 4
+
+/* open listen() port on any interface */
+int socket_inaddr_any_server(int port, int type)
+{
+ struct sockaddr_in6 addr;
+ int s, n;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_any;
+
+ s = socket(AF_INET6, type, 0);
+ if (s < 0) return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
+
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
diff --git a/libcutils/socket_inaddr_any_server_windows.c b/libcutils/socket_inaddr_any_server_windows.c
new file mode 100644
index 0000000..c15200a
--- /dev/null
+++ b/libcutils/socket_inaddr_any_server_windows.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 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 <errno.h>
+
+#include <cutils/sockets.h>
+
+#define LISTEN_BACKLOG 4
+
+extern bool initialize_windows_sockets();
+
+SOCKET socket_inaddr_any_server(int port, int type) {
+ if (!initialize_windows_sockets()) {
+ return INVALID_SOCKET;
+ }
+
+ SOCKET sock = socket(AF_INET6, type, 0);
+ if (sock == INVALID_SOCKET) {
+ return INVALID_SOCKET;
+ }
+
+ // Enforce exclusive addresses so nobody can steal the port from us (1),
+ // and enable dual-stack so both IPv4 and IPv6 work (2).
+ // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx.
+ // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx.
+ int exclusive = 1;
+ DWORD v6_only = 0;
+ if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&exclusive,
+ sizeof(exclusive)) == SOCKET_ERROR ||
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6_only,
+ sizeof(v6_only)) == SOCKET_ERROR) {
+ closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+ // Bind the socket to our local port.
+ struct sockaddr_in6 addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_any;
+ if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
+ closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+ // Start listening for connections if this is a TCP socket.
+ if (type == SOCK_STREAM && listen(sock, LISTEN_BACKLOG) == SOCKET_ERROR) {
+ closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+ return sock;
+}
diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c
deleted file mode 100644
index 2526146..0000000
--- a/libcutils/socket_local_client.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <cutils/sockets.h>
-
-#if defined(_WIN32)
-
-int socket_local_client(const char *name, int namespaceId, int type)
-{
- errno = ENOSYS;
- return -1;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-
-#include "socket_local.h"
-
-#define UNUSED __attribute__((unused))
-
-#define LISTEN_BACKLOG 4
-
-/* Documented in header file. */
-int socket_make_sockaddr_un(const char *name, int namespaceId,
- struct sockaddr_un *p_addr, socklen_t *alen)
-{
- memset (p_addr, 0, sizeof (*p_addr));
- size_t namelen;
-
- switch (namespaceId) {
- case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
-#if defined(__linux__)
- namelen = strlen(name);
-
- // Test with length +1 for the *initial* '\0'.
- if ((namelen + 1) > sizeof(p_addr->sun_path)) {
- goto error;
- }
-
- /*
- * Note: The path in this case is *not* supposed to be
- * '\0'-terminated. ("man 7 unix" for the gory details.)
- */
-
- p_addr->sun_path[0] = 0;
- memcpy(p_addr->sun_path + 1, name, namelen);
-#else
- /* this OS doesn't have the Linux abstract namespace */
-
- namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr)
- - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
- strcat(p_addr->sun_path, name);
-#endif
- break;
-
- case ANDROID_SOCKET_NAMESPACE_RESERVED:
- namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr)
- - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
- strcat(p_addr->sun_path, name);
- break;
-
- case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
- namelen = strlen(name);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr)
- - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, name);
- break;
- default:
- // invalid namespace id
- return -1;
- }
-
- p_addr->sun_family = AF_LOCAL;
- *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
- return 0;
-error:
- return -1;
-}
-
-/**
- * connect to peer named "name" on fd
- * returns same fd or -1 on error.
- * fd is not closed on error. that's your job.
- *
- * Used by AndroidSocketImpl
- */
-int socket_local_client_connect(int fd, const char *name, int namespaceId,
- int type UNUSED)
-{
- struct sockaddr_un addr;
- socklen_t alen;
- int err;
-
- err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
-
- if (err < 0) {
- goto error;
- }
-
- if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
- goto error;
- }
-
- return fd;
-
-error:
- return -1;
-}
-
-/**
- * connect to peer named "name"
- * returns fd or -1 on error
- */
-int socket_local_client(const char *name, int namespaceId, int type)
-{
- int s;
-
- s = socket(AF_LOCAL, type, 0);
- if(s < 0) return -1;
-
- if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
- close(s);
- return -1;
- }
-
- return s;
-}
-
-#endif /* !_WIN32 */
diff --git a/libcutils/socket_local_client_unix.c b/libcutils/socket_local_client_unix.c
new file mode 100644
index 0000000..92fb9f1
--- /dev/null
+++ b/libcutils/socket_local_client_unix.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+
+#if defined(_WIN32)
+
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !_WIN32 */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+#include "socket_local_unix.h"
+
+#define UNUSED __attribute__((unused))
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen)
+{
+ memset (p_addr, 0, sizeof (*p_addr));
+ size_t namelen;
+
+ switch (namespaceId) {
+ case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#if defined(__linux__)
+ namelen = strlen(name);
+
+ // Test with length +1 for the *initial* '\0'.
+ if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+ goto error;
+ }
+
+ /*
+ * Note: The path in this case is *not* supposed to be
+ * '\0'-terminated. ("man 7 unix" for the gory details.)
+ */
+
+ p_addr->sun_path[0] = 0;
+ memcpy(p_addr->sun_path + 1, name, namelen);
+#else
+ /* this OS doesn't have the Linux abstract namespace */
+
+ namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+#endif
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_RESERVED:
+ namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+ namelen = strlen(name);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, name);
+ break;
+ default:
+ // invalid namespace id
+ return -1;
+ }
+
+ p_addr->sun_family = AF_LOCAL;
+ *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ *
+ * Used by AndroidSocketImpl
+ */
+int socket_local_client_connect(int fd, const char *name, int namespaceId,
+ int type UNUSED)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ goto error;
+ }
+
+ if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+ goto error;
+ }
+
+ return fd;
+
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if(s < 0) return -1;
+
+ if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+#endif /* !_WIN32 */
diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c
deleted file mode 100644
index c9acdad..0000000
--- a/libcutils/socket_local_server.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* libs/cutils/socket_local_server.c
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <cutils/sockets.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stddef.h>
-
-#if defined(_WIN32)
-
-int socket_local_server(const char *name, int namespaceId, int type)
-{
- errno = ENOSYS;
- return -1;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-
-#include "socket_local.h"
-
-#define LISTEN_BACKLOG 4
-
-/* Only the bottom bits are really the socket type; there are flags too. */
-#define SOCK_TYPE_MASK 0xf
-
-/**
- * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
- * returns 's' on success, -1 on fail
- *
- * Does not call listen()
- */
-int socket_local_server_bind(int s, const char *name, int namespaceId)
-{
- struct sockaddr_un addr;
- socklen_t alen;
- int n;
- int err;
-
- err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
-
- if (err < 0) {
- return -1;
- }
-
- /* basically: if this is a filesystem path, unlink first */
-#if !defined(__linux__)
- if (1) {
-#else
- if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
- || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
-#endif
- /*ignore ENOENT*/
- unlink(addr.sun_path);
- }
-
- n = 1;
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
-
- if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
- return -1;
- }
-
- return s;
-
-}
-
-
-/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem
- * namespace
- *
- * Returns fd on success, -1 on fail
- */
-
-int socket_local_server(const char *name, int namespace, int type)
-{
- int err;
- int s;
-
- s = socket(AF_LOCAL, type, 0);
- if (s < 0) return -1;
-
- err = socket_local_server_bind(s, name, namespace);
-
- if (err < 0) {
- close(s);
- return -1;
- }
-
- if ((type & SOCK_TYPE_MASK) == SOCK_STREAM) {
- int ret;
-
- ret = listen(s, LISTEN_BACKLOG);
-
- if (ret < 0) {
- close(s);
- return -1;
- }
- }
-
- return s;
-}
-
-#endif /* !_WIN32 */
diff --git a/libcutils/socket_local_server_unix.c b/libcutils/socket_local_server_unix.c
new file mode 100644
index 0000000..db9e1e0
--- /dev/null
+++ b/libcutils/socket_local_server_unix.c
@@ -0,0 +1,126 @@
+/* libs/cutils/socket_local_server.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#if defined(_WIN32)
+
+int socket_local_server(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !_WIN32 */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "socket_local_unix.h"
+
+#define LISTEN_BACKLOG 4
+
+/* Only the bottom bits are really the socket type; there are flags too. */
+#define SOCK_TYPE_MASK 0xf
+
+/**
+ * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
+ * returns 's' on success, -1 on fail
+ *
+ * Does not call listen()
+ */
+int socket_local_server_bind(int s, const char *name, int namespaceId)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int n;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ return -1;
+ }
+
+ /* basically: if this is a filesystem path, unlink first */
+#if !defined(__linux__)
+ if (1) {
+#else
+ if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
+ || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
+#endif
+ /*ignore ENOENT*/
+ unlink(addr.sun_path);
+ }
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
+ return -1;
+ }
+
+ return s;
+
+}
+
+
+/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem
+ * namespace
+ *
+ * Returns fd on success, -1 on fail
+ */
+
+int socket_local_server(const char *name, int namespace, int type)
+{
+ int err;
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if (s < 0) return -1;
+
+ err = socket_local_server_bind(s, name, namespace);
+
+ if (err < 0) {
+ close(s);
+ return -1;
+ }
+
+ if ((type & SOCK_TYPE_MASK) == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
+#endif /* !_WIN32 */
diff --git a/libcutils/socket_local.h b/libcutils/socket_local_unix.h
similarity index 100%
rename from libcutils/socket_local.h
rename to libcutils/socket_local_unix.h
diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c
deleted file mode 100644
index e14cffb..0000000
--- a/libcutils/socket_loopback_client.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#endif
-
-#include <cutils/sockets.h>
-
-/* Connect to port on the loopback IP interface. type is
- * SOCK_STREAM or SOCK_DGRAM.
- * return is a file descriptor or -1 on error
- */
-int socket_loopback_client(int port, int type)
-{
- struct sockaddr_in addr;
- int s;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- s = socket(AF_INET, type, 0);
- if(s < 0) return -1;
-
- if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(s);
- return -1;
- }
-
- return s;
-
-}
-
diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c
deleted file mode 100644
index b600e34..0000000
--- a/libcutils/socket_loopback_server.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define LISTEN_BACKLOG 4
-
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#endif
-
-#include <cutils/sockets.h>
-
-/* open listen() port on loopback interface */
-int socket_loopback_server(int port, int type)
-{
- struct sockaddr_in addr;
- int s, n;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- s = socket(AF_INET, type, 0);
- if(s < 0) return -1;
-
- n = 1;
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
-
- if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(s);
- return -1;
- }
-
- if (type == SOCK_STREAM) {
- int ret;
-
- ret = listen(s, LISTEN_BACKLOG);
-
- if (ret < 0) {
- close(s);
- return -1;
- }
- }
-
- return s;
-}
-
diff --git a/libcutils/socket_loopback_server_unix.c b/libcutils/socket_loopback_server_unix.c
new file mode 100644
index 0000000..7b92fd6
--- /dev/null
+++ b/libcutils/socket_loopback_server_unix.c
@@ -0,0 +1,88 @@
+/*
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LISTEN_BACKLOG 4
+
+#if !defined(_WIN32)
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+#include <cutils/sockets.h>
+
+static int _socket_loopback_server(int family, int type, struct sockaddr * addr, size_t size)
+{
+ int s, n;
+
+ s = socket(family, type, 0);
+ if(s < 0)
+ return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
+
+ if(bind(s, addr, size) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
+/* open listen() port on loopback IPv6 interface */
+int socket_loopback_server6(int port, int type)
+{
+ struct sockaddr_in6 addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_loopback;
+
+ return _socket_loopback_server(AF_INET6, type, (struct sockaddr *) &addr, sizeof(addr));
+}
+
+/* open listen() port on loopback interface */
+int socket_loopback_server(int port, int type)
+{
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ return _socket_loopback_server(AF_INET, type, (struct sockaddr *) &addr, sizeof(addr));
+}
diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c
deleted file mode 100644
index 3300b8f..0000000
--- a/libcutils/socket_network_client.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-#include <cutils/sockets.h>
-
-static int toggle_O_NONBLOCK(int s) {
- int flags = fcntl(s, F_GETFL);
- if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
- close(s);
- return -1;
- }
- return s;
-}
-
-// Connect to the given host and port.
-// 'timeout' is in seconds (0 for no timeout).
-// Returns a file descriptor or -1 on error.
-// On error, check *getaddrinfo_error (for use with gai_strerror) first;
-// if that's 0, use errno instead.
-int socket_network_client_timeout(const char* host, int port, int type, int timeout,
- int* getaddrinfo_error) {
- struct addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = type;
-
- char port_str[16];
- snprintf(port_str, sizeof(port_str), "%d", port);
-
- struct addrinfo* addrs;
- *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs);
- if (*getaddrinfo_error != 0) {
- return -1;
- }
-
- // TODO: try all the addresses if there's more than one?
- int family = addrs[0].ai_family;
- int protocol = addrs[0].ai_protocol;
- socklen_t addr_len = addrs[0].ai_addrlen;
- struct sockaddr_storage addr;
- memcpy(&addr, addrs[0].ai_addr, addr_len);
-
- freeaddrinfo(addrs);
-
- // The Mac doesn't have SOCK_NONBLOCK.
- int s = socket(family, type, protocol);
- if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
-
- int rc = connect(s, (const struct sockaddr*) &addr, addr_len);
- if (rc == 0) {
- return toggle_O_NONBLOCK(s);
- } else if (rc == -1 && errno != EINPROGRESS) {
- close(s);
- return -1;
- }
-
- fd_set r_set;
- FD_ZERO(&r_set);
- FD_SET(s, &r_set);
- fd_set w_set = r_set;
-
- struct timeval ts;
- ts.tv_sec = timeout;
- ts.tv_usec = 0;
- if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
- close(s);
- return -1;
- }
- if (rc == 0) { // we had a timeout
- errno = ETIMEDOUT;
- close(s);
- return -1;
- }
-
- int error = 0;
- socklen_t len = sizeof(error);
- if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
- if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
- close(s);
- return -1;
- }
- } else {
- close(s);
- return -1;
- }
-
- if (error) { // check if we had a socket error
- errno = error;
- close(s);
- return -1;
- }
-
- return toggle_O_NONBLOCK(s);
-}
-
-int socket_network_client(const char* host, int port, int type) {
- int getaddrinfo_error;
- return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error);
-}
diff --git a/libcutils/socket_network_client_unix.c b/libcutils/socket_network_client_unix.c
new file mode 100644
index 0000000..37851b1
--- /dev/null
+++ b/libcutils/socket_network_client_unix.c
@@ -0,0 +1,125 @@
+/*
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <cutils/sockets.h>
+
+static int toggle_O_NONBLOCK(int s) {
+ int flags = fcntl(s, F_GETFL);
+ if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
+ close(s);
+ return -1;
+ }
+ return s;
+}
+
+// Connect to the given host and port.
+// 'timeout' is in seconds (0 for no timeout).
+// Returns a file descriptor or -1 on error.
+// On error, check *getaddrinfo_error (for use with gai_strerror) first;
+// if that's 0, use errno instead.
+int socket_network_client_timeout(const char* host, int port, int type, int timeout,
+ int* getaddrinfo_error) {
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = type;
+
+ char port_str[16];
+ snprintf(port_str, sizeof(port_str), "%d", port);
+
+ struct addrinfo* addrs;
+ *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs);
+ if (*getaddrinfo_error != 0) {
+ return -1;
+ }
+
+ int result = -1;
+ for (struct addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
+ // The Mac doesn't have SOCK_NONBLOCK.
+ int s = socket(addr->ai_family, type, addr->ai_protocol);
+ if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
+
+ int rc = connect(s, addr->ai_addr, addr->ai_addrlen);
+ if (rc == 0) {
+ result = toggle_O_NONBLOCK(s);
+ break;
+ } else if (rc == -1 && errno != EINPROGRESS) {
+ close(s);
+ continue;
+ }
+
+ fd_set r_set;
+ FD_ZERO(&r_set);
+ FD_SET(s, &r_set);
+ fd_set w_set = r_set;
+
+ struct timeval ts;
+ ts.tv_sec = timeout;
+ ts.tv_usec = 0;
+ if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
+ close(s);
+ break;
+ }
+ if (rc == 0) { // we had a timeout
+ errno = ETIMEDOUT;
+ close(s);
+ break;
+ }
+
+ int error = 0;
+ socklen_t len = sizeof(error);
+ if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
+ if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ close(s);
+ break;
+ }
+ } else {
+ close(s);
+ break;
+ }
+
+ if (error) { // check if we had a socket error
+ // TODO: Update the timeout.
+ errno = error;
+ close(s);
+ continue;
+ }
+
+ result = toggle_O_NONBLOCK(s);
+ break;
+ }
+
+ freeaddrinfo(addrs);
+ return result;
+}
+
+int socket_network_client(const char* host, int port, int type) {
+ int getaddrinfo_error;
+ return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error);
+}
diff --git a/libcutils/socket_network_client_windows.c b/libcutils/socket_network_client_windows.c
new file mode 100644
index 0000000..ab1a52f
--- /dev/null
+++ b/libcutils/socket_network_client_windows.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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 <cutils/sockets.h>
+
+extern bool initialize_windows_sockets();
+
+SOCKET socket_network_client(const char* host, int port, int type) {
+ if (!initialize_windows_sockets()) {
+ return INVALID_SOCKET;
+ }
+
+ // First resolve the host and port parameters into a usable network address.
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = type;
+
+ struct addrinfo* address = NULL;
+ char port_str[16];
+ snprintf(port_str, sizeof(port_str), "%d", port);
+ if (getaddrinfo(host, port_str, &hints, &address) != 0 || address == NULL) {
+ if (address != NULL) {
+ freeaddrinfo(address);
+ }
+ return INVALID_SOCKET;
+ }
+
+ // Now create and connect the socket.
+ SOCKET sock = socket(address->ai_family, address->ai_socktype,
+ address->ai_protocol);
+ if (sock == INVALID_SOCKET) {
+ freeaddrinfo(address);
+ return INVALID_SOCKET;
+ }
+
+ if (connect(sock, address->ai_addr, address->ai_addrlen) == SOCKET_ERROR) {
+ closesocket(sock);
+ freeaddrinfo(address);
+ return INVALID_SOCKET;
+ }
+
+ freeaddrinfo(address);
+ return sock;
+}
diff --git a/libcutils/sockets.c b/libcutils/sockets.c
deleted file mode 100644
index d438782..0000000
--- a/libcutils/sockets.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cutils/sockets.h>
-#include <log/log.h>
-
-#if defined(__ANDROID__)
-/* For the socket trust (credentials) check */
-#include <private/android_filesystem_config.h>
-#define __android_unused
-#else
-#define __android_unused __attribute__((__unused__))
-#endif
-
-bool socket_peer_is_trusted(int fd __android_unused)
-{
-#if defined(__ANDROID__)
- struct ucred cr;
- socklen_t len = sizeof(cr);
- int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
-
- if (n != 0) {
- ALOGE("could not get socket credentials: %s\n", strerror(errno));
- return false;
- }
-
- if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
- ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
- return false;
- }
-#endif
-
- return true;
-}
diff --git a/libcutils/sockets.cpp b/libcutils/sockets.cpp
new file mode 100644
index 0000000..23a447b
--- /dev/null
+++ b/libcutils/sockets.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// This file contains socket implementation that can be shared between
+// platforms as long as the correct headers are included.
+
+#include <cutils/sockets.h>
+
+int socket_get_local_port(cutils_socket_t sock) {
+ sockaddr_storage addr;
+ socklen_t addr_size = sizeof(addr);
+
+ if (getsockname(sock, reinterpret_cast<sockaddr*>(&addr), &addr_size) == 0) {
+ // sockaddr_in and sockaddr_in6 always overlap the port field.
+ return ntohs(reinterpret_cast<sockaddr_in*>(&addr)->sin_port);
+ }
+ return -1;
+}
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
new file mode 100644
index 0000000..e91f358
--- /dev/null
+++ b/libcutils/sockets_unix.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "socket-unix"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <cutils/android_get_control_file.h>
+#include <cutils/sockets.h>
+#include <log/log.h>
+
+#include "android_get_control_env.h"
+
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(exp) (exp) // KISS implementation
+#endif
+
+#if defined(__ANDROID__)
+/* For the socket trust (credentials) check */
+#include <private/android_filesystem_config.h>
+#define __android_unused
+#else
+#define __android_unused __attribute__((__unused__))
+#endif
+
+bool socket_peer_is_trusted(int fd __android_unused) {
+#if defined(__ANDROID__)
+ ucred cr;
+ socklen_t len = sizeof(cr);
+ int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+
+ if (n != 0) {
+ ALOGE("could not get socket credentials: %s\n", strerror(errno));
+ return false;
+ }
+
+ if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
+ ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+int socket_close(int sock) {
+ return close(sock);
+}
+
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
+ timeval tv;
+ tv.tv_sec = timeout_ms / 1000;
+ tv.tv_usec = (timeout_ms % 1000) * 1000;
+ return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+}
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+ const cutils_socket_buffer_t* buffers,
+ size_t num_buffers) {
+ if (num_buffers > SOCKET_SEND_BUFFERS_MAX_BUFFERS) {
+ return -1;
+ }
+
+ iovec iovec_buffers[SOCKET_SEND_BUFFERS_MAX_BUFFERS];
+ for (size_t i = 0; i < num_buffers; ++i) {
+ // It's safe to cast away const here; iovec declares non-const
+ // void* because it's used for both send and receive, but since
+ // we're only sending, the data won't be modified.
+ iovec_buffers[i].iov_base = const_cast<void*>(buffers[i].data);
+ iovec_buffers[i].iov_len = buffers[i].length;
+ }
+
+ return writev(sock, iovec_buffers, num_buffers);
+}
+
+int android_get_control_socket(const char* name) {
+ int fd = __android_get_control_from_env(ANDROID_SOCKET_ENV_PREFIX, name);
+
+ if (fd < 0) return fd;
+
+ // Compare to UNIX domain socket name, must match!
+ struct sockaddr_un addr;
+ socklen_t addrlen = sizeof(addr);
+ int ret = TEMP_FAILURE_RETRY(getsockname(fd, (struct sockaddr *)&addr, &addrlen));
+ if (ret < 0) return -1;
+ char *path = NULL;
+ if (asprintf(&path, ANDROID_SOCKET_DIR "/%s", name) < 0) return -1;
+ if (!path) return -1;
+ int cmp = strcmp(addr.sun_path, path);
+ free(path);
+ if (cmp != 0) return -1;
+
+ // It is what we think it is
+ return fd;
+}
diff --git a/libcutils/sockets_windows.cpp b/libcutils/sockets_windows.cpp
new file mode 100644
index 0000000..3064c70
--- /dev/null
+++ b/libcutils/sockets_windows.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 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 <cutils/sockets.h>
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms741549(v=vs.85).aspx
+// claims WSACleanup() should be called before program exit, but general
+// consensus seems to be that it hasn't actually been necessary for a long time,
+// likely since Windows 3.1. Additionally, trying to properly use WSACleanup()
+// can be extremely tricky and cause deadlock when using threads or atexit().
+//
+// Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.
+// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp
+// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc
+extern "C" bool initialize_windows_sockets() {
+ // There's no harm in calling WSAStartup() multiple times but no benefit
+ // either, we may as well skip it after the first.
+ static bool init_success = false;
+
+ if (!init_success) {
+ WSADATA wsaData;
+ init_success = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0);
+ }
+
+ return init_success;
+}
+
+int socket_close(cutils_socket_t sock) {
+ return closesocket(sock);
+}
+
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
+ return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
+ reinterpret_cast<char*>(&timeout_ms), sizeof(timeout_ms));
+}
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+ const cutils_socket_buffer_t* buffers,
+ size_t num_buffers) {
+ if (num_buffers > SOCKET_SEND_BUFFERS_MAX_BUFFERS) {
+ return -1;
+ }
+
+ WSABUF wsa_buffers[SOCKET_SEND_BUFFERS_MAX_BUFFERS];
+ for (size_t i = 0; i < num_buffers; ++i) {
+ // It's safe to cast away const here; WSABUF declares non-const
+ // void* because it's used for both send and receive, but since
+ // we're only sending, the data won't be modified.
+ wsa_buffers[i].buf =
+ reinterpret_cast<char*>(const_cast<void*>(buffers[i].data));
+ wsa_buffers[i].len = buffers[i].length;
+ }
+
+ DWORD bytes_sent = 0;
+ if (WSASend(sock, wsa_buffers, num_buffers, &bytes_sent, 0, nullptr,
+ nullptr) != SOCKET_ERROR) {
+ return bytes_sent;
+ }
+
+ return -1;
+}
+
+int android_get_control_socket(const char* name) {
+ return -1;
+}
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
index 924289a..8dafded 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.c
@@ -31,6 +31,20 @@
#define UNUSED __attribute__((unused))
+/* When an object is allocated but not freed in a function,
+ * because its ownership is released to other object like a hashmap,
+ * call RELEASE_OWNERSHIP to tell the clang analyzer and avoid
+ * false warnings about potential memory leak.
+ * For now, a "temporary" assignment to global variables
+ * is enough to confuse the clang static analyzer.
+ */
+#ifdef __clang_analyzer__
+static void *released_pointer;
+#define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
+#else
+#define RELEASE_OWNERSHIP(x)
+#endif
+
struct str_parms {
Hashmap *map;
};
@@ -42,6 +56,9 @@
}
/* use djb hash unless we find it inadequate */
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
static int str_hash_fn(void *str)
{
uint32_t hash = 5381;
@@ -167,9 +184,12 @@
/* if we replaced a value, free it */
old_val = hashmapPut(str_parms->map, key, value);
+ RELEASE_OWNERSHIP(value);
if (old_val) {
free(old_val);
free(key);
+ } else {
+ RELEASE_OWNERSHIP(key);
}
items++;
@@ -219,10 +239,13 @@
goto clean_up;
}
// For new keys, hashmap takes ownership of tmp_key and tmp_val.
+ RELEASE_OWNERSHIP(tmp_key);
+ RELEASE_OWNERSHIP(tmp_val);
tmp_key = tmp_val = NULL;
} else {
// For existing keys, hashmap takes ownership of tmp_val.
// (It also gives up ownership of old_val.)
+ RELEASE_OWNERSHIP(tmp_val);
tmp_val = NULL;
}
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
index 1a8ba86..4dc987e 100644
--- a/libcutils/strdup16to8.c
+++ b/libcutils/strdup16to8.c
@@ -55,7 +55,8 @@
/* Fast path for the usual case where 3*len is < SIZE_MAX-1.
*/
if (len < (SIZE_MAX-1)/3) {
- while (len--) {
+ while (len != 0) {
+ len--;
unsigned int uic = *utf16Str++;
if (uic > 0x07ff)
@@ -69,7 +70,8 @@
}
/* The slower but paranoid version */
- while (len--) {
+ while (len != 0) {
+ len--;
unsigned int uic = *utf16Str++;
size_t utf8Cur = utf8Len;
@@ -112,7 +114,8 @@
* strnlen16to8() properly or at a minimum checked the result of
* its malloc(SIZE_MAX) in case of overflow.
*/
- while (len--) {
+ while (len != 0) {
+ len--;
unsigned int uic = *utf16Str++;
if (uic > 0x07ff) {
diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.c
index 63e5ca4..c23cf8b 100644
--- a/libcutils/strdup8to16.c
+++ b/libcutils/strdup8to16.c
@@ -27,7 +27,7 @@
#define UTF16_REPLACEMENT_CHAR 0xfffd
/* Clever trick from Dianne that returns 1-4 depending on leading bit sequence*/
-#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1)
+#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> (((ch) >> 3) & 0x1e)) & 3) + 1)
/* note: macro expands to multiple lines */
#define UTF8_SHIFT_AND_MASK(unicode, byte) \
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
new file mode 100644
index 0000000..0b0dc09
--- /dev/null
+++ b/libcutils/tests/Android.bp
@@ -0,0 +1,80 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+ name: "libcutils_test_default",
+ srcs: ["sockets_test.cpp"],
+
+ target: {
+ android: {
+ srcs: [
+ "AshmemTest.cpp",
+ "MemsetTest.cpp",
+ "PropertiesTest.cpp",
+ "sched_policy_test.cpp",
+ "trace-dev_test.cpp",
+ "test_str_parms.cpp",
+ "android_get_control_socket_test.cpp",
+ "android_get_control_file_test.cpp",
+ "multiuser_test.cpp"
+ ],
+ },
+
+ not_windows: {
+ srcs: [
+ "test_str_parms.cpp",
+ ],
+ },
+ },
+
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+}
+
+test_libraries = [
+ "libcutils",
+ "liblog",
+ "libbase",
+]
+
+cc_test {
+ name: "libcutils_test",
+ defaults: ["libcutils_test_default"],
+ host_supported: true,
+ shared_libs: test_libraries,
+}
+
+cc_test {
+ name: "libcutils_test_static",
+ defaults: ["libcutils_test_default"],
+ static_libs: ["libc"] + test_libraries,
+ stl: "libc++_static",
+
+ target: {
+ android: {
+ static_executable: true,
+ },
+ windows: {
+ host_ldlibs: ["-lws2_32"],
+
+ enabled: true,
+ },
+ },
+}
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
deleted file mode 100644
index cf70345..0000000
--- a/libcutils/tests/Android.mk
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-test_src_files := \
- test_str_parms.cpp \
-
-test_target_only_src_files := \
- MemsetTest.cpp \
- PropertiesTest.cpp \
-
-test_libraries := libcutils liblog
-
-
-#
-# Target.
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils_test
-LOCAL_SRC_FILES := $(test_src_files) $(test_target_only_src_files)
-LOCAL_SHARED_LIBRARIES := $(test_libraries)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils_test_static
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_SRC_FILES := $(test_src_files) $(test_target_only_src_files)
-LOCAL_STATIC_LIBRARIES := libc $(test_libraries)
-LOCAL_CXX_STL := libc++_static
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_NATIVE_TEST)
-
-
-#
-# Host.
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils_test
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_SHARED_LIBRARIES := $(test_libraries)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_HOST_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils_test_static
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_STATIC_LIBRARIES := $(test_libraries)
-LOCAL_CXX_STL := libc++_static
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcutils/tests/AshmemTest.cpp b/libcutils/tests/AshmemTest.cpp
new file mode 100644
index 0000000..51c679f
--- /dev/null
+++ b/libcutils/tests/AshmemTest.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+#include <gtest/gtest.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+void TestCreateRegion(size_t size, unique_fd &fd, int prot) {
+ fd = unique_fd(ashmem_create_region(nullptr, size));
+ ASSERT_TRUE(fd >= 0);
+ ASSERT_TRUE(ashmem_valid(fd));
+ ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd)));
+ ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
+}
+
+void TestMmap(const unique_fd &fd, size_t size, int prot, void **region) {
+ *region = mmap(nullptr, size, prot, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, *region);
+}
+
+void TestProtDenied(const unique_fd &fd, size_t size, int prot) {
+ EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
+}
+
+void FillData(uint8_t* data, size_t dataLen) {
+ for (size_t i = 0; i < dataLen; i++) {
+ data[i] = i & 0xFF;
+ }
+}
+
+TEST(AshmemTest, BasicTest) {
+ constexpr size_t size = PAGE_SIZE;
+ uint8_t data[size];
+ FillData(data, size);
+
+ unique_fd fd;
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+
+ void *region1;
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, ®ion1));
+
+ memcpy(region1, &data, size);
+ ASSERT_EQ(0, memcmp(region1, &data, size));
+
+ EXPECT_EQ(0, munmap(region1, size));
+
+ void *region2;
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, ®ion2));
+ ASSERT_EQ(0, memcmp(region2, &data, size));
+ EXPECT_EQ(0, munmap(region2, size));
+}
+
+TEST(AshmemTest, ForkTest) {
+ constexpr size_t size = PAGE_SIZE;
+ uint8_t data[size];
+ FillData(data, size);
+
+ unique_fd fd;
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+
+ void *region1;
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, ®ion1));
+
+ memcpy(region1, &data, size);
+ ASSERT_EQ(0, memcmp(region1, &data, size));
+ EXPECT_EQ(0, munmap(region1, size));
+
+ ASSERT_EXIT({
+ void *region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (region2 == MAP_FAILED) {
+ _exit(1);
+ }
+ if (memcmp(region2, &data, size) != 0) {
+ _exit(2);
+ }
+ memset(region2, 0, size);
+ munmap(region2, size);
+ _exit(0);
+ }, ::testing::ExitedWithCode(0),"");
+
+ memset(&data, 0, size);
+ void *region2;
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, ®ion2));
+ ASSERT_EQ(0, memcmp(region2, &data, size));
+ EXPECT_EQ(0, munmap(region2, size));
+}
+
+TEST(AshmemTest, ProtTest) {
+ unique_fd fd;
+ constexpr size_t size = PAGE_SIZE;
+ void *region;
+
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ));
+ TestProtDenied(fd, size, PROT_WRITE);
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, ®ion));
+ EXPECT_EQ(0, munmap(region, size));
+
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
+ TestProtDenied(fd, size, PROT_READ);
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, ®ion));
+ EXPECT_EQ(0, munmap(region, size));
+}
+
+TEST(AshmemTest, ForkProtTest) {
+ unique_fd fd;
+ constexpr size_t size = PAGE_SIZE;
+
+ int protFlags[] = { PROT_READ, PROT_WRITE };
+ for (int i = 0; i < 2; i++) {
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+ ASSERT_EXIT({
+ if (ashmem_set_prot_region(fd, protFlags[i]) >= 0) {
+ _exit(0);
+ } else {
+ _exit(1);
+ }
+ }, ::testing::ExitedWithCode(0), "");
+ ASSERT_NO_FATAL_FAILURE(TestProtDenied(fd, size, protFlags[1-i]));
+ }
+}
+
+TEST(AshmemTest, ForkMultiRegionTest) {
+ constexpr size_t size = PAGE_SIZE;
+ uint8_t data[size];
+ FillData(data, size);
+
+ constexpr int nRegions = 16;
+ unique_fd fd[nRegions];
+ for (int i = 0; i < nRegions; i++) {
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd[i], PROT_READ | PROT_WRITE));
+ void *region;
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, ®ion));
+ memcpy(region, &data, size);
+ ASSERT_EQ(0, memcmp(region, &data, size));
+ EXPECT_EQ(0, munmap(region, size));
+ }
+
+ ASSERT_EXIT({
+ for (int i = 0; i < nRegions; i++) {
+ void *region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0);
+ if (region == MAP_FAILED) {
+ _exit(1);
+ }
+ if (memcmp(region, &data, size) != 0) {
+ munmap(region, size);
+ _exit(2);
+ }
+ memset(region, 0, size);
+ munmap(region, size);
+ }
+ _exit(0);
+ }, ::testing::ExitedWithCode(0), "");
+
+ memset(&data, 0, size);
+ for (int i = 0; i < nRegions; i++) {
+ void *region;
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, ®ion));
+ ASSERT_EQ(0, memcmp(region, &data, size));
+ EXPECT_EQ(0, munmap(region, size));
+ }
+}
diff --git a/libcutils/tests/MemsetTest.cpp b/libcutils/tests/MemsetTest.cpp
index 45efc51..a98485f 100644
--- a/libcutils/tests/MemsetTest.cpp
+++ b/libcutils/tests/MemsetTest.cpp
@@ -20,6 +20,8 @@
#include <sys/mman.h>
#include <sys/types.h>
+#include <memory>
+
#include <cutils/memory.h>
#include <gtest/gtest.h>
@@ -127,14 +129,14 @@
min_incr = 2;
value |= value << 16;
}
- uint32_t* expected_buf = new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)];
+ std::unique_ptr<uint32_t[]> expected_buf(new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)]);
for (size_t i = 0; i < MAX_TEST_SIZE/sizeof(uint32_t); i++) {
expected_buf[i] = value;
}
// Allocate one large buffer with lots of extra space so that we can
// guarantee that all possible alignments will fit.
- uint8_t *buf = new uint8_t[3*MAX_TEST_SIZE];
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[3*MAX_TEST_SIZE]);
uint8_t *buf_align;
for (size_t i = 0; i < num_aligns; i++) {
size_t incr = min_incr;
@@ -142,7 +144,7 @@
incr = GetIncrement(len, min_incr);
buf_align = reinterpret_cast<uint8_t*>(GetAlignedPtr(
- buf+FENCEPOST_LENGTH, align[i][0], align[i][1]));
+ buf.get()+FENCEPOST_LENGTH, align[i][0], align[i][1]));
SetFencepost(&buf_align[-FENCEPOST_LENGTH]);
SetFencepost(&buf_align[len]);
@@ -153,15 +155,13 @@
} else {
android_memset32(reinterpret_cast<uint32_t*>(buf_align), value, len);
}
- ASSERT_EQ(0, memcmp(expected_buf, buf_align, len))
+ ASSERT_EQ(0, memcmp(expected_buf.get(), buf_align, len))
<< "Failed size " << len << " align " << align[i][0] << " " << align[i][1] << "\n";
VerifyFencepost(&buf_align[-FENCEPOST_LENGTH]);
VerifyFencepost(&buf_align[len]);
}
}
- delete expected_buf;
- delete buf;
}
TEST(libcutils, android_memset16_non_zero) {
diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp
index 659821c..7921972 100644
--- a/libcutils/tests/PropertiesTest.cpp
+++ b/libcutils/tests/PropertiesTest.cpp
@@ -15,20 +15,22 @@
*/
#define LOG_TAG "Properties_test"
-#include <utils/Log.h>
-#include <gtest/gtest.h>
-#include <cutils/properties.h>
#include <limits.h>
-#include <string>
-#include <sstream>
+
#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <android/log.h>
+#include <android-base/macros.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
namespace android {
#define STRINGIFY_INNER(x) #x
#define STRINGIFY(x) STRINGIFY_INNER(x)
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#define ASSERT_OK(x) ASSERT_EQ(0, (x))
#define EXPECT_OK(x) EXPECT_EQ(0, (x))
@@ -85,7 +87,7 @@
}
void ResetValue(unsigned char c = 0xFF) {
- for (size_t i = 0; i < ARRAY_SIZE(mValue); ++i) {
+ for (size_t i = 0; i < arraysize(mValue); ++i) {
mValue[i] = (char) c;
}
}
@@ -106,14 +108,14 @@
ResetValue();
// Since the value is null, default value will be returned
- int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);
EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);
}
// Trivial case => get returns what was set
{
- int len = SetAndGetProperty("hello_world");
+ size_t len = SetAndGetProperty("hello_world");
EXPECT_EQ(strlen("hello_world"), len) << "hello_world key";
EXPECT_STREQ("hello_world", mValue);
ResetValue();
@@ -122,7 +124,7 @@
// Set to empty string => get returns default always
{
const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING";
- int len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
+ size_t len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key";
EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);
ResetValue();
@@ -147,7 +149,7 @@
// Expect that the value set fails since it's too long
EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));
- int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed";
EXPECT_STREQ(VALID_TEST_VALUE, mValue);
@@ -157,19 +159,68 @@
TEST_F(PropertiesTest, GetString) {
- // Try to use a default value that's too long => set fails
+ // Try to use a default value that's too long => get truncates the value
{
ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a');
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
// Expect that the value is truncated since it's too long (by 1)
int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX-1, len);
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
EXPECT_STREQ(maxLengthString.c_str(), mValue);
ResetValue();
}
+
+ // Try to use a default value that's the max length => get succeeds
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');
+
+ // Expect that the value matches maxLengthString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+ }
+
+ // Try to use a default value of length one => get succeeds
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ std::string oneCharString = std::string(1, 'c');
+
+ // Expect that the value matches oneCharString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());
+ EXPECT_EQ(1, len);
+ EXPECT_STREQ(oneCharString.c_str(), mValue);
+ ResetValue();
+ }
+
+ // Try to use a default value of length zero => get succeeds
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ std::string zeroCharString = std::string(0, 'd');
+
+ // Expect that the value matches oneCharString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());
+ EXPECT_EQ(0, len);
+ EXPECT_STREQ(zeroCharString.c_str(), mValue);
+ ResetValue();
+ }
+
+ // Try to use a NULL default value => get returns 0
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ // Expect a return value of 0
+ int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);
+ EXPECT_EQ(0, len);
+ ResetValue();
+ }
}
TEST_F(PropertiesTest, GetBool) {
@@ -177,7 +228,7 @@
* TRUE
*/
const char *valuesTrue[] = { "1", "true", "y", "yes", "on", };
- for (size_t i = 0; i < ARRAY_SIZE(valuesTrue); ++i) {
+ for (size_t i = 0; i < arraysize(valuesTrue); ++i) {
ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesTrue[i]));
bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false);
EXPECT_TRUE(val) << "Property should've been TRUE for value: '" << valuesTrue[i] << "'";
@@ -187,7 +238,7 @@
* FALSE
*/
const char *valuesFalse[] = { "0", "false", "n", "no", "off", };
- for (size_t i = 0; i < ARRAY_SIZE(valuesFalse); ++i) {
+ for (size_t i = 0; i < arraysize(valuesFalse); ++i) {
ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesFalse[i]));
bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true);
EXPECT_FALSE(val) << "Property shoud've been FALSE For string value: '" << valuesFalse[i] << "'";
@@ -200,7 +251,7 @@
"+1", " 1 ", " true", " true ", " y ", " yes", "yes ",
"+0", "-0", "00", " 00 ", " false", "false ",
};
- for (size_t i = 0; i < ARRAY_SIZE(valuesNeither); ++i) {
+ for (size_t i = 0; i < arraysize(valuesNeither); ++i) {
ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesNeither[i]));
// The default value should always be used
@@ -249,9 +300,9 @@
DEFAULT_VALUE, DEFAULT_VALUE,
};
- ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues));
+ ASSERT_EQ(arraysize(setValues), arraysize(getValues));
- for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) {
+ for (size_t i = 0; i < arraysize(setValues); ++i) {
ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i]));
int64_t val = property_get_int64(PROPERTY_TEST_KEY, DEFAULT_VALUE);
@@ -296,9 +347,9 @@
DEFAULT_VALUE, DEFAULT_VALUE,
};
- ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues));
+ ASSERT_EQ(arraysize(setValues), arraysize(getValues));
- for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) {
+ for (size_t i = 0; i < arraysize(setValues); ++i) {
ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i]));
int32_t val = property_get_int32(PROPERTY_TEST_KEY, DEFAULT_VALUE);
diff --git a/libcutils/tests/android_get_control_file_test.cpp b/libcutils/tests/android_get_control_file_test.cpp
new file mode 100644
index 0000000..6c6fd2a
--- /dev/null
+++ b/libcutils/tests/android_get_control_file_test.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <cutils/android_get_control_file.h>
+#include <gtest/gtest.h>
+
+TEST(FilesTest, android_get_control_file) {
+ TemporaryFile tf;
+ ASSERT_GE(tf.fd, 0);
+
+ std::string key(ANDROID_FILE_ENV_PREFIX);
+ key += tf.path;
+
+ std::for_each(key.begin(), key.end(), [] (char& c) { c = isalnum(c) ? c : '_'; });
+
+ EXPECT_EQ(unsetenv(key.c_str()), 0);
+ EXPECT_EQ(android_get_control_file(tf.path), -1);
+
+ EXPECT_EQ(setenv(key.c_str(), android::base::StringPrintf("%d", tf.fd).c_str(), true), 0);
+
+ EXPECT_EQ(android_get_control_file(tf.path), tf.fd);
+ close(tf.fd);
+ EXPECT_EQ(android_get_control_file(tf.path), -1);
+ EXPECT_EQ(unsetenv(key.c_str()), 0);
+ EXPECT_EQ(android_get_control_file(tf.path), -1);
+}
diff --git a/libcutils/tests/android_get_control_socket_test.cpp b/libcutils/tests/android_get_control_socket_test.cpp
new file mode 100644
index 0000000..e586748
--- /dev/null
+++ b/libcutils/tests/android_get_control_socket_test.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+
+#include <cutils/sockets.h>
+#include <gtest/gtest.h>
+
+#ifndef SOCK_NONBLOCK
+#define SOCK_NONBLOCK 0
+#endif
+
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+
+TEST(SocketsTest, android_get_control_socket) {
+ static const char key[] = ANDROID_SOCKET_ENV_PREFIX "SocketsTest_android_get_control_socket";
+ static const char* name = key + strlen(ANDROID_SOCKET_ENV_PREFIX);
+
+ EXPECT_EQ(unsetenv(key), 0);
+ EXPECT_EQ(android_get_control_socket(name), -1);
+
+ int fd;
+ ASSERT_GE(fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0), 0);
+#ifdef F_GETFL
+ int flags;
+ ASSERT_GE(flags = fcntl(fd, F_GETFL), 0);
+ ASSERT_GE(fcntl(fd, F_SETFL, flags | O_NONBLOCK), 0);
+#endif
+ EXPECT_EQ(android_get_control_socket(name), -1);
+
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name);
+ unlink(addr.sun_path);
+
+ EXPECT_EQ(bind(fd, (struct sockaddr*)&addr, sizeof(addr)), 0);
+ EXPECT_EQ(android_get_control_socket(name), -1);
+
+ char val[32];
+ snprintf(val, sizeof(val), "%d", fd);
+ EXPECT_EQ(setenv(key, val, true), 0);
+
+ EXPECT_EQ(android_get_control_socket(name), fd);
+ socket_close(fd);
+ EXPECT_EQ(android_get_control_socket(name), -1);
+ EXPECT_EQ(unlink(addr.sun_path), 0);
+ EXPECT_EQ(android_get_control_socket(name), -1);
+ EXPECT_EQ(unsetenv(key), 0);
+ EXPECT_EQ(android_get_control_socket(name), -1);
+}
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
new file mode 100644
index 0000000..c5f58b4
--- /dev/null
+++ b/libcutils/tests/multiuser_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/multiuser.h>
+#include <gtest/gtest.h>
+
+TEST(MultiuserTest, TestMerge) {
+ EXPECT_EQ(0, multiuser_get_uid(0, 0));
+ EXPECT_EQ(1000, multiuser_get_uid(0, 1000));
+ EXPECT_EQ(10000, multiuser_get_uid(0, 10000));
+ EXPECT_EQ(50000, multiuser_get_uid(0, 50000));
+ EXPECT_EQ(1000000, multiuser_get_uid(10, 0));
+ EXPECT_EQ(1001000, multiuser_get_uid(10, 1000));
+ EXPECT_EQ(1010000, multiuser_get_uid(10, 10000));
+ EXPECT_EQ(1050000, multiuser_get_uid(10, 50000));
+}
+
+TEST(MultiuserTest, TestSplitUser) {
+ EXPECT_EQ(0, multiuser_get_user_id(0));
+ EXPECT_EQ(0, multiuser_get_user_id(1000));
+ EXPECT_EQ(0, multiuser_get_user_id(10000));
+ EXPECT_EQ(0, multiuser_get_user_id(50000));
+ EXPECT_EQ(10, multiuser_get_user_id(1000000));
+ EXPECT_EQ(10, multiuser_get_user_id(1001000));
+ EXPECT_EQ(10, multiuser_get_user_id(1010000));
+ EXPECT_EQ(10, multiuser_get_user_id(1050000));
+}
+
+TEST(MultiuserTest, TestSplitApp) {
+ EXPECT_EQ(0, multiuser_get_app_id(0));
+ EXPECT_EQ(1000, multiuser_get_app_id(1000));
+ EXPECT_EQ(10000, multiuser_get_app_id(10000));
+ EXPECT_EQ(50000, multiuser_get_app_id(50000));
+ EXPECT_EQ(0, multiuser_get_app_id(1000000));
+ EXPECT_EQ(1000, multiuser_get_app_id(1001000));
+ EXPECT_EQ(10000, multiuser_get_app_id(1010000));
+ EXPECT_EQ(50000, multiuser_get_app_id(1050000));
+}
+
+TEST(MultiuserTest, TestCache) {
+ EXPECT_EQ(-1, multiuser_get_cache_gid(0, 0));
+ EXPECT_EQ(-1, multiuser_get_cache_gid(0, 1000));
+ EXPECT_EQ(20000, multiuser_get_cache_gid(0, 10000));
+ EXPECT_EQ(-1, multiuser_get_cache_gid(0, 50000));
+ EXPECT_EQ(1020000, multiuser_get_cache_gid(10, 10000));
+}
+
+TEST(MultiuserTest, TestExt) {
+ EXPECT_EQ(-1, multiuser_get_ext_gid(0, 0));
+ EXPECT_EQ(-1, multiuser_get_ext_gid(0, 1000));
+ EXPECT_EQ(30000, multiuser_get_ext_gid(0, 10000));
+ EXPECT_EQ(-1, multiuser_get_ext_gid(0, 50000));
+ EXPECT_EQ(1030000, multiuser_get_ext_gid(10, 10000));
+}
+
+TEST(MultiuserTest, TestShared) {
+ EXPECT_EQ(-1, multiuser_get_shared_gid(0, 0));
+ EXPECT_EQ(-1, multiuser_get_shared_gid(0, 1000));
+ EXPECT_EQ(50000, multiuser_get_shared_gid(0, 10000));
+ EXPECT_EQ(-1, multiuser_get_shared_gid(0, 50000));
+ EXPECT_EQ(1050000, multiuser_get_shared_gid(10, 10000));
+}
diff --git a/libcutils/tests/sched_policy_test.cpp b/libcutils/tests/sched_policy_test.cpp
new file mode 100644
index 0000000..173174a
--- /dev/null
+++ b/libcutils/tests/sched_policy_test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <chrono>
+#include <thread>
+#include <vector>
+
+#include <sys/capability.h>
+
+#include <cutils/sched_policy.h>
+
+#include <gtest/gtest.h>
+
+bool hasCapSysNice() {
+ __user_cap_header_struct header;
+ memset(&header, 0, sizeof(header));
+ header.version = _LINUX_CAPABILITY_VERSION_3;
+
+ __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3];
+ if (capget(&header, &caps[0])) {
+ GTEST_LOG_(WARNING) << "failed to get process capabilities";
+ return false;
+ }
+
+ auto nice_idx = CAP_TO_INDEX(CAP_SYS_NICE);
+ auto nice_mask = CAP_TO_MASK(CAP_SYS_NICE);
+ return caps[nice_idx].effective & nice_mask;
+}
+
+long long medianSleepTime() {
+ std::vector<long long> sleepTimes;
+ constexpr size_t numSamples = 100;
+
+ for (size_t i = 0; i < numSamples; i++) {
+ auto start = std::chrono::steady_clock::now();
+ std::this_thread::sleep_for(std::chrono::nanoseconds(1));
+ auto end = std::chrono::steady_clock::now();
+
+ auto diff = end - start;
+ sleepTimes.push_back(diff.count());
+ }
+
+ constexpr auto median = numSamples / 2;
+ std::nth_element(sleepTimes.begin(), sleepTimes.begin() + median,
+ sleepTimes.end());
+ return sleepTimes[median];
+}
+
+TEST(SchedPolicy, set_sched_policy) {
+ if (!hasCapSysNice()) {
+ GTEST_LOG_(INFO) << "skipping test that requires CAP_SYS_NICE";
+ return;
+ }
+
+ // A measureable effect of scheduling policy is that the kernel has 800x
+ // greater slack time in waking up a sleeping background thread.
+ //
+ // Look for 100x difference in how long FB and BG threads actually sleep
+ // when trying to sleep for 1 ns. This difference is large enough not
+ // to happen by chance, but small enough (compared to 800x) to keep inherent
+ // fuzziness in scheduler behavior from causing false negatives.
+ const unsigned int BG_FG_SLACK_FACTOR = 100;
+
+ ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+ auto bgSleepTime = medianSleepTime();
+
+ ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+ auto fgSleepTime = medianSleepTime();
+ ASSERT_GT(bgSleepTime, fgSleepTime * BG_FG_SLACK_FACTOR);
+}
+
+TEST(SchedPolicy, get_sched_policy) {
+ SchedPolicy policy;
+ ASSERT_EQ(0, get_sched_policy(0, &policy));
+
+ const char *policyName = get_sched_policy_name(policy);
+ EXPECT_NE(nullptr, policyName);
+ EXPECT_STRNE("error", policyName);
+
+ ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+ SchedPolicy newPolicy;
+ ASSERT_EQ(0, get_sched_policy(0, &newPolicy));
+ EXPECT_EQ(SP_BACKGROUND, newPolicy);
+}
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp
new file mode 100644
index 0000000..0441fb6
--- /dev/null
+++ b/libcutils/tests/sockets_test.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+// Tests socket functionality using loopback connections. Requires IPv4 and
+// IPv6 capabilities. These tests assume that no UDP packets are lost, which
+// should be the case for loopback communication, but is not guaranteed.
+
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <cutils/sockets.h>
+#include <gtest/gtest.h>
+
+// Makes sure the passed sockets are valid, sends data between them, and closes
+// them. Any failures are logged with gtest.
+//
+// On Mac recvfrom() will not fill in the address for TCP sockets, so we need
+// separate logic paths depending on socket type.
+static void TestConnectedSockets(cutils_socket_t server, cutils_socket_t client,
+ int type) {
+ ASSERT_NE(INVALID_SOCKET, server);
+ ASSERT_NE(INVALID_SOCKET, client);
+
+ char buffer[128];
+ sockaddr_storage addr;
+ socklen_t addr_size = sizeof(addr);
+
+ // Send client -> server first to get the UDP client's address.
+ ASSERT_EQ(3, send(client, "foo", 3, 0));
+ if (type == SOCK_DGRAM) {
+ EXPECT_EQ(3, recvfrom(server, buffer, sizeof(buffer), 0,
+ reinterpret_cast<sockaddr*>(&addr), &addr_size));
+ } else {
+ EXPECT_EQ(3, recv(server, buffer, sizeof(buffer), 0));
+ }
+ EXPECT_EQ(0, memcmp(buffer, "foo", 3));
+
+ // Now send server -> client.
+ if (type == SOCK_DGRAM) {
+ ASSERT_EQ(3, sendto(server, "bar", 3, 0,
+ reinterpret_cast<sockaddr*>(&addr), addr_size));
+ } else {
+ ASSERT_EQ(3, send(server, "bar", 3, 0));
+ }
+ EXPECT_EQ(3, recv(client, buffer, sizeof(buffer), 0));
+ EXPECT_EQ(0, memcmp(buffer, "bar", 3));
+
+ // Send multiple buffers using socket_send_buffers().
+ std::string data[] = {"foo", "bar", "12345"};
+ cutils_socket_buffer_t socket_buffers[] = { {data[0].data(), data[0].length()},
+ {data[1].data(), data[1].length()},
+ {data[2].data(), data[2].length()} };
+ EXPECT_EQ(11, socket_send_buffers(client, socket_buffers, 3));
+ EXPECT_EQ(11, recv(server, buffer, sizeof(buffer), 0));
+ EXPECT_EQ(0, memcmp(buffer, "foobar12345", 11));
+
+ EXPECT_EQ(0, socket_close(server));
+ EXPECT_EQ(0, socket_close(client));
+}
+
+// Tests receive timeout. The timing verification logic must be very coarse to
+// make sure different systems can all pass these tests.
+void TestReceiveTimeout(cutils_socket_t sock) {
+ time_t start_time;
+ char buffer[32];
+
+ // Make sure a 20ms timeout completes in 1 second or less.
+ EXPECT_EQ(0, socket_set_receive_timeout(sock, 20));
+ start_time = time(nullptr);
+ EXPECT_EQ(-1, recv(sock, buffer, sizeof(buffer), 0));
+ EXPECT_LE(difftime(time(nullptr), start_time), 1.0);
+
+ // Make sure a 1250ms timeout takes 1 second or more.
+ EXPECT_EQ(0, socket_set_receive_timeout(sock, 1250));
+ start_time = time(nullptr);
+ EXPECT_EQ(-1, recv(sock, buffer, sizeof(buffer), 0));
+ EXPECT_LE(1.0, difftime(time(nullptr), start_time));
+}
+
+// Tests socket_get_local_port().
+TEST(SocketsTest, TestGetLocalPort) {
+ cutils_socket_t server;
+
+ // Check a bunch of ports so that we can ignore any conflicts in case
+ // of ports already being taken, but if a server is able to start up we
+ // should always be able to read its port.
+ for (int port : {10000, 12345, 15999, 20202, 25000}) {
+ for (int type : {SOCK_DGRAM, SOCK_STREAM}) {
+ server = socket_inaddr_any_server(port, SOCK_DGRAM);
+ if (server != INVALID_SOCKET) {
+ EXPECT_EQ(port, socket_get_local_port(server));
+ }
+ socket_close(server);
+ }
+ }
+
+ // Check expected failure for an invalid socket.
+ EXPECT_EQ(-1, socket_get_local_port(INVALID_SOCKET));
+}
+
+// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 UDP.
+TEST(SocketsTest, TestIpv4UdpLoopback) {
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_DGRAM);
+ cutils_socket_t client = socket_network_client(
+ "127.0.0.1", socket_get_local_port(server), SOCK_DGRAM);
+
+ TestConnectedSockets(server, client, SOCK_DGRAM);
+}
+
+// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 TCP.
+TEST(SocketsTest, TestIpv4TcpLoopback) {
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);
+ ASSERT_NE(INVALID_SOCKET, server);
+
+ cutils_socket_t client = socket_network_client(
+ "127.0.0.1", socket_get_local_port(server), SOCK_STREAM);
+ cutils_socket_t handler = accept(server, nullptr, nullptr);
+ EXPECT_EQ(0, socket_close(server));
+
+ TestConnectedSockets(handler, client, SOCK_STREAM);
+}
+
+// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 UDP.
+TEST(SocketsTest, TestIpv6UdpLoopback) {
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_DGRAM);
+ cutils_socket_t client = socket_network_client(
+ "::1", socket_get_local_port(server), SOCK_DGRAM);
+
+ TestConnectedSockets(server, client, SOCK_DGRAM);
+}
+
+// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 TCP.
+TEST(SocketsTest, TestIpv6TcpLoopback) {
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);
+ ASSERT_NE(INVALID_SOCKET, server);
+
+ cutils_socket_t client = socket_network_client(
+ "::1", socket_get_local_port(server), SOCK_STREAM);
+ cutils_socket_t handler = accept(server, nullptr, nullptr);
+ EXPECT_EQ(0, socket_close(server));
+
+ TestConnectedSockets(handler, client, SOCK_STREAM);
+}
+
+// Tests setting a receive timeout for UDP sockets.
+TEST(SocketsTest, TestUdpReceiveTimeout) {
+ cutils_socket_t sock = socket_inaddr_any_server(0, SOCK_DGRAM);
+ ASSERT_NE(INVALID_SOCKET, sock);
+
+ TestReceiveTimeout(sock);
+
+ EXPECT_EQ(0, socket_close(sock));
+}
+
+// Tests setting a receive timeout for TCP sockets.
+TEST(SocketsTest, TestTcpReceiveTimeout) {
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);
+ ASSERT_NE(INVALID_SOCKET, server);
+
+ cutils_socket_t client = socket_network_client(
+ "localhost", socket_get_local_port(server), SOCK_STREAM);
+ cutils_socket_t handler = accept(server, nullptr, nullptr);
+ EXPECT_EQ(0, socket_close(server));
+
+ TestReceiveTimeout(handler);
+
+ EXPECT_EQ(0, socket_close(client));
+ EXPECT_EQ(0, socket_close(handler));
+}
+
+// Tests socket_send_buffers() failure.
+TEST(SocketsTest, TestSocketSendBuffersFailure) {
+ EXPECT_EQ(-1, socket_send_buffers(INVALID_SOCKET, nullptr, 0));
+}
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/tests/trace-dev_test.cpp
new file mode 100644
index 0000000..edf981b
--- /dev/null
+++ b/libcutils/tests/trace-dev_test.cpp
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "../trace-dev.c"
+
+class TraceDevTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ lseek(tmp_file_.fd, 0, SEEK_SET);
+ atrace_marker_fd = tmp_file_.fd;
+ }
+
+ void TearDown() override {
+ atrace_marker_fd = -1;
+ }
+
+ TemporaryFile tmp_file_;
+
+ static std::string MakeName(size_t length) {
+ std::string name;
+ for (size_t i = 0; i < length; i++) {
+ name += '0' + (i % 10);
+ }
+ return name;
+ }
+};
+
+TEST_F(TraceDevTest, atrace_begin_body_normal) {
+ atrace_begin_body("fake_name");
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("B|%d|fake_name", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_exact) {
+ std::string expected = android::base::StringPrintf("B|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);
+ atrace_begin_body(name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name;
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_begin_body(name.c_str());
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_truncated) {
+ std::string expected = android::base::StringPrintf("B|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_begin_body(name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
+ expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_normal) {
+ atrace_async_begin_body("fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("S|%d|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_exact) {
+ std::string expected = android::base::StringPrintf("S|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+ atrace_async_begin_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_async_begin_body(name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_truncated) {
+ std::string expected = android::base::StringPrintf("S|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_begin_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_normal) {
+ atrace_async_end_body("fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("F|%d|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_exact) {
+ std::string expected = android::base::StringPrintf("F|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+ atrace_async_end_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_async_end_body(name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_truncated) {
+ std::string expected = android::base::StringPrintf("F|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_end_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_normal) {
+ atrace_int_body("fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("C|%d|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_exact) {
+ std::string expected = android::base::StringPrintf("C|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+ atrace_int_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_int_body(name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_truncated) {
+ std::string expected = android::base::StringPrintf("C|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_int_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_normal) {
+ atrace_int64_body("fake_name", 17179869183L);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("C|%d|fake_name|17179869183", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_exact) {
+ std::string expected = android::base::StringPrintf("C|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 13);
+ atrace_int64_body(name.c_str(), 17179869183L);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name + "|17179869183";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_int64_body(name.c_str(), 17179869183L);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_truncated) {
+ std::string expected = android::base::StringPrintf("C|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_int64_body(name.c_str(), 17179869183L);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 13;
+ expected += android::base::StringPrintf("%.*s|17179869183", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index a06987e..113f423 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "cutils-trace"
+
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
@@ -23,12 +25,11 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
+
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <cutils/trace.h>
-
-#define LOG_TAG "cutils-trace"
-#include <log/log.h>
+#include <private/android_logger.h>
/**
* Maximum size of a message that can be logged to the trace buffer.
@@ -66,24 +67,17 @@
// values listed in the app_cmdlines property.
static bool atrace_is_cmdline_match(const char* cmdline)
{
+ int count = property_get_int32("debug.atrace.app_number", 0);
+
+ char buf[PROPERTY_KEY_MAX];
char value[PROPERTY_VALUE_MAX];
- char* start = value;
- property_get("debug.atrace.app_cmdlines", value, "");
-
- while (start != NULL) {
- char* end = strchr(start, ',');
-
- if (end != NULL) {
- *end = '\0';
- end++;
- }
-
- if (strcmp(cmdline, start) == 0) {
+ for (int i = 0; i < count; i++) {
+ snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
+ property_get(buf, value, "");
+ if (strcmp(value, cmdline) == 0) {
return true;
}
-
- start = end;
}
return false;
@@ -92,19 +86,12 @@
// Determine whether application-level tracing is enabled for this process.
static bool atrace_is_app_tracing_enabled()
{
- bool sys_debuggable = false;
- char value[PROPERTY_VALUE_MAX];
+ bool sys_debuggable = __android_log_is_debuggable();
bool result = false;
- // Check whether the system is debuggable.
- property_get("ro.debuggable", value, "0");
- if (value[0] == '1') {
- sys_debuggable = true;
- }
-
if (sys_debuggable || atrace_is_debuggable) {
// Check whether tracing is enabled for this process.
- FILE * file = fopen("/proc/self/cmdline", "r");
+ FILE * file = fopen("/proc/self/cmdline", "re");
if (file) {
char cmdline[4096];
if (fgets(cmdline, sizeof(cmdline), file)) {
@@ -173,7 +160,7 @@
static void atrace_init_once()
{
- atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
+ atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
if (atrace_marker_fd == -1) {
ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
atrace_enabled_tags = 0;
@@ -194,49 +181,53 @@
void atrace_begin_body(const char* name)
{
char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name);
+ int len = snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
+ if (len >= (int) sizeof(buf)) {
+ ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name);
+ len = sizeof(buf) - 1;
+ }
write(atrace_marker_fd, buf, len);
}
+void atrace_end_body()
+{
+ char c = 'E';
+ write(atrace_marker_fd, &c, 1);
+}
+
+#define WRITE_MSG(format_begin, format_end, pid, name, value) { \
+ char buf[ATRACE_MESSAGE_LENGTH]; \
+ int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
+ name, value); \
+ if (len >= (int) sizeof(buf)) { \
+ /* Given the sizeof(buf), and all of the current format buffers, \
+ * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
+ int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+ /* Truncate the name to make the message fit. */ \
+ ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+ len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
+ name_len, name, value); \
+ } \
+ write(atrace_marker_fd, buf, len); \
+}
void atrace_async_begin_body(const char* name, int32_t cookie)
{
- char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
-
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%" PRId32,
- getpid(), name, cookie);
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("S|%d|", "|%" PRId32, getpid(), name, cookie);
}
void atrace_async_end_body(const char* name, int32_t cookie)
{
- char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
-
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%" PRId32,
- getpid(), name, cookie);
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("F|%d|", "|%" PRId32, getpid(), name, cookie);
}
void atrace_int_body(const char* name, int32_t value)
{
- char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
-
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId32,
- getpid(), name, value);
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("C|%d|", "|%" PRId32, getpid(), name, value);
}
void atrace_int64_body(const char* name, int64_t value)
{
- char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
-
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId64,
- getpid(), name, value);
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("C|%d|", "|%" PRId64, getpid(), name, value);
}
diff --git a/libcutils/trace-host.c b/libcutils/trace-host.c
index 6478e3e..05842cd 100644
--- a/libcutils/trace-host.c
+++ b/libcutils/trace-host.c
@@ -29,6 +29,7 @@
void atrace_update_tags() { }
void atrace_setup() { }
void atrace_begin_body(const char* name __unused) { }
+void atrace_end_body() { }
void atrace_async_begin_body(const char* name __unused, int32_t cookie __unused) { }
void atrace_async_end_body(const char* name __unused, int32_t cookie __unused) { }
void atrace_int_body(const char* name __unused, int32_t value __unused) { }
diff --git a/libcutils/uevent.c b/libcutils/uevent.c
index de5d227..f548dca 100644
--- a/libcutils/uevent.c
+++ b/libcutils/uevent.c
@@ -116,7 +116,12 @@
if(s < 0)
return -1;
- setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz));
+ /* buf_sz should be less than net.core.rmem_max for this to succeed */
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
+ close(s);
+ return -1;
+ }
+
setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
new file mode 100644
index 0000000..041fd63
--- /dev/null
+++ b/libdiskconfig/Android.bp
@@ -0,0 +1,32 @@
+cc_library {
+ name: "libdiskconfig",
+ srcs: [
+ "diskconfig.c",
+ "diskutils.c",
+ "write_lst.c",
+ "config_mbr.c",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+ cflags: ["-Werror"],
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ linux: {
+ cflags: [
+ "-O2",
+ "-g",
+ "-W",
+ "-Wall",
+ "-D_LARGEFILE64_SOURCE",
+ ],
+ },
+ },
+}
diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk
deleted file mode 100644
index 624e385..0000000
--- a/libdiskconfig/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-commonSources := \
- diskconfig.c \
- diskutils.c \
- write_lst.c \
- config_mbr.c
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(commonSources)
-LOCAL_MODULE := libdiskconfig
-LOCAL_MODULE_TAGS := optional
-LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc
-LOCAL_CFLAGS := -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-ifeq ($(HOST_OS),linux)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(commonSources)
-LOCAL_MODULE := libdiskconfig_host
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE
-include $(BUILD_HOST_STATIC_LIBRARY)
-endif # HOST_OS == linux
diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c
index 7b6ca1c..ace9bbf 100644
--- a/libdiskconfig/config_mbr.c
+++ b/libdiskconfig/config_mbr.c
@@ -16,15 +16,14 @@
*/
#define LOG_TAG "config_mbr"
+
#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <stdio.h>
-
-#include <cutils/log.h>
#include <diskconfig/diskconfig.h>
-
+#include <log/log.h>
/* start and len are in LBA units */
static void
diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c
index 1167d4b..c7e1b43 100644
--- a/libdiskconfig/diskconfig.c
+++ b/libdiskconfig/diskconfig.c
@@ -20,21 +20,19 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
-
-#include <linux/fs.h>
+#include <unistd.h>
#include <cutils/config_utils.h>
#include <log/log.h>
#include <diskconfig/diskconfig.h>
-
static int
parse_len(const char *str, uint64_t *plen)
{
diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c
index 5d0ee62..fe1b4c1 100644
--- a/libdiskconfig/diskutils.c
+++ b/libdiskconfig/diskutils.c
@@ -23,8 +23,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include <sys/stat.h>
+#include <unistd.h>
#include <log/log.h>
diff --git a/libdiskconfig/dump_diskconfig.c b/libdiskconfig/dump_diskconfig.c
index 75256f6..3c4f620 100644
--- a/libdiskconfig/dump_diskconfig.c
+++ b/libdiskconfig/dump_diskconfig.c
@@ -16,9 +16,10 @@
*/
#define LOG_TAG "dump_diskconfig"
+
#include <stdio.h>
-#include <cutils/log.h>
+#include <log/log.h>
#include "diskconfig.h"
diff --git a/include/diskconfig/diskconfig.h b/libdiskconfig/include/diskconfig/diskconfig.h
similarity index 100%
rename from include/diskconfig/diskconfig.h
rename to libdiskconfig/include/diskconfig/diskconfig.h
diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c
index 90b1c82..c3d5c0a 100644
--- a/libdiskconfig/write_lst.c
+++ b/libdiskconfig/write_lst.c
@@ -16,15 +16,15 @@
*/
#define LOG_TAG "write_lst"
-#include <sys/types.h>
+
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <cutils/log.h>
-
#include <diskconfig/diskconfig.h>
+#include <log/log.h>
struct write_list *
alloc_wl(uint32_t data_len)
diff --git a/libion/Android.bp b/libion/Android.bp
new file mode 100644
index 0000000..da98111
--- /dev/null
+++ b/libion/Android.bp
@@ -0,0 +1,25 @@
+
+cc_library {
+ name: "libion",
+ srcs: ["ion.c"],
+ shared_libs: ["liblog"],
+ local_include_dirs: [
+ "include",
+ "kernel-headers",
+ ],
+ export_include_dirs: [
+ "include",
+ "kernel-headers",
+ ],
+ cflags: ["-Werror"],
+}
+
+cc_binary {
+ name: "iontest",
+ srcs: ["ion_test.c"],
+ static_libs: ["libion"],
+ shared_libs: ["liblog"],
+ cflags: ["-Werror"],
+}
+
+subdirs = ["tests"]
diff --git a/libion/Android.mk b/libion/Android.mk
deleted file mode 100644
index 6562cd3..0000000
--- a/libion/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := ion.c
-LOCAL_MODULE := libion
-LOCAL_MODULE_TAGS := optional
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
-LOCAL_CFLAGS := -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := ion.c ion_test.c
-LOCAL_MODULE := iontest
-LOCAL_MODULE_TAGS := optional tests
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/libion/ion.c b/libion/ion.c
index 4908932..9aaa6f2 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -19,21 +19,22 @@
*/
#define LOG_TAG "ion"
-#include <cutils/log.h>
#include <errno.h>
#include <fcntl.h>
+#include <linux/ion.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
+#include <unistd.h>
-#include <linux/ion.h>
#include <ion/ion.h>
+#include <log/log.h>
int ion_open()
{
- int fd = open("/dev/ion", O_RDWR);
+ int fd = open("/dev/ion", O_RDONLY | O_CLOEXEC);
if (fd < 0)
ALOGE("open /dev/ion failed!\n");
return fd;
@@ -91,6 +92,7 @@
int flags, off_t offset, unsigned char **ptr, int *map_fd)
{
int ret;
+ unsigned char *tmp_ptr;
struct ion_fd_data data = {
.handle = handle,
};
@@ -103,16 +105,17 @@
ret = ion_ioctl(fd, ION_IOC_MAP, &data);
if (ret < 0)
return ret;
- *map_fd = data.fd;
- if (*map_fd < 0) {
+ if (data.fd < 0) {
ALOGE("map ioctl returned negative fd\n");
return -EINVAL;
}
- *ptr = mmap(NULL, length, prot, flags, *map_fd, offset);
- if (*ptr == MAP_FAILED) {
+ tmp_ptr = mmap(NULL, length, prot, flags, data.fd, offset);
+ if (tmp_ptr == MAP_FAILED) {
ALOGE("mmap failed: %s\n", strerror(errno));
return -errno;
}
+ *map_fd = data.fd;
+ *ptr = tmp_ptr;
return ret;
}
@@ -129,11 +132,11 @@
ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
if (ret < 0)
return ret;
- *share_fd = data.fd;
- if (*share_fd < 0) {
+ if (data.fd < 0) {
ALOGE("share ioctl returned negative fd\n");
return -EINVAL;
}
+ *share_fd = data.fd;
return ret;
}
diff --git a/libion/kernel-headers/linux/ion.h b/libion/kernel-headers/linux/ion.h
index 5af39d0..3c28080 100644
--- a/libion/kernel-headers/linux/ion.h
+++ b/libion/kernel-headers/linux/ion.h
@@ -38,7 +38,7 @@
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
-#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
#define ION_FLAG_CACHED 1
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define ION_FLAG_CACHED_NEEDS_SYNC 2
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
new file mode 100644
index 0000000..4428848
--- /dev/null
+++ b/libion/tests/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "ion-unit-tests",
+ clang: true,
+ cflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ ],
+ shared_libs: ["libion"],
+ srcs: [
+ "ion_test_fixture.cpp",
+ "allocate_test.cpp",
+ "formerly_valid_handle_test.cpp",
+ "invalid_values_test.cpp",
+ "map_test.cpp",
+ "device_test.cpp",
+ "exit_test.cpp",
+ ],
+}
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
deleted file mode 100644
index 894f90e..0000000
--- a/libion/tests/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := ion-unit-tests
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
-LOCAL_SHARED_LIBRARIES += libion
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
-LOCAL_SRC_FILES := \
- ion_test_fixture.cpp \
- allocate_test.cpp \
- formerly_valid_handle_test.cpp \
- invalid_values_test.cpp \
- map_test.cpp \
- device_test.cpp \
- exit_test.cpp
-include $(BUILD_NATIVE_TEST)
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
index e26b302..3c4524e 100644
--- a/libion/tests/allocate_test.cpp
+++ b/libion/tests/allocate_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <memory>
#include <sys/mman.h>
#include <gtest/gtest.h>
@@ -90,7 +91,7 @@
TEST_F(Allocate, Zeroed)
{
- void *zeroes = calloc(4096, 1);
+ auto zeroes_ptr = std::make_unique<char[]>(4096);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -125,14 +126,11 @@
ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
ASSERT_TRUE(ptr != NULL);
- ASSERT_EQ(0, memcmp(ptr, zeroes, 4096));
+ ASSERT_EQ(0, memcmp(ptr, zeroes_ptr.get(), 4096));
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(zeroes);
-
}
TEST_F(Allocate, Large)
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
index 6f6e1bd..eb3f7b6 100644
--- a/libion/tests/device_test.cpp
+++ b/libion/tests/device_test.cpp
@@ -15,6 +15,7 @@
*/
#include <fcntl.h>
+#include <memory>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -45,7 +46,7 @@
void Device::SetUp()
{
IonAllHeapsTest::SetUp();
- m_deviceFd = open("/dev/ion-test", O_RDWR);
+ m_deviceFd = open("/dev/ion-test", O_RDONLY);
ASSERT_GE(m_deviceFd, 0);
}
@@ -133,8 +134,8 @@
TEST_F(Device, KernelReadCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -161,14 +162,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelWriteCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -195,14 +194,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAReadCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -227,14 +224,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAWriteCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -261,14 +256,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelReadCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -295,14 +288,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelWriteCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -329,14 +320,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAReadCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -363,14 +352,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAWriteCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -399,13 +386,11 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelRead)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -432,14 +417,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelWrite)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -466,14 +449,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMARead)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -498,14 +479,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAWrite)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -532,13 +511,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, IsCached)
{
- void *buf = malloc(4096);
+ auto buf_ptr = std::make_unique<char[]>(4096);
+ void *buf = buf_ptr.get();
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
diff --git a/liblog/Android.bp b/liblog/Android.bp
new file mode 100644
index 0000000..747fcc8
--- /dev/null
+++ b/liblog/Android.bp
@@ -0,0 +1,118 @@
+//
+// Copyright (C) 2008-2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+liblog_sources = [
+ "log_event_list.c",
+ "log_event_write.c",
+ "logger_write.c",
+ "config_write.c",
+ "logger_name.c",
+ "logger_lock.c",
+ "log_ratelimit.cpp",
+]
+liblog_host_sources = [
+ "fake_log_device.c",
+ "fake_writer.c",
+]
+liblog_target_sources = [
+ "event_tag_map.cpp",
+ "config_read.c",
+ "log_time.cpp",
+ "properties.c",
+ "logprint.c",
+ "pmsg_reader.c",
+ "pmsg_writer.c",
+ "logd_reader.c",
+ "logd_writer.c",
+ "logger_read.c",
+]
+
+// Shared and static library for host and device
+// ========================================================
+cc_library {
+ name: "liblog",
+ host_supported: true,
+
+ srcs: liblog_sources,
+
+ target: {
+ host: {
+ srcs: liblog_host_sources,
+ cflags: ["-DFAKE_LOG_DEVICE=1"],
+ },
+ android: {
+ srcs: liblog_target_sources,
+ // AddressSanitizer runtime library depends on liblog.
+ sanitize: {
+ address: false,
+ },
+ },
+ android_arm: {
+ // TODO: This is to work around b/24465209. Remove after root cause is fixed
+ ldflags: ["-Wl,--hash-style=both"],
+ },
+ windows: {
+ srcs: ["uio.c"],
+ enabled: true,
+ },
+ not_windows: {
+ srcs: ["event_tag_map.cpp"],
+ },
+ linux: {
+ host_ldlibs: ["-lrt"],
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ },
+
+ export_include_dirs: ["include"],
+
+ cflags: [
+ "-Werror",
+ "-fvisibility=hidden",
+ // This is what we want to do:
+ // liblog_cflags := $(shell \
+ // sed -n \
+ // 's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \
+ // $(LOCAL_PATH)/event.logtags)
+ // so make sure we do not regret hard-coding it as follows:
+ "-DLIBLOG_LOG_TAG=1006",
+ "-DSNET_EVENT_LOG_TAG=1397638484",
+ ],
+ logtags: ["event.logtags"],
+ compile_multilib: "both",
+}
+
+ndk_headers {
+ name: "liblog_headers",
+ from: "include/android",
+ to: "android",
+ srcs: ["include/android/log.h"],
+ license: "NOTICE",
+}
+
+cc_library_headers {
+ name: "liblog_vndk_headers",
+ export_include_dirs: ["include_vndk"],
+}
+
+ndk_library {
+ name: "liblog.ndk",
+ symbol_file: "liblog.map.txt",
+ first_version: "9",
+ unversioned_until: "current",
+}
diff --git a/liblog/Android.mk b/liblog/Android.mk
index 930dcf7..6c4dff5 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -1,90 +1,3 @@
-#
-# Copyright (C) 2008-2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
LOCAL_PATH := $(my-dir)
-include $(CLEAR_VARS)
-
-# This is what we want to do:
-# liblog_cflags := $(shell \
-# sed -n \
-# 's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \
-# $(LOCAL_PATH)/event.logtags)
-# so make sure we do not regret hard-coding it as follows:
-liblog_cflags := -DLIBLOG_LOG_TAG=1005
-
-liblog_sources := logd_write.c
-
-# some files must not be compiled when building against Mingw
-# they correspond to features not used by our host development tools
-# which are also hard or even impossible to port to native Win32
-
-ifeq ($(strip $(USE_MINGW)),)
- liblog_sources += \
- event_tag_map.c
-else
- liblog_sources += \
- uio.c
-endif
-
-liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags
-liblog_target_sources := $(liblog_sources) log_time.cpp log_is_loggable.c
-ifeq ($(strip $(USE_MINGW)),)
-liblog_target_sources += logprint.c
-endif
-liblog_target_sources += log_read.c
-
-# Shared and static library for host
-# ========================================================
-LOCAL_MODULE := liblog
-LOCAL_SRC_FILES := $(liblog_host_sources)
-LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror $(liblog_cflags)
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := liblog
-LOCAL_WHOLE_STATIC_LIBRARIES := liblog
-ifeq ($(strip $(HOST_OS)),linux)
-LOCAL_LDLIBS := -lrt
-endif
-LOCAL_MULTILIB := both
-LOCAL_CXX_STL := none
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-# Shared and static library for target
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := liblog
-LOCAL_SRC_FILES := $(liblog_target_sources)
-LOCAL_CFLAGS := -Werror $(liblog_cflags)
-# AddressSanitizer runtime library depends on liblog.
-LOCAL_SANITIZE := never
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := liblog
-LOCAL_WHOLE_STATIC_LIBRARIES := liblog
-LOCAL_CFLAGS := -Werror $(liblog_cflags)
-
-# TODO: This is to work around b/19059885. Remove after root cause is fixed
-LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
-
-LOCAL_SANITIZE := never
-LOCAL_CXX_STL := none
-
-include $(BUILD_SHARED_LIBRARY)
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/README b/liblog/README
index f29ac04..610338c 100644
--- a/liblog/README
+++ b/liblog/README
@@ -1,11 +1,17 @@
-LIBLOG(3) Android NDK Programming Manual LIBLOG(3)
+LIBLOG(3) Android Internal NDK Programming Manual LIBLOG(3)
NAME
- liblog - Android NDK logger interfaces
+ liblog - Android Internal NDK logger interfaces
SYNOPSIS
+ /*
+ * Please limit to 24 characters for runtime is loggable,
+ * 16 characters for persist is loggable, and logcat pretty
+ * alignment with limit of 7 characters.
+ */
+ #define LOG_TAG "yourtag"
#include <log/log.h>
ALOG(android_priority, tag, format, ...)
@@ -56,9 +62,7 @@
LOG_EVENT_INT(tag, value)
LOG_EVENT_LONG(tag, value)
- Link with -llog
-
- #include <log/logger.h>
+ clockid_t android_log_clockid()
log_id_t android_logger_get_id(struct logger *logger)
int android_logger_clear(struct logger *logger)
@@ -66,21 +70,44 @@
int android_logger_get_log_readable_size(struct logger *logger)
int android_logger_get_log_version(struct logger *logger)
- struct logger_list *android_logger_list_alloc(int mode, unsigned int
- tail, pid_t pid)
- struct logger *android_logger_open(struct logger_list *logger_list,
- log_id_t id)
- struct logger_list *android_logger_list_open(log_id_t id, int mode,
- unsigned int tail, pid_t pid)
-
- int android_logger_list_read(struct logger_list *logger_list, struct
- log_msg *log_msg
-
+ struct logger_list *android_logger_list_alloc(int mode,
+ unsigned int tail,
+ pid_t pid)
+ struct logger *android_logger_open(struct logger_list *logger_list,
+ log_id_t id)
+ struct logger_list *android_logger_list_open(log_id_t id, int mode,
+ unsigned int tail,
+ pid_t pid)
+ int android_logger_list_read(struct logger_list *logger_list,
+ struct log_msg *log_msg)
void android_logger_list_free(struct logger_list *logger_list)
log_id_t android_name_to_log_id(const char *logName)
const char *android_log_id_to_name(log_id_t log_id)
+ android_log_context create_android_logger(uint32_t tag)
+
+ int android_log_write_list_begin(android_log_context ctx)
+ int android_log_write_list_end(android_log_context ctx)
+
+ int android_log_write_int32(android_log_context ctx, int32_t value)
+ int android_log_write_int64(android_log_context ctx, int64_t value)
+ int android_log_write_string8(android_log_context ctx,
+ const char *value)
+ int android_log_write_string8_len(android_log_context ctx,
+ const char *value, size_t maxlen)
+ int android_log_write_float32(android_log_context ctx, float value)
+
+ int android_log_write_list(android_log_context ctx,
+ log_id_t id = LOG_ID_EVENTS)
+
+ android_log_context create_android_log_parser(const char *msg,
+ size_t len)
+ android_log_list_element android_log_read_next(android_log_context ctx)
+ android_log_list_element android_log_peek_next(android_log_context ctx)
+
+ int android_log_destroy(android_log_context *ctx)
+
Link with -llog
DESCRIPTION
@@ -116,6 +143,10 @@
code, otherwise the android_logger_list_read call will block for new
entries.
+ The ANDROID_LOG_WRAP mode flag to the android_logger_list_alloc_time
+ signals logd to quiesce the reader until the buffer is about to prune
+ at the start time then proceed to dumping content.
+
The ANDROID_LOG_PSTORE mode flag to the android_logger_open is used to
switch from the active logs to the persistent logs from before the last
reboot.
@@ -159,8 +190,8 @@
library retries on EINTR, -EINTR should never be returned.
SEE ALSO
- syslogd(8)
+ syslogd(8), klogd, auditd(8)
- 24 Jan 2014 LIBLOG(3)
+ 17 Oct 2016 LIBLOG(3)
diff --git a/liblog/config_read.c b/liblog/config_read.c
new file mode 100644
index 0000000..1f54152
--- /dev/null
+++ b/liblog/config_read.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "config_read.h"
+#include "logger.h"
+
+LIBLOG_HIDDEN struct listnode __android_log_transport_read =
+ { &__android_log_transport_read, &__android_log_transport_read };
+LIBLOG_HIDDEN struct listnode __android_log_persist_read =
+ { &__android_log_persist_read, &__android_log_persist_read };
+
+static void __android_log_add_transport(
+ struct listnode *list, struct android_log_transport_read *transport) {
+ size_t i;
+
+ /* Try to keep one functioning transport for each log buffer id */
+ for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
+ struct android_log_transport_read *transp;
+
+ if (list_empty(list)) {
+ if (!transport->available || ((*transport->available)(i) >= 0)) {
+ list_add_tail(list, &transport->node);
+ return;
+ }
+ } else {
+ read_transport_for_each(transp, list) {
+ if (!transp->available) {
+ return;
+ }
+ if (((*transp->available)(i) < 0) &&
+ (!transport->available ||
+ ((*transport->available)(i) >= 0))) {
+ list_add_tail(list, &transport->node);
+ return;
+ }
+ }
+ }
+ }
+}
+
+LIBLOG_HIDDEN void __android_log_config_read() {
+#if (FAKE_LOG_DEVICE == 0)
+ extern struct android_log_transport_read logdLoggerRead;
+ extern struct android_log_transport_read pmsgLoggerRead;
+
+ __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead);
+ __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead);
+#endif
+}
diff --git a/liblog/config_read.h b/liblog/config_read.h
new file mode 100644
index 0000000..49a3b75
--- /dev/null
+++ b/liblog/config_read.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_CONFIG_READ_H__
+#define _LIBLOG_CONFIG_READ_H__
+
+#include <cutils/list.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+extern LIBLOG_HIDDEN struct listnode __android_log_transport_read;
+extern LIBLOG_HIDDEN struct listnode __android_log_persist_read;
+
+#define read_transport_for_each(transp, transports) \
+ for ((transp) = node_to_item((transports)->next, \
+ struct android_log_transport_read, node); \
+ ((transp) != node_to_item(transports, \
+ struct android_log_transport_read, node)); \
+ (transp) = node_to_item((transp)->node.next, \
+ struct android_log_transport_read, node)) \
+
+#define read_transport_for_each_safe(transp, n, transports) \
+ for ((transp) = node_to_item((transports)->next, \
+ struct android_log_transport_read, node), \
+ (n) = (transp)->node.next; \
+ ((transp) != node_to_item(transports, \
+ struct android_log_transport_read, node)); \
+ (transp) = node_to_item(n, struct android_log_transport_read, node), \
+ (n) = (transp)->node.next)
+
+LIBLOG_HIDDEN void __android_log_config_read();
+
+__END_DECLS
+
+#endif /* _LIBLOG_CONFIG_READ_H__ */
diff --git a/liblog/config_write.c b/liblog/config_write.c
new file mode 100644
index 0000000..d689f63
--- /dev/null
+++ b/liblog/config_write.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "config_write.h"
+#include "logger.h"
+
+LIBLOG_HIDDEN struct listnode __android_log_transport_write =
+ { &__android_log_transport_write, &__android_log_transport_write };
+LIBLOG_HIDDEN struct listnode __android_log_persist_write =
+ { &__android_log_persist_write, &__android_log_persist_write};
+
+static void __android_log_add_transport(
+ struct listnode *list, struct android_log_transport_write *transport) {
+ size_t i;
+
+ /* Try to keep one functioning transport for each log buffer id */
+ for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
+ struct android_log_transport_write *transp;
+
+ if (list_empty(list)) {
+ if (!transport->available || ((*transport->available)(i) >= 0)) {
+ list_add_tail(list, &transport->node);
+ return;
+ }
+ } else {
+ write_transport_for_each(transp, list) {
+ if (!transp->available) {
+ return;
+ }
+ if (((*transp->available)(i) < 0) &&
+ (!transport->available ||
+ ((*transport->available)(i) >= 0))) {
+ list_add_tail(list, &transport->node);
+ return;
+ }
+ }
+ }
+ }
+}
+
+LIBLOG_HIDDEN void __android_log_config_write() {
+#if (FAKE_LOG_DEVICE == 0)
+ extern struct android_log_transport_write logdLoggerWrite;
+ extern struct android_log_transport_write pmsgLoggerWrite;
+
+ __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
+ __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
+#else
+ extern struct android_log_transport_write fakeLoggerWrite;
+
+ __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
+#endif
+}
diff --git a/liblog/config_write.h b/liblog/config_write.h
new file mode 100644
index 0000000..3b01a9a
--- /dev/null
+++ b/liblog/config_write.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_CONFIG_WRITE_H__
+#define _LIBLOG_CONFIG_WRITE_H__
+
+#include <cutils/list.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+extern LIBLOG_HIDDEN struct listnode __android_log_transport_write;
+extern LIBLOG_HIDDEN struct listnode __android_log_persist_write;
+
+#define write_transport_for_each(transp, transports) \
+ for ((transp) = node_to_item((transports)->next, \
+ struct android_log_transport_write, node); \
+ ((transp) != node_to_item(transports, \
+ struct android_log_transport_write, node)); \
+ (transp) = node_to_item((transp)->node.next, \
+ struct android_log_transport_write, node)) \
+
+#define write_transport_for_each_safe(transp, n, transports) \
+ for ((transp) = node_to_item((transports)->next, \
+ struct android_log_transport_write, node), \
+ (n) = (transp)->node.next; \
+ ((transp) != node_to_item(transports, \
+ struct android_log_transport_write, node)); \
+ (transp) = node_to_item(n, struct android_log_transport_write, node), \
+ (n) = (transp)->node.next)
+
+LIBLOG_HIDDEN void __android_log_config_write();
+
+__END_DECLS
+
+#endif /* _LIBLOG_CONFIG_WRITE_H__ */
diff --git a/liblog/event.logtags b/liblog/event.logtags
index 72ecab1..301e885 100644
--- a/liblog/event.logtags
+++ b/liblog/event.logtags
@@ -33,4 +33,4 @@
#
# TODO: generate ".java" and ".h" files with integer constants from this file.
-1005 liblog (dropped|1)
+1006 liblog (dropped|1)
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
deleted file mode 100644
index bea99aa..0000000
--- a/liblog/event_tag_map.c
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-
-#include <log/event_tag_map.h>
-#include <log/log.h>
-
-#define OUT_TAG "EventTagMap"
-
-/*
- * Single entry.
- */
-typedef struct EventTag {
- unsigned int tagIndex;
- const char* tagStr;
-} EventTag;
-
-/*
- * Map.
- */
-struct EventTagMap {
- /* memory-mapped source file; we get strings from here */
- void* mapAddr;
- size_t mapLen;
-
- /* array of event tags, sorted numerically by tag index */
- EventTag* tagArray;
- int numTags;
-};
-
-/* fwd */
-static int processFile(EventTagMap* map);
-static int countMapLines(const EventTagMap* map);
-static int parseMapLines(EventTagMap* map);
-static int scanTagLine(char** pData, EventTag* tag, int lineNum);
-static int sortTags(EventTagMap* map);
-
-
-/*
- * Open the map file and allocate a structure to manage it.
- *
- * We create a private mapping because we want to terminate the log tag
- * strings with '\0'.
- */
-EventTagMap* android_openEventTagMap(const char* fileName)
-{
- EventTagMap* newTagMap;
- off_t end;
- int fd = -1;
-
- newTagMap = calloc(1, sizeof(EventTagMap));
- if (newTagMap == NULL)
- return NULL;
-
- fd = open(fileName, O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "%s: unable to open map '%s': %s\n",
- OUT_TAG, fileName, strerror(errno));
- goto fail;
- }
-
- end = lseek(fd, 0L, SEEK_END);
- (void) lseek(fd, 0L, SEEK_SET);
- if (end < 0) {
- fprintf(stderr, "%s: unable to seek map '%s'\n", OUT_TAG, fileName);
- goto fail;
- }
-
- newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE,
- fd, 0);
- if (newTagMap->mapAddr == MAP_FAILED) {
- fprintf(stderr, "%s: mmap(%s) failed: %s\n",
- OUT_TAG, fileName, strerror(errno));
- goto fail;
- }
- newTagMap->mapLen = end;
-
- if (processFile(newTagMap) != 0)
- goto fail;
-
- return newTagMap;
-
-fail:
- android_closeEventTagMap(newTagMap);
- if (fd >= 0)
- close(fd);
- return NULL;
-}
-
-/*
- * Close the map.
- */
-void android_closeEventTagMap(EventTagMap* map)
-{
- if (map == NULL)
- return;
-
- munmap(map->mapAddr, map->mapLen);
- free(map);
-}
-
-/*
- * Look up an entry in the map.
- *
- * The entries are sorted by tag number, so we can do a binary search.
- */
-const char* android_lookupEventTag(const EventTagMap* map, int tag)
-{
- int hi, lo, mid;
-
- lo = 0;
- hi = map->numTags-1;
-
- while (lo <= hi) {
- int cmp;
-
- mid = (lo+hi)/2;
- cmp = map->tagArray[mid].tagIndex - tag;
- if (cmp < 0) {
- /* tag is bigger */
- lo = mid + 1;
- } else if (cmp > 0) {
- /* tag is smaller */
- hi = mid - 1;
- } else {
- /* found */
- return map->tagArray[mid].tagStr;
- }
- }
-
- return NULL;
-}
-
-
-
-/*
- * Determine whether "c" is a whitespace char.
- */
-static inline int isCharWhitespace(char c)
-{
- return (c == ' ' || c == '\n' || c == '\r' || c == '\t');
-}
-
-/*
- * Determine whether "c" is a valid tag char.
- */
-static inline int isCharValidTag(char c)
-{
- return ((c >= 'A' && c <= 'Z') ||
- (c >= 'a' && c <= 'z') ||
- (c >= '0' && c <= '9') ||
- (c == '_'));
-}
-
-/*
- * Determine whether "c" is a valid decimal digit.
- */
-static inline int isCharDigit(char c)
-{
- return (c >= '0' && c <= '9');
-}
-
-
-/*
- * Crunch through the file, parsing the contents and creating a tag index.
- */
-static int processFile(EventTagMap* map)
-{
- /* get a tag count */
- map->numTags = countMapLines(map);
- if (map->numTags < 0)
- return -1;
-
- //printf("+++ found %d tags\n", map->numTags);
-
- /* allocate storage for the tag index array */
- map->tagArray = calloc(1, sizeof(EventTag) * map->numTags);
- if (map->tagArray == NULL)
- return -1;
-
- /* parse the file, null-terminating tag strings */
- if (parseMapLines(map) != 0) {
- fprintf(stderr, "%s: file parse failed\n", OUT_TAG);
- return -1;
- }
-
- /* sort the tags and check for duplicates */
- if (sortTags(map) != 0)
- return -1;
-
- return 0;
-}
-
-/*
- * Run through all lines in the file, determining whether they're blank,
- * comments, or possibly have a tag entry.
- *
- * This is a very "loose" scan. We don't try to detect syntax errors here.
- * The later pass is more careful, but the number of tags found there must
- * match the number of tags found here.
- *
- * Returns the number of potential tag entries found.
- */
-static int countMapLines(const EventTagMap* map)
-{
- int numTags, unknown;
- const char* cp;
- const char* endp;
-
- cp = (const char*) map->mapAddr;
- endp = cp + map->mapLen;
-
- numTags = 0;
- unknown = 1;
- while (cp < endp) {
- if (*cp == '\n') {
- unknown = 1;
- } else if (unknown) {
- if (isCharDigit(*cp)) {
- /* looks like a tag to me */
- numTags++;
- unknown = 0;
- } else if (isCharWhitespace(*cp)) {
- /* might be leading whitespace before tag num, keep going */
- } else {
- /* assume comment; second pass can complain in detail */
- unknown = 0;
- }
- } else {
- /* we've made up our mind; just scan to end of line */
- }
- cp++;
- }
-
- return numTags;
-}
-
-/*
- * Parse the tags out of the file.
- */
-static int parseMapLines(EventTagMap* map)
-{
- int tagNum, lineStart, lineNum;
- char* cp;
- char* endp;
-
- cp = (char*) map->mapAddr;
- endp = cp + map->mapLen;
-
- /* insist on EOL at EOF; simplifies parsing and null-termination */
- if (*(endp-1) != '\n') {
- fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
- return -1;
- }
-
- tagNum = 0;
- lineStart = 1;
- lineNum = 1;
- while (cp < endp) {
- //printf("{%02x}", *cp); fflush(stdout);
- if (*cp == '\n') {
- lineStart = 1;
- lineNum++;
- } else if (lineStart) {
- if (*cp == '#') {
- /* comment; just scan to end */
- lineStart = 0;
- } else if (isCharDigit(*cp)) {
- /* looks like a tag; scan it out */
- if (tagNum >= map->numTags) {
- fprintf(stderr,
- "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
- return -1;
- }
- if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0)
- return -1;
- tagNum++;
- lineNum++; // we eat the '\n'
- /* leave lineStart==1 */
- } else if (isCharWhitespace(*cp)) {
- /* looks like leading whitespace; keep scanning */
- } else {
- fprintf(stderr,
- "%s: unexpected chars (0x%02x) in tag number on line %d\n",
- OUT_TAG, *cp, lineNum);
- return -1;
- }
- } else {
- /* this is a blank or comment line */
- }
- cp++;
- }
-
- if (tagNum != map->numTags) {
- fprintf(stderr, "%s: parsed %d tags, expected %d\n",
- OUT_TAG, tagNum, map->numTags);
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Scan one tag line.
- *
- * "*pData" should be pointing to the first digit in the tag number. On
- * successful return, it will be pointing to the last character in the
- * tag line (i.e. the character before the start of the next line).
- *
- * Returns 0 on success, nonzero on failure.
- */
-static int scanTagLine(char** pData, EventTag* tag, int lineNum)
-{
- char* cp = *pData;
- char* startp;
- char* endp;
- unsigned long val;
-
- startp = cp;
- while (isCharDigit(*++cp))
- ;
- *cp = '\0';
-
- val = strtoul(startp, &endp, 10);
- assert(endp == cp);
- if (endp != cp)
- fprintf(stderr, "ARRRRGH\n");
-
- tag->tagIndex = val;
-
- while (*++cp != '\n' && isCharWhitespace(*cp))
- ;
-
- if (*cp == '\n') {
- fprintf(stderr,
- "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
- return -1;
- }
-
- tag->tagStr = cp;
-
- while (isCharValidTag(*++cp))
- ;
-
- if (*cp == '\n') {
- /* null terminate and return */
- *cp = '\0';
- } else if (isCharWhitespace(*cp)) {
- /* CRLF or trailin spaces; zap this char, then scan for the '\n' */
- *cp = '\0';
-
- /* just ignore the rest of the line till \n
- TODO: read the tag description that follows the tag name
- */
- while (*++cp != '\n') {
- }
- } else {
- fprintf(stderr,
- "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
- return -1;
- }
-
- *pData = cp;
-
- //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr);
- return 0;
-}
-
-/*
- * Compare two EventTags.
- */
-static int compareEventTags(const void* v1, const void* v2)
-{
- const EventTag* tag1 = (const EventTag*) v1;
- const EventTag* tag2 = (const EventTag*) v2;
-
- return tag1->tagIndex - tag2->tagIndex;
-}
-
-/*
- * Sort the EventTag array so we can do fast lookups by tag index. After
- * the sort we do a quick check for duplicate tag indices.
- *
- * Returns 0 on success.
- */
-static int sortTags(EventTagMap* map)
-{
- int i;
-
- qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);
-
- for (i = 1; i < map->numTags; i++) {
- if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) {
- fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n",
- OUT_TAG,
- map->tagArray[i].tagIndex, map->tagArray[i].tagStr,
- map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr);
- return -1;
- }
- }
-
- return 0;
-}
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
new file mode 100644
index 0000000..1f08eb4
--- /dev/null
+++ b/liblog/event_tag_map.cpp
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <experimental/string_view>
+#include <functional>
+#include <string>
+#include <unordered_map>
+
+#include <log/event_tag_map.h>
+#include <utils/FastStrcmp.h>
+#include <utils/RWLock.h>
+#include <private/android_logger.h>
+
+#include "log_portability.h"
+#include "logd_reader.h"
+
+#define OUT_TAG "EventTagMap"
+
+class MapString {
+private:
+ const std::string* alloc; // HAS-AN
+ const std::experimental::string_view str; // HAS-A
+
+public:
+ operator const std::experimental::string_view() const { return str; }
+
+ const char* data() const { return str.data(); }
+ size_t length() const { return str.length(); }
+
+ bool operator== (const MapString& rval) const {
+ if (length() != rval.length()) return false;
+ if (length() == 0) return true;
+ return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+ }
+ bool operator!= (const MapString& rval) const {
+ return !(*this == rval);
+ }
+
+ MapString(const char* str, size_t len) : alloc(NULL), str(str, len) { }
+ explicit MapString(const std::string& str) :
+ alloc(new std::string(str)),
+ str(alloc->data(), alloc->length()) { }
+ MapString(MapString &&rval) :
+ alloc(rval.alloc),
+ str(rval.data(), rval.length()) {
+ rval.alloc = NULL;
+ }
+ explicit MapString(const MapString &rval) :
+ alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
+ str(alloc ? alloc->data() : rval.data(), rval.length()) { }
+
+ ~MapString() { if (alloc) delete alloc; }
+};
+
+// Hash for MapString
+template <> struct std::hash<MapString>
+ : public std::unary_function<const MapString&, size_t> {
+ size_t operator()(const MapString& __t) const noexcept {
+ if (!__t.length()) return 0;
+ return std::hash<std::experimental::string_view>()(std::experimental::string_view(__t));
+ }
+};
+
+typedef std::pair<MapString, MapString> TagFmt;
+
+template <> struct std::hash<TagFmt>
+ : public std::unary_function<const TagFmt&, size_t> {
+ size_t operator()(const TagFmt& __t) const noexcept {
+ // Tag is typically unique. Will cost us an extra 100ns for the
+ // unordered_map lookup if we instead did a hash that combined
+ // both of tag and fmt members, e.g.:
+ //
+ // return std::hash<MapString>()(__t.first) ^
+ // std::hash<MapString>()(__t.second);
+ return std::hash<MapString>()(__t.first);
+ }
+};
+
+// Map
+struct EventTagMap {
+# define NUM_MAPS 2
+ // memory-mapped source file; we get strings from here
+ void* mapAddr[NUM_MAPS];
+ size_t mapLen[NUM_MAPS];
+
+private:
+ std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
+ std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
+ std::unordered_map<MapString, uint32_t> Tag2Idx;
+ // protect unordered sets
+ android::RWLock rwlock;
+
+public:
+ EventTagMap() {
+ memset(mapAddr, 0, sizeof(mapAddr));
+ memset(mapLen, 0, sizeof(mapLen));
+ }
+
+ ~EventTagMap() {
+ Idx2TagFmt.clear();
+ TagFmt2Idx.clear();
+ Tag2Idx.clear();
+ for (size_t which = 0; which < NUM_MAPS; ++which) {
+ if (mapAddr[which]) {
+ munmap(mapAddr[which], mapLen[which]);
+ mapAddr[which] = 0;
+ }
+ }
+ }
+
+ bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
+ const TagFmt* find(uint32_t tag) const;
+ int find(TagFmt&& tagfmt) const;
+ int find(MapString&& tag) const;
+};
+
+bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
+ bool ret = true;
+ static const char errorFormat[] = OUT_TAG ": duplicate tag entries %" PRIu32
+ ":%.*s:%.*s and %" PRIu32
+ ":%.*s:%.*s)\n";
+ android::RWLock::AutoWLock writeLock(rwlock);
+ {
+ std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ it = Idx2TagFmt.find(tag);
+ if (it != Idx2TagFmt.end()) {
+ if (verbose) {
+ fprintf(stderr, errorFormat,
+ it->first,
+ (int)it->second.first.length(), it->second.first.data(),
+ (int)it->second.second.length(), it->second.second.data(),
+ tag,
+ (int)tagfmt.first.length(), tagfmt.first.data(),
+ (int)tagfmt.second.length(), tagfmt.second.data());
+ }
+ ret = false;
+ } else {
+ Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
+ }
+ }
+
+ {
+ std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+ it = TagFmt2Idx.find(tagfmt);
+ if (it != TagFmt2Idx.end()) {
+ if (verbose) {
+ fprintf(stderr, errorFormat,
+ it->second,
+ (int)it->first.first.length(), it->first.first.data(),
+ (int)it->first.second.length(), it->first.second.data(),
+ tag,
+ (int)tagfmt.first.length(), tagfmt.first.data(),
+ (int)tagfmt.second.length(), tagfmt.second.data());
+ }
+ ret = false;
+ } else {
+ TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
+ }
+ }
+
+ {
+ std::unordered_map<MapString, uint32_t>::const_iterator it;
+ it = Tag2Idx.find(tagfmt.first);
+ if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
+ Tag2Idx.erase(it);
+ it = Tag2Idx.end();
+ }
+ if (it == Tag2Idx.end()) {
+ Tag2Idx.emplace(std::make_pair(tagfmt.first, tag));
+ }
+ }
+
+ return ret;
+}
+
+const TagFmt* EventTagMap::find(uint32_t tag) const {
+ std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = Idx2TagFmt.find(tag);
+ if (it == Idx2TagFmt.end()) return NULL;
+ return &(it->second);
+}
+
+int EventTagMap::find(TagFmt&& tagfmt) const {
+ std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = TagFmt2Idx.find(std::move(tagfmt));
+ if (it == TagFmt2Idx.end()) return -1;
+ return it->second;
+}
+
+int EventTagMap::find(MapString&& tag) const {
+ std::unordered_map<MapString, uint32_t>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = Tag2Idx.find(std::move(tag));
+ if (it == Tag2Idx.end()) return -1;
+ return it->second;
+}
+
+// Scan one tag line.
+//
+// "*pData" should be pointing to the first digit in the tag number. On
+// successful return, it will be pointing to the last character in the
+// tag line (i.e. the character before the start of the next line).
+//
+// Returns 0 on success, nonzero on failure.
+static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
+ char* cp;
+ unsigned long val = strtoul(*pData, &cp, 10);
+ if (cp == *pData) {
+ fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
+ errno = EINVAL;
+ return -1;
+ }
+
+ uint32_t tagIndex = val;
+ if (tagIndex != val) {
+ fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
+ errno = ERANGE;
+ return -1;
+ }
+
+ while ((*++cp != '\n') && isspace(*cp)) {
+ }
+
+ if (*cp == '\n') {
+ fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
+ errno = EINVAL;
+ return -1;
+ }
+
+ const char* tag = cp;
+ // Determine whether "c" is a valid tag char.
+ while (isalnum(*++cp) || (*cp == '_')) { }
+ size_t tagLen = cp - tag;
+
+ if (!isspace(*cp)) {
+ fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
+ errno = EINVAL;
+ return -1;
+ }
+
+ while (isspace(*cp) && (*cp != '\n')) ++cp;
+ const char* fmt = NULL;
+ size_t fmtLen = 0;
+ if (*cp != '#') {
+ fmt = cp;
+ while ((*cp != '\n') && (*cp != '#')) ++cp;
+ while ((cp > fmt) && isspace(*(cp - 1))) --cp;
+ fmtLen = cp - fmt;
+ }
+
+ // KISS Only report identicals if they are global
+ // Ideally we want to check if there are identicals
+ // recorded for the same uid, but recording that
+ // unused detail in our database is too burdensome.
+ bool verbose = true;
+ while ((*cp != '#') && (*cp != '\n')) ++cp;
+ if (*cp == '#') {
+ do {
+ ++cp;
+ } while (isspace(*cp) && (*cp != '\n'));
+ verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
+ }
+
+ while (*cp != '\n') ++cp;
+#ifdef DEBUG
+ fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
+#endif
+ *pData = cp;
+
+ if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+ MapString(tag, tagLen), MapString(fmt, fmtLen))), verbose)) {
+ return 0;
+ }
+ errno = EMLINK;
+ return -1;
+}
+
+static const char* eventTagFiles[NUM_MAPS] = {
+ EVENT_TAG_MAP_FILE,
+ "/dev/event-log-tags",
+};
+
+// Parse the tags out of the file.
+static int parseMapLines(EventTagMap* map, size_t which) {
+ char* cp = static_cast<char*>(map->mapAddr[which]);
+ size_t len = map->mapLen[which];
+ char* endp = cp + len;
+
+ // insist on EOL at EOF; simplifies parsing and null-termination
+ if (!len || (*(endp - 1) != '\n')) {
+#ifdef DEBUG
+ fprintf(stderr, OUT_TAG ": map file %zu[%zu] missing EOL on last line\n",
+ which, len);
+#endif
+ if (which) { // do not propagate errors for other files
+ return 0;
+ }
+ errno = EINVAL;
+ return -1;
+ }
+
+ bool lineStart = true;
+ int lineNum = 1;
+ while (cp < endp) {
+ if (*cp == '\n') {
+ lineStart = true;
+ lineNum++;
+ } else if (lineStart) {
+ if (*cp == '#') {
+ // comment; just scan to end
+ lineStart = false;
+ } else if (isdigit(*cp)) {
+ // looks like a tag; scan it out
+ if (scanTagLine(map, &cp, lineNum) != 0) {
+ if (!which || (errno != EMLINK)) {
+ return -1;
+ }
+ }
+ lineNum++; // we eat the '\n'
+ // leave lineStart==true
+ } else if (isspace(*cp)) {
+ // looks like leading whitespace; keep scanning
+ } else {
+ fprintf(stderr,
+ OUT_TAG ": unexpected chars (0x%02x) in tag number on line %d\n",
+ *cp, lineNum);
+ errno = EINVAL;
+ return -1;
+ }
+ } else {
+ // this is a blank or comment line
+ }
+ cp++;
+ }
+
+ return 0;
+}
+
+// Open the map file and allocate a structure to manage it.
+//
+// We create a private mapping because we want to terminate the log tag
+// strings with '\0'.
+LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
+ EventTagMap* newTagMap;
+ off_t end[NUM_MAPS];
+ int save_errno, fd[NUM_MAPS];
+ size_t which;
+
+ memset(fd, -1, sizeof(fd));
+ memset(end, 0, sizeof(end));
+
+ for (which = 0; which < NUM_MAPS; ++which) {
+ const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+ fd[which] = open(tagfile, O_RDONLY | O_CLOEXEC);
+ if (fd[which] < 0) {
+ if (!which) {
+ save_errno = errno;
+ fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
+ tagfile, strerror(save_errno));
+ goto fail_errno;
+ }
+ continue;
+ }
+ end[which] = lseek(fd[which], 0L, SEEK_END);
+ save_errno = errno;
+ (void)lseek(fd[which], 0L, SEEK_SET);
+ if (!which && (end[0] < 0)) {
+ fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
+ tagfile, strerror(save_errno));
+ goto fail_close;
+ }
+ if (fileName) break; // Only allow one as specified
+ }
+
+ newTagMap = new EventTagMap;
+ if (newTagMap == NULL) {
+ save_errno = errno;
+ goto fail_close;
+ }
+
+ for (which = 0; which < NUM_MAPS; ++which) {
+ if (fd[which] >= 0) {
+ newTagMap->mapAddr[which] = mmap(NULL, end[which],
+ which ?
+ PROT_READ :
+ PROT_READ | PROT_WRITE,
+ which ?
+ MAP_SHARED :
+ MAP_PRIVATE,
+ fd[which], 0);
+ save_errno = errno;
+ close(fd[which]);
+ fd[which] = -1;
+ if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
+ (newTagMap->mapAddr[which] != NULL)) {
+ newTagMap->mapLen[which] = end[which];
+ } else if (!which) {
+ const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+ fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
+ tagfile, strerror(save_errno));
+ goto fail_unmap;
+ }
+ }
+ }
+
+ for (which = 0; which < NUM_MAPS; ++which) {
+ if (parseMapLines(newTagMap, which) != 0) {
+ delete newTagMap;
+ return NULL;
+ }
+ }
+
+ return newTagMap;
+
+fail_unmap:
+ save_errno = EINVAL;
+ delete newTagMap;
+fail_close:
+ for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+fail_errno:
+ errno = save_errno;
+ return NULL;
+}
+
+// Close the map.
+LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) {
+ if (map) delete map;
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
+ size_t *len,
+ unsigned int tag) {
+ if (len) *len = 0;
+ const TagFmt* str = map->find(tag);
+ if (!str) return NULL;
+ if (len) *len = str->first.length();
+ return str->first.data();
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
+ const EventTagMap* map, size_t *len, unsigned int tag) {
+ if (len) *len = 0;
+ const TagFmt* str = map->find(tag);
+ if (!str) return NULL;
+ if (len) *len = str->second.length();
+ return str->second.data();
+}
+
+// This function is deprecated and replaced with android_lookupEventTag_len
+// since it will cause the map to change from Shared and backed by a file,
+// to Private Dirty and backed up by swap, albeit highly compressible. By
+// deprecating this function everywhere, we save 100s of MB of memory space.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
+ unsigned int tag) {
+ size_t len;
+ const char* tagStr = android_lookupEventTag_len(map, &len, tag);
+
+ if (!tagStr) return tagStr;
+ char* cp = const_cast<char*>(tagStr);
+ cp += len;
+ if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
+ return tagStr;
+}
+
+// Look up tagname, generate one if necessary, and return a tag
+LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
+ const char* tagname,
+ const char* format,
+ int prio) {
+ size_t len = strlen(tagname);
+ if (!len) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
+ !__android_log_is_loggable_len(prio, tagname, len,
+ __android_log_is_debuggable() ?
+ ANDROID_LOG_VERBOSE :
+ ANDROID_LOG_DEBUG)) {
+ errno = EPERM;
+ return -1;
+ }
+
+ if (!format) format="";
+ ssize_t fmtLen = strlen(format);
+ int ret = map->find(TagFmt(std::make_pair(MapString(tagname, len),
+ MapString(format, fmtLen))));
+ if (ret != -1) return ret;
+
+ // call event tag service to arrange for a new tag
+ char *buf = NULL;
+ // Can not use android::base::StringPrintf, asprintf + free instead.
+ static const char command_template[] = "getEventTag name=%s format=\"%s\"";
+ ret = asprintf(&buf, command_template, tagname, format);
+ if (ret > 0) {
+ // Add some buffer margin for an estimate of the full return content.
+ char *cp;
+ size_t size = ret - strlen(command_template) +
+ strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
+ if (size > (size_t)ret) {
+ cp = static_cast<char*>(realloc(buf, size));
+ if (cp) {
+ buf = cp;
+ } else {
+ size = ret;
+ }
+ } else {
+ size = ret;
+ }
+ // Ask event log tag service for an allocation
+ if (__send_log_msg(buf, size) >= 0) {
+ buf[size - 1] = '\0';
+ unsigned long val = strtoul(buf, &cp, 10); // return size
+ if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
+ val = strtoul(cp + 1, &cp, 10); // allocated tag number
+ if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
+ free(buf);
+ ret = val;
+ // cache
+ map->emplaceUnique(ret, TagFmt(std::make_pair(
+ MapString(std::string(tagname, len)),
+ MapString(std::string(format, fmtLen)))));
+ return ret;
+ }
+ }
+ }
+ free(buf);
+ }
+
+ // Hail Mary
+ ret = map->find(MapString(tagname, len));
+ if (ret == -1) errno = ESRCH;
+ return ret;
+}
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index 8a8ece2..957129e 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -19,23 +19,23 @@
* passed on to the underlying (fake) log device. When not in the
* simulator, messages are printed to stderr.
*/
-#include "fake_log_device.h"
-
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <log/logd.h>
-
#if !defined(_WIN32)
#include <pthread.h>
#endif
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
+#include <android/log.h>
+#include <log/uio.h>
+
+#include "fake_log_device.h"
+#include "log_portability.h"
#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
@@ -69,7 +69,7 @@
int fakeFd;
/* a printable name for this fake device */
- char *debugName;
+ char debugName[sizeof("/dev/log/security")];
/* nonzero if this is a binary log */
int isBinary;
@@ -99,6 +99,10 @@
static void 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(&fakeLogDeviceLock);
}
@@ -106,9 +110,12 @@
{
pthread_mutex_unlock(&fakeLogDeviceLock);
}
+
#else // !defined(_WIN32)
+
#define lock() ((void)0)
#define unlock() ((void)0)
+
#endif // !defined(_WIN32)
@@ -116,8 +123,8 @@
* File descriptor management.
*/
#define FAKE_FD_BASE 10000
-#define MAX_OPEN_LOGS 16
-static LogState *openLogTable[MAX_OPEN_LOGS];
+#define MAX_OPEN_LOGS 8
+static LogState openLogTable[MAX_OPEN_LOGS];
/*
* Allocate an fd and associate a new LogState with it.
@@ -127,11 +134,10 @@
{
size_t i;
- for (i = 0; i < sizeof(openLogTable); i++) {
- if (openLogTable[i] == NULL) {
- openLogTable[i] = calloc(1, sizeof(LogState));
- openLogTable[i]->fakeFd = FAKE_FD_BASE + i;
- return openLogTable[i];
+ for (i = 0; i < (sizeof(openLogTable) / sizeof(openLogTable[0])); i++) {
+ if (openLogTable[i].fakeFd == 0) {
+ openLogTable[i].fakeFd = FAKE_FD_BASE + i;
+ return &openLogTable[i];
}
}
return NULL;
@@ -143,7 +149,7 @@
static LogState *fdToLogState(int fd)
{
if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
- return openLogTable[fd - FAKE_FD_BASE];
+ return &openLogTable[fd - FAKE_FD_BASE];
}
return NULL;
}
@@ -159,9 +165,7 @@
ls = fdToLogState(fd);
if (ls != NULL) {
- openLogTable[fd - FAKE_FD_BASE] = NULL;
- free(ls->debugName);
- free(ls);
+ memset(&openLogTable[fd - FAKE_FD_BASE], 0, sizeof(openLogTable[0]));
}
unlock();
@@ -184,10 +188,12 @@
{
static const int kDevLogLen = sizeof("/dev/log/") - 1;
- logState->debugName = strdup(pathName);
+ strncpy(logState->debugName, pathName, sizeof(logState->debugName));
+ logState->debugName[sizeof(logState->debugName) - 1] = '\0';
/* identify binary logs */
- if (strcmp(pathName + kDevLogLen, "events") == 0) {
+ if (!strcmp(pathName + kDevLogLen, "events") ||
+ !strcmp(pathName + kDevLogLen, "security")) {
logState->isBinary = 1;
}
@@ -211,8 +217,7 @@
i = 0;
while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
- i < kMaxTagLen)
- {
+ i < kMaxTagLen) {
tagName[i++] = *tags++;
}
if (i == kMaxTagLen) {
@@ -313,9 +318,9 @@
};
int idx;
- idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
+ idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
if (idx < 0 ||
- idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+ idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
return "?unknown?";
return priorityStrings[idx];
}
@@ -360,7 +365,11 @@
char prefixBuf[128], suffixBuf[128];
char priChar;
time_t when;
+#if !defined(_WIN32)
pid_t pid, tid;
+#else
+ uint32_t pid, tid;
+#endif
TRACE("LOG %d: %s %s", logPrio, tag, msg);
@@ -443,13 +452,15 @@
while (p < end) {
if (*p++ == '\n') numLines++;
}
- if (p > msg && *(p-1) != '\n') numLines++;
+ if (p > msg && *(p-1) != '\n') {
+ numLines++;
+ }
/*
* Create an array of iovecs large enough to write all of
* the lines with a prefix and a suffix.
*/
- const size_t INLINE_VECS = 6;
+ const size_t INLINE_VECS = 64;
const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*));
struct iovec stackVec[INLINE_VECS];
struct iovec* vec = stackVec;
@@ -458,13 +469,13 @@
if (numLines > MAX_LINES)
numLines = MAX_LINES;
- numVecs = numLines*3; // 3 iovecs per line.
+ numVecs = numLines * 3; // 3 iovecs per line.
if (numVecs > INLINE_VECS) {
vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs);
if (vec == NULL) {
msg = "LOG: write failed, no memory";
- numVecs = 3;
- numLines = 1;
+ numVecs = INLINE_VECS;
+ numLines = numVecs / 3;
vec = stackVec;
}
}
@@ -483,7 +494,9 @@
v++;
}
const char* start = p;
- while (p < end && *p != '\n') p++;
+ while (p < end && *p != '\n') {
+ p++;
+ }
if ((p-start) > 0) {
v->iov_base = (void*)start;
v->iov_len = p-start;
@@ -499,7 +512,7 @@
}
numLines -= 1;
}
-
+
/*
* Write the entire message to the log file with a single writev() call.
* We need to use this rather than a collection of printf()s on a FILE*
@@ -518,10 +531,10 @@
int cc = writev(fileno(stderr), vec, v-vec);
if (cc == totalLen) break;
-
+
if (cc < 0) {
if(errno == EINTR) continue;
-
+
/* can't really log the failure; for now, throw out a stderr */
fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
break;
@@ -670,7 +683,7 @@
}
}
-int fakeLogOpen(const char *pathName, int flags)
+LIBLOG_HIDDEN int fakeLogOpen(const char *pathName, int flags)
{
if (redirectOpen == NULL) {
setRedirects();
@@ -678,20 +691,54 @@
return redirectOpen(pathName, flags);
}
-int fakeLogClose(int fd)
+/*
+ * The logger API has no means or need to 'stop' or 'close' using the logs,
+ * and as such, there is no way for that 'stop' or 'close' to translate into
+ * a close operation to the fake log handler. fakeLogClose is provided for
+ * completeness only.
+ *
+ * We have no intention of adding a log close operation as it would complicate
+ * every user of the logging API with no gain since the only valid place to
+ * call is in the exit handler. Logging can continue in the exit handler to
+ * help debug HOST tools ...
+ */
+LIBLOG_HIDDEN int fakeLogClose(int fd)
{
/* Assume that open() was called first. */
return redirectClose(fd);
}
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count)
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd,
+ const struct iovec* vector, int count)
{
/* Assume that open() was called first. */
return redirectWritev(fd, vector, count);
}
-int __android_log_is_loggable(int prio, const char *tag __unused, int def)
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf __unused,
+ size_t buf_size __unused)
+{
+ return -ENODEV;
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
+ const char *tag __unused,
+ int def)
{
int logLevel = def;
return logLevel >= 0 && prio >= logLevel;
}
+
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio,
+ const char *tag __unused,
+ size_t len __unused,
+ int def)
+{
+ int logLevel = def;
+ return logLevel >= 0 && prio >= logLevel;
+}
+
+LIBLOG_ABI_PRIVATE int __android_log_is_debuggable()
+{
+ return 1;
+}
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 9d168cd..4529b5d 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -19,10 +19,13 @@
#include <sys/types.h>
+#include "log_portability.h"
+
struct iovec;
-int fakeLogOpen(const char *pathName, int flags);
-int fakeLogClose(int fd);
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
+LIBLOG_HIDDEN int fakeLogOpen(const char *pathName, int flags);
+LIBLOG_HIDDEN int fakeLogClose(int fd);
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd,
+ const struct iovec* vector, int count);
#endif // _LIBLOG_FAKE_LOG_DEVICE_H
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c
new file mode 100644
index 0000000..dab8bc5
--- /dev/null
+++ b/liblog/fake_writer.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include "config_write.h"
+#include "fake_log_device.h"
+#include "log_portability.h"
+#include "logger.h"
+
+static int fakeOpen();
+static void fakeClose();
+static int fakeWrite(log_id_t log_id, struct timespec *ts,
+ struct iovec *vec, size_t nr);
+
+static int logFds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 };
+
+LIBLOG_HIDDEN struct android_log_transport_write fakeLoggerWrite = {
+ .node = { &fakeLoggerWrite.node, &fakeLoggerWrite.node },
+ .context.private = &logFds,
+ .name = "fake",
+ .available = NULL,
+ .open = fakeOpen,
+ .close = fakeClose,
+ .write = fakeWrite,
+};
+
+static int fakeOpen() {
+ int i;
+
+ for (i = 0; i < LOG_ID_MAX; i++) {
+ char buf[sizeof("/dev/log_security")];
+ snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
+ logFds[i] = fakeLogOpen(buf, O_WRONLY);
+ }
+ return 0;
+}
+
+static void fakeClose() {
+ int i;
+
+ for (i = 0; i < LOG_ID_MAX; i++) {
+ fakeLogClose(logFds[i]);
+ logFds[i] = -1;
+ }
+}
+
+static int fakeWrite(log_id_t log_id, struct timespec *ts __unused,
+ struct iovec *vec, size_t nr)
+{
+ ssize_t ret;
+ int logFd;
+
+ if (/*(int)log_id >= 0 &&*/ (int)log_id >= (int)LOG_ID_MAX) {
+ return -EBADF;
+ }
+
+ logFd = logFds[(int)log_id];
+ ret = TEMP_FAILURE_RETRY(fakeLogWritev(logFd, vec, nr));
+ if (ret < 0) {
+ ret = -errno;
+ }
+
+ return ret;
+}
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
new file mode 100644
index 0000000..9f198fe
--- /dev/null
+++ b/liblog/include/android/log.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_LOG_H
+#define _ANDROID_LOG_H
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit) since
+ * platform release 1.5
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/*
+ * Support routines to send messages to the Android in-kernel log buffer,
+ * which can later be accessed through the 'logcat' utility.
+ *
+ * Each log message must have
+ * - a priority
+ * - a log tag
+ * - some text
+ *
+ * The tag normally corresponds to the component that emits the log message,
+ * and should be reasonably small.
+ *
+ * Log message text may be truncated to less than an implementation-specific
+ * limit (e.g. 1023 characters max).
+ *
+ * Note that a newline character ("\n") will be appended automatically to your
+ * log message, if not already there. It is not possible to send several messages
+ * and have them appear on a single line in logcat.
+ *
+ * PLEASE USE LOGS WITH MODERATION:
+ *
+ * - Sending log messages eats CPU and slow down your application and the
+ * system.
+ *
+ * - The circular log buffer is pretty small (<64KB), sending many messages
+ * might push off other important log messages from the rest of the system.
+ *
+ * - In release builds, only send log messages to account for exceptional
+ * conditions.
+ *
+ * NOTE: These functions MUST be implemented by /system/lib/liblog.so
+ */
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Android log priority values, in ascending priority order.
+ */
+typedef enum android_LogPriority {
+ ANDROID_LOG_UNKNOWN = 0,
+ ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+ ANDROID_LOG_VERBOSE,
+ ANDROID_LOG_DEBUG,
+ ANDROID_LOG_INFO,
+ ANDROID_LOG_WARN,
+ ANDROID_LOG_ERROR,
+ ANDROID_LOG_FATAL,
+ ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_write(int prio, const char* tag, const char* text);
+
+/*
+ * Send a formatted string to the log, used like printf(fmt,...)
+ */
+int __android_log_print(int prio, const char* tag, const char* fmt, ...)
+#if defined(__GNUC__)
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+ __attribute__ ((__format__(gnu_printf, 3, 4)))
+#else
+ __attribute__ ((__format__(printf, 3, 4)))
+#endif
+#else
+ __attribute__ ((__format__(printf, 3, 4)))
+#endif
+#endif
+ ;
+
+/*
+ * A variant of __android_log_print() that takes a va_list to list
+ * additional parameters.
+ */
+int __android_log_vprint(int prio, const char* tag,
+ const char* fmt, va_list ap)
+#if defined(__GNUC__)
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+ __attribute__ ((__format__(gnu_printf, 3, 0)))
+#else
+ __attribute__ ((__format__(printf, 3, 0)))
+#endif
+#else
+ __attribute__ ((__format__(printf, 3, 0)))
+#endif
+#endif
+ ;
+
+/*
+ * Log an assertion failure and abort the process to have a chance
+ * to inspect it if a debugger is attached. This uses the FATAL priority.
+ */
+void __android_log_assert(const char* cond, const char* tag,
+ const char* fmt, ...)
+#if defined(__GNUC__)
+ __attribute__ ((__noreturn__))
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+ __attribute__ ((__format__(gnu_printf, 3, 4)))
+#else
+ __attribute__ ((__format__(printf, 3, 4)))
+#endif
+#else
+ __attribute__ ((__format__(printf, 3, 4)))
+#endif
+#endif
+ ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ANDROID_LOG_H */
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
new file mode 100644
index 0000000..e57e47b
--- /dev/null
+++ b/liblog/include/log/event_tag_map.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
+#define _LIBS_CUTILS_EVENTTAGMAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
+
+struct EventTagMap;
+typedef struct EventTagMap EventTagMap;
+
+/*
+ * Open the specified file as an event log tag map.
+ *
+ * Returns NULL on failure.
+ */
+EventTagMap* android_openEventTagMap(const char* fileName);
+
+/*
+ * Close the map.
+ */
+void android_closeEventTagMap(EventTagMap* map);
+
+/*
+ * Look up a tag by index. Returns the tag string, or NULL if not found.
+ */
+const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag)
+ __attribute__((deprecated("use android_lookupEventTag_len() instead to minimize MAP_PRIVATE copy-on-write memory impact")));
+
+/*
+ * Look up a tag by index. Returns the tag string & string length, or NULL if
+ * not found. Returned string is not guaranteed to be nul terminated.
+ */
+const char* android_lookupEventTag_len(const EventTagMap* map,
+ size_t* len, unsigned int tag);
+
+/*
+ * Look up a format by index. Returns the format string & string length,
+ * or NULL if not found. Returned string is not guaranteed to be nul terminated.
+ */
+const char* android_lookupEventFormat_len(const EventTagMap* map,
+ size_t* len, unsigned int tag);
+
+/*
+ * Look up tagname, generate one if necessary, and return a tag
+ */
+int android_lookupEventTagNum(EventTagMap* map, const char* tagname,
+ const char* format, int prio);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
new file mode 100644
index 0000000..db22211
--- /dev/null
+++ b/liblog/include/log/log.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2005-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_LOG_H
+#define _LIBS_LOG_LOG_H
+
+/* Too many in the ecosystem assume these are included */
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+#include <stdint.h> /* uint16_t, int32_t */
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <log/log_id.h>
+#include <log/log_main.h>
+#include <log/log_radio.h>
+#include <log/log_read.h>
+#include <log/log_system.h>
+#include <log/log_time.h>
+#include <log/uio.h> /* helper to define iovec for portability */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * LOG_TAG is the local tag used for the following simplified
+ * logging macros. You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG. You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
+ * work around issues with debug-only syntax errors in assertions
+ * that are missing format strings. See commit
+ * 19299904343daf191267564fe32e6cd5c165cd42
+ */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Event logging.
+ */
+
+/*
+ * The following should not be used directly.
+ */
+
+int __android_log_bwrite(int32_t tag, const void* payload, size_t len);
+int __android_log_btwrite(int32_t tag, char type, const void* payload,
+ size_t len);
+int __android_log_bswrite(int32_t tag, const char* payload);
+
+#define android_bWriteLog(tag, payload, len) \
+ __android_log_bwrite(tag, payload, len)
+#define android_btWriteLog(tag, type, payload, len) \
+ __android_log_btwrite(tag, type, payload, len)
+
+/*
+ * Event log entry types.
+ */
+#ifndef __AndroidEventLogType_defined
+#define __AndroidEventLogType_defined
+typedef enum {
+ /* Special markers for android_log_list_element type */
+ EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */
+ EVENT_TYPE_UNKNOWN = '?', /* protocol error */
+
+ /* must match with declaration in java/android/android/util/EventLog.java */
+ EVENT_TYPE_INT = 0, /* int32_t */
+ EVENT_TYPE_LONG = 1, /* int64_t */
+ EVENT_TYPE_STRING = 2,
+ EVENT_TYPE_LIST = 3,
+ EVENT_TYPE_FLOAT = 4,
+} AndroidEventLogType;
+#endif
+#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
+#define typeof_AndroidEventLogType unsigned char
+
+#ifndef LOG_EVENT_INT
+#define LOG_EVENT_INT(_tag, _value) { \
+ int intBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \
+ sizeof(intBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_LONG
+#define LOG_EVENT_LONG(_tag, _value) { \
+ long long longBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \
+ sizeof(longBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_FLOAT
+#define LOG_EVENT_FLOAT(_tag, _value) { \
+ float floatBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \
+ sizeof(floatBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_STRING
+#define LOG_EVENT_STRING(_tag, _value) \
+ (void) __android_log_bswrite(_tag, _value);
+#endif
+
+#ifdef __linux__
+
+#ifndef __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
+#elif __ANDROID_API__ > 22 /* > Lollipop */
+#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
+clockid_t android_log_clockid();
+#endif
+
+#endif /* __linux__ */
+
+/* --------------------------------------------------------------------- */
+
+#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
+#elif __ANDROID_API__ > 22 /* > Lollipop */
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
+
+#define android_errorWriteLog(tag, subTag) \
+ __android_log_error_write(tag, subTag, -1, NULL, 0)
+
+#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
+ __android_log_error_write(tag, subTag, uid, data, dataLen)
+
+int __android_log_error_write(int tag, const char* subTag, int32_t uid,
+ const char* data, uint32_t dataLen);
+
+#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
+
+/* --------------------------------------------------------------------- */
+
+#ifndef __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
+#elif __ANDROID_API__ > 18 /* > JellyBean */
+#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
+/*
+ * Release any logger resources (a new log write will immediately re-acquire)
+ *
+ * May be used to clean up File descriptors after a Fork, the resources are
+ * all O_CLOEXEC so wil self clean on exec().
+ */
+void __android_log_close();
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
+#elif __ANDROID_API__ > 25 /* > OC */
+#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
+
+/*
+ * if last is NULL, caller _must_ provide a consistent value for seconds.
+ *
+ * Return -1 if we can not acquire a lock, which below will permit the logging,
+ * error on allowing a log message through.
+ */
+int __android_log_ratelimit(time_t seconds, time_t* last);
+
+/*
+ * Usage:
+ *
+ * // Global default and state
+ * IF_ALOG_RATELIMIT() {
+ * ALOG*(...);
+ * }
+ *
+ * // local state, 10 seconds ratelimit
+ * static time_t local_state;
+ * IF_ALOG_RATELIMIT_LOCAL(10, &local_state) {
+ * ALOG*(...);
+ * }
+ */
+
+#define IF_ALOG_RATELIMIT() \
+ if (__android_log_ratelimit(0, NULL) > 0)
+#define IF_ALOG_RATELIMIT_LOCAL(seconds, state) \
+ if (__android_log_ratelimit(seconds, state) > 0)
+
+#else
+
+/* No ratelimiting as API unsupported */
+#define IF_ALOG_RATELIMIT() if (1)
+#define IF_ALOG_RATELIMIT_LOCAL(...) if (1)
+
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_LOG_H */
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
new file mode 100644
index 0000000..31d49b2
--- /dev/null
+++ b/liblog/include/log/log_event_list.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2005-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_EVENT_LIST_H
+#define _LIBS_LOG_EVENT_LIST_H
+
+#include <stdint.h>
+
+#if (defined(__cplusplus) && defined(_USING_LIBCXX))
+extern "C++" {
+#include <string>
+}
+#endif
+
+#include <log/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_EVENT_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
+#elif __ANDROID_API__ > 23 /* > Marshmallow */
+#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_EVENT_INTERFACE
+
+/* For manipulating lists of events. */
+
+#define ANDROID_MAX_LIST_NEST_DEPTH 8
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+#ifndef __android_log_context_defined
+#define __android_log_context_defined
+typedef struct android_log_context_internal* android_log_context;
+#endif
+
+/*
+ * Elements returned when reading a list of events.
+ */
+#ifndef __android_log_list_element_defined
+#define __android_log_list_element_defined
+typedef struct {
+ AndroidEventLogType type;
+ uint16_t complete;
+ uint16_t len;
+ union {
+ int32_t int32;
+ int64_t int64;
+ char* string;
+ float float32;
+ } data;
+} android_log_list_element;
+#endif
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ * elements, we will manufacturer a list to embrace it for your API
+ * convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char* value);
+int android_log_write_string8_len(android_log_context ctx,
+ const char* value, size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/*
+ * Creates a context from a raw buffer representing a list of events to be read.
+ */
+android_log_context create_android_log_parser(const char* msg, size_t len);
+
+android_log_list_element android_log_read_next(android_log_context ctx);
+android_log_list_element android_log_peek_next(android_log_context ctx);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context* ctx);
+
+#ifdef __cplusplus
+#ifndef __class_android_log_event_list_defined
+#define __class_android_log_event_list_defined
+/* android_log_list C++ helpers */
+extern "C++" {
+class android_log_event_list {
+friend class __android_log_event_list;
+
+private:
+ android_log_context ctx;
+ int ret;
+
+ android_log_event_list(const android_log_event_list&) = delete;
+ void operator =(const android_log_event_list&) = delete;
+
+public:
+ explicit android_log_event_list(int tag) : ret(0) {
+ ctx = create_android_logger(static_cast<uint32_t>(tag));
+ }
+ explicit android_log_event_list(log_msg& log_msg) : ret(0) {
+ ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
+ log_msg.entry.len - sizeof(uint32_t));
+ }
+ ~android_log_event_list() { android_log_destroy(&ctx); }
+
+ 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; }
+
+ 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;
+ }
+
+ android_log_event_list& operator <<(int32_t value) {
+ int retval = android_log_write_int32(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ android_log_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;
+ }
+
+ android_log_event_list& operator <<(int64_t value) {
+ int retval = android_log_write_int64(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ android_log_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;
+ }
+
+ android_log_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)
+ android_log_event_list& operator <<(const std::string& value) {
+ int retval = android_log_write_string8_len(ctx,
+ value.data(),
+ value.length());
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+#endif
+
+ android_log_event_list& operator <<(float value) {
+ int retval = android_log_write_float32(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ int write(log_id_t id = LOG_ID_EVENTS) {
+ int retval = android_log_write_list(ctx, id);
+ if (retval < 0) ret = retval;
+ return ret;
+ }
+
+ int operator <<(log_id_t id) {
+ int retval = android_log_write_list(ctx, id);
+ if (retval < 0) ret = retval;
+ android_log_destroy(&ctx);
+ 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
+
+#endif /* __ANDROID_USE_LIBLOG_EVENT_INTERFACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_EVENT_LIST_H */
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
new file mode 100644
index 0000000..3078e4e
--- /dev/null
+++ b/liblog/include/log/log_id.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_LOG_ID_H
+#define _LIBS_LOG_LOG_ID_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef log_id_t_defined
+#define log_id_t_defined
+typedef enum log_id {
+ LOG_ID_MIN = 0,
+
+ LOG_ID_MAIN = 0,
+ LOG_ID_RADIO = 1,
+ LOG_ID_EVENTS = 2,
+ LOG_ID_SYSTEM = 3,
+ LOG_ID_CRASH = 4,
+ LOG_ID_SECURITY = 5,
+ LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
+
+ LOG_ID_MAX
+} log_id_t;
+#endif
+#define sizeof_log_id_t sizeof(typeof_log_id_t)
+#define typeof_log_id_t unsigned char
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text);
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
+#if defined(__GNUC__)
+ __attribute__((__format__(printf, 4, 5)))
+#endif
+ ;
+
+/*
+ * log_id_t helpers
+ */
+log_id_t android_name_to_log_id(const char* logName);
+const char* android_log_id_to_name(log_id_t log_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_LOG_ID_H */
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
new file mode 100644
index 0000000..f45397a
--- /dev/null
+++ b/liblog/include/log/log_main.h
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_LOG_MAIN_H
+#define _LIBS_LOG_LOG_MAIN_H
+
+#include <android/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG. You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
+ * work around issues with debug-only syntax errors in assertions
+ * that are missing format strings. See commit
+ * 19299904343daf191267564fe32e6cd5c165cd42
+ */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+#define android_writeLog(prio, tag, text) \
+ __android_log_write(prio, tag, text)
+
+#define android_printLog(prio, tag, ...) \
+ __android_log_print(prio, tag, __VA_ARGS__)
+
+#define android_vprintLog(prio, cond, tag, ...) \
+ __android_log_vprint(prio, tag, __VA_ARGS__)
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef LOG_PRI
+#define LOG_PRI(priority, tag, ...) \
+ android_printLog(priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to pass in a varargs ("args" is a va_list).
+ */
+#ifndef LOG_PRI_VA
+#define LOG_PRI_VA(priority, tag, fmt, args) \
+ android_vprintLog(priority, NULL, tag, fmt, args)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/* XXX Macros to work around syntax errors in places where format string
+ * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
+ * (happens only in debug builds).
+ */
+
+/* Returns 2nd arg. Used to substitute default value if caller's vararg list
+ * is empty.
+ */
+#define __android_second(dummy, second, ...) second
+
+/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
+ * returns nothing.
+ */
+#define __android_rest(first, ...) , ## __VA_ARGS__
+
+#define android_printAssert(cond, tag, ...) \
+ __android_log_assert(cond, tag, \
+ __android_second(0, ## __VA_ARGS__, NULL) __android_rest(__VA_ARGS__))
+
+/*
+ * Log a fatal error. If the given condition fails, this stops program
+ * execution like a normal assertion, but also generating the given message.
+ * It is NOT stripped from release builds. Note that the condition test
+ * is -inverted- from the normal assert() semantics.
+ */
+#ifndef LOG_ALWAYS_FATAL_IF
+#define LOG_ALWAYS_FATAL_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+#ifndef LOG_ALWAYS_FATAL
+#define LOG_ALWAYS_FATAL(...) \
+ ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) )
+#endif
+
+/*
+ * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+
+#if LOG_NDEBUG
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) ((void)0)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) ((void)0)
+#endif
+
+#else
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
+#endif
+
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds. Uses the current LOG_TAG.
+ */
+#ifndef ALOG_ASSERT
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * C/C++ logging functions. See the logging documentation for API details.
+ *
+ * We'd like these to be available from C code (in case we import some from
+ * somewhere), so this has a C interface.
+ *
+ * The output will be correct when the log file is shared between multiple
+ * threads and/or multiple processes so long as the operating system
+ * supports O_APPEND. These calls have mutex-protected data structures
+ * and so are NOT reentrant. Do not use LOG in a signal handler.
+ */
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#ifndef ALOGV
+#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define ALOGV(...) do { if (0) { __ALOGV(__VA_ARGS__); } } while (0)
+#else
+#define ALOGV(...) __ALOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef ALOGV_IF
+#if LOG_NDEBUG
+#define ALOGV_IF(cond, ...) ((void)0)
+#else
+#define ALOGV_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug log message using the current LOG_TAG.
+ */
+#ifndef ALOGD
+#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGD_IF
+#define ALOGD_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current LOG_TAG.
+ */
+#ifndef ALOGI
+#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGI_IF
+#define ALOGI_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current LOG_TAG.
+ */
+#ifndef ALOGW
+#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGW_IF
+#define ALOGW_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current LOG_TAG.
+ */
+#ifndef ALOGE
+#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGE_IF
+#define ALOGE_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * verbose priority.
+ */
+#ifndef IF_ALOGV
+#if LOG_NDEBUG
+#define IF_ALOGV() if (false)
+#else
+#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
+#endif
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * debug priority.
+ */
+#ifndef IF_ALOGD
+#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * info priority.
+ */
+#ifndef IF_ALOGI
+#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * warn priority.
+ */
+#ifndef IF_ALOGW
+#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * error priority.
+ */
+#ifndef IF_ALOGE
+#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ * ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ */
+#ifndef ALOG
+#define ALOG(priority, tag, ...) \
+ LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Conditional given a desired logging priority and tag.
+ */
+#ifndef IF_ALOG
+#define IF_ALOG(priority, tag) \
+ if (android_testLog(ANDROID_##priority, tag))
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
+ * android_testLog will remain constant in its purpose as a wrapper
+ * for Android logging filter policy, and can be subject to
+ * change. It can be reused by the developers that override
+ * IF_ALOG as a convenient means to reimplement their policy
+ * over Android.
+ */
+
+#ifndef __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 2
+#elif __ANDROID_API__ > 24 /* > Nougat */
+#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 2
+#elif __ANDROID_API__ > 22 /* > Lollipop */
+#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE
+
+/*
+ * Use the per-tag properties "log.tag.<tagname>" to generate a runtime
+ * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
+ * ANDROID_LOG_FATAL. default_prio if no property. Undefined behavior if
+ * any other value.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio);
+
+#if __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE > 1
+#include <sys/types.h>
+
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len,
+ int default_prio);
+
+#if LOG_NDEBUG /* Production */
+#define android_testLog(prio, tag) \
+ (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+ ANDROID_LOG_DEBUG) != 0)
+#else
+#define android_testLog(prio, tag) \
+ (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+ ANDROID_LOG_VERBOSE) != 0)
+#endif
+
+#else
+
+#if LOG_NDEBUG /* Production */
+#define android_testLog(prio, tag) \
+ (__android_log_is_loggable(prio, tag, ANDROID_LOG_DEBUG) != 0)
+#else
+#define android_testLog(prio, tag) \
+ (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
+#endif
+
+#endif
+
+#else /* __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE */
+
+#define android_testLog(prio, tag) (1)
+
+#endif /* !__ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE */
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
new file mode 100644
index 0000000..30a73f2
--- /dev/null
+++ b/liblog/include/log/log_radio.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_LOG_RADIO_H
+#define _LIBS_LOG_LOG_RADIO_H
+
+#include <android/log.h>
+#include <log/log_id.h>
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG. You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Simplified macro to send a verbose radio log message using current LOG_TAG.
+ */
+#ifndef RLOGV
+#define __RLOGV(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define RLOGV(...) do { if (0) { __RLOGV(__VA_ARGS__); } } while (0)
+#else
+#define RLOGV(...) __RLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef RLOGV_IF
+#if LOG_NDEBUG
+#define RLOGV_IF(cond, ...) ((void)0)
+#else
+#define RLOGV_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug radio log message using current LOG_TAG.
+ */
+#ifndef RLOGD
+#define RLOGD(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGD_IF
+#define RLOGD_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info radio log message using current LOG_TAG.
+ */
+#ifndef RLOGI
+#define RLOGI(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGI_IF
+#define RLOGI_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning radio log message using current LOG_TAG.
+ */
+#ifndef RLOGW
+#define RLOGW(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGW_IF
+#define RLOGW_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error radio log message using current LOG_TAG.
+ */
+#ifndef RLOGE
+#define RLOGE(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGE_IF
+#define RLOGE_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+#endif /* _LIBS_LOG_LOG_RADIO_H */
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
new file mode 100644
index 0000000..5b5eebc
--- /dev/null
+++ b/liblog/include/log/log_read.h
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_LOG_READ_H
+#define _LIBS_LOG_LOG_READ_H
+
+/* deal with possible sys/cdefs.h conflict with fcntl.h */
+#ifdef __unused
+#define __unused_defined __unused
+#undef __unused
+#endif
+
+#include <fcntl.h> /* Pick up O_* macros */
+
+/* restore definitions from above */
+#ifdef __unused_defined
+#define __unused __attribute__((__unused__))
+#endif
+
+#include <stdint.h>
+
+#include <log/log_id.h>
+#include <log/log_time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Native log reading interface section. See logcat for sample code.
+ *
+ * The preferred API is an exec of logcat. Likely uses of this interface
+ * are if native code suffers from exec or filtration being too costly,
+ * access to raw information, or parsing is an issue.
+ */
+
+/*
+ * The userspace structure for version 1 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_defined
+#define __struct_logger_entry_defined
+struct logger_entry {
+ uint16_t len; /* length of the payload */
+ uint16_t __pad; /* no matter what, we get 2 bytes of padding */
+ int32_t pid; /* generating process's pid */
+ int32_t tid; /* generating process's tid */
+ int32_t sec; /* seconds since Epoch */
+ int32_t nsec; /* nanoseconds */
+#ifndef __cplusplus
+ char msg[0]; /* the entry's payload */
+#endif
+};
+#endif
+
+/*
+ * The userspace structure for version 2 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_v2_defined
+#define __struct_logger_entry_v2_defined
+struct logger_entry_v2 {
+ uint16_t len; /* length of the payload */
+ uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */
+ int32_t pid; /* generating process's pid */
+ int32_t tid; /* generating process's tid */
+ int32_t sec; /* seconds since Epoch */
+ int32_t nsec; /* nanoseconds */
+ uint32_t euid; /* effective UID of logger */
+#ifndef __cplusplus
+ char msg[0]; /* the entry's payload */
+#endif
+} __attribute__((__packed__));
+#endif
+
+/*
+ * The userspace structure for version 3 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_v3_defined
+#define __struct_logger_entry_v3_defined
+struct logger_entry_v3 {
+ uint16_t len; /* length of the payload */
+ uint16_t hdr_size; /* sizeof(struct logger_entry_v3) */
+ int32_t pid; /* generating process's pid */
+ int32_t tid; /* generating process's tid */
+ int32_t sec; /* seconds since Epoch */
+ int32_t nsec; /* nanoseconds */
+ uint32_t lid; /* log id of the payload */
+#ifndef __cplusplus
+ char msg[0]; /* the entry's payload */
+#endif
+} __attribute__((__packed__));
+#endif
+
+/*
+ * The userspace structure for version 4 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_v4_defined
+#define __struct_logger_entry_v4_defined
+struct logger_entry_v4 {
+ uint16_t len; /* length of the payload */
+ uint16_t hdr_size; /* sizeof(struct logger_entry_v4) */
+ int32_t pid; /* generating process's pid */
+ uint32_t tid; /* generating process's tid */
+ uint32_t sec; /* seconds since Epoch */
+ uint32_t nsec; /* nanoseconds */
+ uint32_t lid; /* log id of the payload, bottom 4 bits currently */
+ uint32_t uid; /* generating process's uid */
+#ifndef __cplusplus
+ char msg[0]; /* the entry's payload */
+#endif
+};
+#endif
+
+/*
+ * The maximum size of the log entry payload that can be
+ * written to the logger. An attempt to write more than
+ * this amount will result in a truncated log entry.
+ */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
+
+/*
+ * The maximum size of a log entry which can be read.
+ * An attempt to read less than this amount may result
+ * in read() returning EINVAL.
+ */
+#define LOGGER_ENTRY_MAX_LEN (5*1024)
+
+#ifndef __struct_log_msg_defined
+#define __struct_log_msg_defined
+struct log_msg {
+ union {
+ unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
+ struct logger_entry_v4 entry;
+ struct logger_entry_v4 entry_v4;
+ struct logger_entry_v3 entry_v3;
+ struct logger_entry_v2 entry_v2;
+ struct logger_entry entry_v1;
+ } __attribute__((aligned(4)));
+#ifdef __cplusplus
+ /* Matching log_time operators */
+ bool operator== (const log_msg& T) const
+ {
+ return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec);
+ }
+ bool operator!= (const log_msg& T) const
+ {
+ return !(*this == T);
+ }
+ bool operator< (const log_msg& T) const
+ {
+ return (entry.sec < T.entry.sec)
+ || ((entry.sec == T.entry.sec)
+ && (entry.nsec < T.entry.nsec));
+ }
+ bool operator>= (const log_msg& T) const
+ {
+ return !(*this < T);
+ }
+ bool operator> (const log_msg& T) const
+ {
+ return (entry.sec > T.entry.sec)
+ || ((entry.sec == T.entry.sec)
+ && (entry.nsec > T.entry.nsec));
+ }
+ bool operator<= (const log_msg& T) const
+ {
+ return !(*this > T);
+ }
+ uint64_t nsec() const
+ {
+ return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
+ }
+
+ /* packet methods */
+ log_id_t id()
+ {
+ return static_cast<log_id_t>(entry.lid);
+ }
+ char* msg()
+ {
+ unsigned short hdr_size = entry.hdr_size;
+ if (!hdr_size) {
+ hdr_size = sizeof(entry_v1);
+ }
+ if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) {
+ return NULL;
+ }
+ return reinterpret_cast<char*>(buf) + hdr_size;
+ }
+ unsigned int len()
+ {
+ return (entry.hdr_size ?
+ entry.hdr_size :
+ static_cast<uint16_t>(sizeof(entry_v1))) +
+ entry.len;
+ }
+#endif
+};
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_READER_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
+#elif __ANDROID_API__ > 23 /* > Marshmallow */
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
+#elif __ANDROID_API__ > 22 /* > Lollipop */
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 2
+#elif __ANDROID_API__ > 19 /* > KitKat */
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_READER_INTERFACE
+
+struct logger;
+
+log_id_t android_logger_get_id(struct logger* logger);
+
+int android_logger_clear(struct logger* logger);
+long android_logger_get_log_size(struct logger* logger);
+int android_logger_set_log_size(struct logger* logger, unsigned long size);
+long android_logger_get_log_readable_size(struct logger* logger);
+int android_logger_get_log_version(struct logger* logger);
+
+struct logger_list;
+
+#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
+ssize_t android_logger_get_statistics(struct logger_list* logger_list,
+ char* buf, size_t len);
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
+ char* buf, size_t len);
+int android_logger_set_prune_list(struct logger_list* logger_list,
+ char* buf, size_t len);
+#endif
+
+#define ANDROID_LOG_RDONLY O_RDONLY
+#define ANDROID_LOG_WRONLY O_WRONLY
+#define ANDROID_LOG_RDWR O_RDWR
+#define ANDROID_LOG_ACCMODE O_ACCMODE
+#define ANDROID_LOG_NONBLOCK O_NONBLOCK
+#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 2
+#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
+#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
+#endif
+#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
+#define ANDROID_LOG_PSTORE 0x80000000
+#endif
+
+struct logger_list* android_logger_list_alloc(int mode,
+ unsigned int tail,
+ pid_t pid);
+struct logger_list* android_logger_list_alloc_time(int mode,
+ log_time start,
+ pid_t pid);
+void android_logger_list_free(struct logger_list* logger_list);
+/* In the purest sense, the following two are orthogonal interfaces */
+int android_logger_list_read(struct logger_list* logger_list,
+ struct log_msg* log_msg);
+
+/* Multiple log_id_t opens */
+struct logger* android_logger_open(struct logger_list* logger_list,
+ log_id_t id);
+#define android_logger_close android_logger_free
+/* Single log_id_t open */
+struct logger_list* android_logger_list_open(log_id_t id,
+ int mode,
+ unsigned int tail,
+ pid_t pid);
+#define android_logger_list_close android_logger_list_free
+
+#endif /* __ANDROID_USE_LIBLOG_READER_INTERFACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_LOG_H */
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
new file mode 100644
index 0000000..8c1ec96
--- /dev/null
+++ b/liblog/include/log/log_system.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_LOG_SYSTEM_H
+#define _LIBS_LOG_LOG_SYSTEM_H
+
+#include <android/log.h>
+#include <log/log_id.h>
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG. You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * Simplified macro to send a verbose system log message using current LOG_TAG.
+ */
+#ifndef SLOGV
+#define __SLOGV(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define SLOGV(...) do { if (0) { __SLOGV(__VA_ARGS__); } } while (0)
+#else
+#define SLOGV(...) __SLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef SLOGV_IF
+#if LOG_NDEBUG
+#define SLOGV_IF(cond, ...) ((void)0)
+#else
+#define SLOGV_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug system log message using current LOG_TAG.
+ */
+#ifndef SLOGD
+#define SLOGD(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGD_IF
+#define SLOGD_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info system log message using current LOG_TAG.
+ */
+#ifndef SLOGI
+#define SLOGI(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGI_IF
+#define SLOGI_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning system log message using current LOG_TAG.
+ */
+#ifndef SLOGW
+#define SLOGW(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGW_IF
+#define SLOGW_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error system log message using current LOG_TAG.
+ */
+#ifndef SLOGE
+#define SLOGE(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGE_IF
+#define SLOGE_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+#endif /* _LIBS_LOG_LOG_SYSTEM_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
new file mode 100644
index 0000000..900dc1b
--- /dev/null
+++ b/liblog/include/log/log_time.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_LOG_TIME_H
+#define _LIBS_LOG_LOG_TIME_H
+
+#include <stdint.h>
+#include <time.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#define NS_PER_SEC 1000000000ULL
+
+#ifndef __struct_log_time_defined
+#define __struct_log_time_defined
+
+#ifdef __cplusplus
+
+/*
+ * NB: we did NOT define a copy constructor. This will result in structure
+ * no longer being compatible with pass-by-value which is desired
+ * efficient behavior. Also, pass-by-reference breaks C/C++ ABI.
+ */
+struct log_time {
+public:
+ uint32_t tv_sec; /* good to Feb 5 2106 */
+ uint32_t tv_nsec;
+
+ static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
+ static const uint32_t tv_nsec_max = 999999999UL;
+
+ log_time(const timespec& T)
+ {
+ tv_sec = static_cast<uint32_t>(T.tv_sec);
+ tv_nsec = static_cast<uint32_t>(T.tv_nsec);
+ }
+ log_time(uint32_t sec, uint32_t nsec)
+ {
+ tv_sec = sec;
+ tv_nsec = nsec;
+ }
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+#define __struct_log_time_private_defined
+ static const timespec EPOCH;
+#endif
+ log_time()
+ {
+ }
+#ifdef __linux__
+ log_time(clockid_t id)
+ {
+ timespec T;
+ clock_gettime(id, &T);
+ tv_sec = static_cast<uint32_t>(T.tv_sec);
+ tv_nsec = static_cast<uint32_t>(T.tv_nsec);
+ }
+#endif
+ log_time(const char* T)
+ {
+ const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
+ tv_sec = c[0] |
+ (static_cast<uint32_t>(c[1]) << 8) |
+ (static_cast<uint32_t>(c[2]) << 16) |
+ (static_cast<uint32_t>(c[3]) << 24);
+ tv_nsec = c[4] |
+ (static_cast<uint32_t>(c[5]) << 8) |
+ (static_cast<uint32_t>(c[6]) << 16) |
+ (static_cast<uint32_t>(c[7]) << 24);
+ }
+
+ /* timespec */
+ bool operator== (const timespec& T) const
+ {
+ return (tv_sec == static_cast<uint32_t>(T.tv_sec))
+ && (tv_nsec == static_cast<uint32_t>(T.tv_nsec));
+ }
+ bool operator!= (const timespec& T) const
+ {
+ return !(*this == T);
+ }
+ bool operator< (const timespec& T) const
+ {
+ return (tv_sec < static_cast<uint32_t>(T.tv_sec))
+ || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
+ && (tv_nsec < static_cast<uint32_t>(T.tv_nsec)));
+ }
+ bool operator>= (const timespec& T) const
+ {
+ return !(*this < T);
+ }
+ bool operator> (const timespec& T) const
+ {
+ return (tv_sec > static_cast<uint32_t>(T.tv_sec))
+ || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
+ && (tv_nsec > static_cast<uint32_t>(T.tv_nsec)));
+ }
+ bool operator<= (const timespec& T) const
+ {
+ return !(*this > T);
+ }
+
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+ log_time operator-= (const timespec& T);
+ log_time operator- (const timespec& T) const
+ {
+ log_time local(*this);
+ return local -= T;
+ }
+ log_time operator+= (const timespec& T);
+ log_time operator+ (const timespec& T) const
+ {
+ log_time local(*this);
+ return local += T;
+ }
+#endif
+
+ /* log_time */
+ bool operator== (const log_time& T) const
+ {
+ return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
+ }
+ bool operator!= (const log_time& T) const
+ {
+ return !(*this == T);
+ }
+ bool operator< (const log_time& T) const
+ {
+ return (tv_sec < T.tv_sec)
+ || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
+ }
+ bool operator>= (const log_time& T) const
+ {
+ return !(*this < T);
+ }
+ bool operator> (const log_time& T) const
+ {
+ return (tv_sec > T.tv_sec)
+ || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
+ }
+ bool operator<= (const log_time& T) const
+ {
+ return !(*this > T);
+ }
+
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+ log_time operator-= (const log_time& T);
+ log_time operator- (const log_time& T) const
+ {
+ log_time local(*this);
+ return local -= T;
+ }
+ log_time operator+= (const log_time& T);
+ log_time operator+ (const log_time& T) const
+ {
+ log_time local(*this);
+ return local += T;
+ }
+#endif
+
+ uint64_t nsec() const
+ {
+ return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
+ }
+
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+ static const char default_format[];
+
+ /* Add %#q for the fraction of a second to the standard library functions */
+ char* strptime(const char* s, const char* format = default_format);
+#endif
+} __attribute__((__packed__));
+
+#else
+
+typedef struct log_time {
+ uint32_t tv_sec;
+ uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif
+
+#endif
+
+#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include/log/logd.h b/liblog/include/log/logd.h
new file mode 100644
index 0000000..77400ca
--- /dev/null
+++ b/liblog/include/log/logd.h
@@ -0,0 +1,5 @@
+#ifndef _LIBS_LOG_LOGD_H
+#define _LIBS_LOG_LOGD_H
+#include <log/log.h>
+#warning "Deprecated: do not include log/logd.h, use log/log.h instead"
+#endif /*_LIBS_LOG_LOGD_H*/
diff --git a/liblog/include/log/logger.h b/liblog/include/log/logger.h
new file mode 100644
index 0000000..1bf2d17
--- /dev/null
+++ b/liblog/include/log/logger.h
@@ -0,0 +1,5 @@
+#ifndef _LIBS_LOG_LOGGER_H
+#define _LIBS_LOG_LOGGER_H
+#include <log/log.h>
+#warning "Deprecated: do not include log/logger.h, use log/log.h instead"
+#endif /*_LIBS_LOG_LOGGER_H*/
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
new file mode 100644
index 0000000..5b99c3c
--- /dev/null
+++ b/liblog/include/log/logprint.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGPRINT_H
+#define _LOGPRINT_H
+
+#include <pthread.h>
+
+#include <android/log.h>
+#include <log/event_tag_map.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ /* Verbs */
+ FORMAT_OFF = 0,
+ FORMAT_BRIEF,
+ FORMAT_PROCESS,
+ FORMAT_TAG,
+ FORMAT_THREAD,
+ FORMAT_RAW,
+ FORMAT_TIME,
+ FORMAT_THREADTIME,
+ FORMAT_LONG,
+ /* Adverbs. The following are modifiers to above format verbs */
+ FORMAT_MODIFIER_COLOR, /* converts priority to color */
+ FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
+ FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
+ FORMAT_MODIFIER_YEAR, /* Adds year to date */
+ FORMAT_MODIFIER_ZONE, /* Adds zone to date, + UTC */
+ FORMAT_MODIFIER_EPOCH, /* Print time as seconds since Jan 1 1970 */
+ FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
+ FORMAT_MODIFIER_UID, /* Adds uid */
+ FORMAT_MODIFIER_DESCRIPT, /* Adds descriptive */
+ /* private, undocumented */
+ FORMAT_MODIFIER_TIME_NSEC, /* switches from msec to nsec time precision */
+} AndroidLogPrintFormat;
+
+typedef struct AndroidLogFormat_t AndroidLogFormat;
+
+typedef struct AndroidLogEntry_t {
+ time_t tv_sec;
+ long tv_nsec;
+ android_LogPriority priority;
+ int32_t uid;
+ int32_t pid;
+ int32_t tid;
+ const char* tag;
+ size_t tagLen;
+ size_t messageLen;
+ const char* message;
+} AndroidLogEntry;
+
+AndroidLogFormat* android_log_format_new();
+
+void android_log_format_free(AndroidLogFormat* p_format);
+
+/* currently returns 0 if format is a modifier, 1 if not */
+int android_log_setPrintFormat(AndroidLogFormat* p_format,
+ AndroidLogPrintFormat format);
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char* s);
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterRule(AndroidLogFormat* p_format,
+ const char* filterExpression);
+
+/**
+ * filterString: a whitespace-separated set of filter expressions
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat* p_format,
+ const char* filterString);
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine (
+ AndroidLogFormat* p_format, const char* tag, android_LogPriority pri);
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry* buf,
+ AndroidLogEntry* entry);
+
+/**
+ * Like android_log_processLogBuffer, but for binary logs.
+ *
+ * If "map" is non-NULL, it will be used to convert the log tag number
+ * into a string.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry* buf,
+ AndroidLogEntry* entry, const EventTagMap* map, char* messageBuf,
+ int messageBufLen);
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char* android_log_formatLogLine (
+ AndroidLogFormat* p_format,
+ char* defaultBuffer,
+ size_t defaultBufferSize,
+ const AndroidLogEntry* p_line,
+ size_t* p_outLength);
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Assumes single threaded execution
+ *
+ */
+int android_log_printLogLine(
+ AndroidLogFormat* p_format,
+ int fd,
+ const AndroidLogEntry* entry);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_LOGPRINT_H*/
diff --git a/include/log/uio.h b/liblog/include/log/uio.h
similarity index 100%
rename from include/log/uio.h
rename to liblog/include/log/uio.h
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
new file mode 100644
index 0000000..9f81b1f
--- /dev/null
+++ b/liblog/include/private/android_logger.h
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+
+/* This file is used to define the internal protocol for the Android Logger */
+
+#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+
+/* Android private interfaces */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#if (defined(__cplusplus) && defined(_USING_LIBCXX))
+extern "C++" {
+#include <string>
+}
+#endif
+
+#include <log/log_event_list.h>
+#include <log/log.h>
+
+#define LOGGER_MAGIC 'l'
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Header Structure to pstore */
+typedef struct __attribute__((__packed__)) {
+ uint8_t magic;
+ uint16_t len;
+ uint16_t uid;
+ uint16_t pid;
+} android_pmsg_log_header_t;
+
+/* Header Structure to logd, and second header for pstore */
+typedef struct __attribute__((__packed__)) {
+ typeof_log_id_t id;
+ uint16_t tid;
+ log_time realtime;
+} android_log_header_t;
+
+/* Event Header Structure to logd */
+typedef struct __attribute__((__packed__)) {
+ int32_t tag; // Little Endian Order
+} android_event_header_t;
+
+/* Event payload EVENT_TYPE_INT */
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_INT
+ int32_t data; // Little Endian Order
+} android_event_int_t;
+
+/* Event with single EVENT_TYPE_INT */
+typedef struct __attribute__((__packed__)) {
+ android_event_header_t header;
+ android_event_int_t payload;
+} android_log_event_int_t;
+
+/* Event payload EVENT_TYPE_LONG */
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_LONG
+ int64_t data; // Little Endian Order
+} android_event_long_t;
+
+/* Event with single EVENT_TYPE_LONG */
+typedef struct __attribute__((__packed__)) {
+ android_event_header_t header;
+ android_event_long_t payload;
+} android_log_event_long_t;
+
+/*
+ * Event payload EVENT_TYPE_STRING
+ *
+ * Danger: do not embed this structure into another structure.
+ * This structure uses a flexible array member, and when
+ * compiled using g++, __builtin_object_size(data, 1) returns
+ * a bad value. This is possibly a g++ bug, or a bug due to
+ * the fact that flexible array members are not supported
+ * in C++.
+ * http://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c
+ */
+
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_STRING;
+ int32_t length; // Little Endian Order
+ char data[];
+} android_event_string_t;
+
+/* Event with single EVENT_TYPE_STRING */
+typedef struct __attribute__((__packed__)) {
+ android_event_header_t header;
+ int8_t type; // EVENT_TYPE_STRING;
+ int32_t length; // Little Endian Order
+ char data[];
+} android_log_event_string_t;
+
+#define ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE 256 /* 1MB file */
+#define ANDROID_LOG_PMSG_FILE_SEQUENCE 1000
+
+ssize_t __android_log_pmsg_file_write(
+ log_id_t logId,
+ char prio,
+ const char* filename,
+ const char* buf, size_t len);
+
+#define LOG_ID_ANY ((log_id_t)-1)
+#define ANDROID_LOG_ANY ANDROID_LOG_UNKNOWN
+
+/* first 5 arguments match __android_log_msg_file_write, a cast is safe */
+typedef ssize_t (*__android_log_pmsg_file_read_fn)(
+ log_id_t logId,
+ char prio,
+ const char* filename,
+ const char* buf, size_t len, void* arg);
+
+ssize_t __android_log_pmsg_file_read(
+ log_id_t logId, char prio, const char* prefix,
+ __android_log_pmsg_file_read_fn fn, void* arg);
+
+int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len);
+int __android_log_security_bswrite(int32_t tag, const char* payload);
+int __android_log_security(); /* Device Owner is present */
+
+int __android_log_is_debuggable();
+
+#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
+#define BOOL_DEFAULT_FALSE 0x0 /* false if property not present */
+#define BOOL_DEFAULT_TRUE 0x1 /* true if property not present */
+#define BOOL_DEFAULT_FLAG_PERSIST 0x2 /* <key>, persist.<key>, ro.<key> */
+#define BOOL_DEFAULT_FLAG_ENG 0x4 /* off for user */
+#define BOOL_DEFAULT_FLAG_SVELTE 0x8 /* off for low_ram */
+bool __android_logger_property_get_bool(const char* key, int flag);
+
+#define LOG_BUFFER_SIZE (256 * 1024) /* Tuned with ro.logd.size per-platform */
+#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
+#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
+unsigned long __android_logger_get_buffer_size(log_id_t logId);
+bool __android_logger_valid_buffer_size(unsigned long value);
+
+/* Retrieve the composed event buffer */
+int android_log_write_list_buffer(android_log_context ctx, const char** msg);
+
+#ifdef __cplusplus
+#ifdef __class_android_log_event_list_defined
+#ifndef __class_android_log_event_list_private_defined
+#define __class_android_log_event_list_private_defined
+/* android_log_context C++ helpers */
+extern "C++" {
+class __android_log_event_list : public android_log_event_list {
+ __android_log_event_list(const android_log_event_list&) = delete;
+ void operator =(const __android_log_event_list&) = delete;
+
+public:
+ explicit __android_log_event_list(int tag) : android_log_event_list(tag) { }
+ explicit __android_log_event_list(log_msg& log_msg) : android_log_event_list(log_msg) { }
+
+#if defined(_USING_LIBCXX)
+ operator std::string() {
+ if (ret) return std::string("");
+ const char* cp = NULL;
+ ssize_t len = android_log_write_list_buffer(ctx, &cp);
+ if (len < 0) ret = len;
+ if (!cp || (len <= 0)) return std::string("");
+ return std::string(cp, len);
+ }
+#endif
+
+};
+}
+#endif
+#endif
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ */
diff --git a/liblog/include_vndk/android b/liblog/include_vndk/android
new file mode 120000
index 0000000..a3c0320
--- /dev/null
+++ b/liblog/include_vndk/android
@@ -0,0 +1 @@
+../include/android
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
new file mode 100644
index 0000000..f93b377
--- /dev/null
+++ b/liblog/include_vndk/log/log.h
@@ -0,0 +1,23 @@
+/*Special log.h file for VNDK linking modules*/
+
+#ifndef _LIBS_LOG_LOG_H
+#define _LIBS_LOG_LOG_H
+
+#include <android/log.h>
+#include <log/log_id.h>
+#include <log/log_main.h>
+#include <log/log_radio.h>
+#include <log/log_read.h>
+#include <log/log_time.h>
+
+/*
+ * LOG_TAG is the local tag used for the following simplified
+ * logging macros. You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+#endif /*_LIBS_LOG_LOG_H*/
diff --git a/liblog/include_vndk/log/log_id.h b/liblog/include_vndk/log/log_id.h
new file mode 120000
index 0000000..dce92b9
--- /dev/null
+++ b/liblog/include_vndk/log/log_id.h
@@ -0,0 +1 @@
+../../include/log/log_id.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_main.h b/liblog/include_vndk/log/log_main.h
new file mode 120000
index 0000000..f2ec018
--- /dev/null
+++ b/liblog/include_vndk/log/log_main.h
@@ -0,0 +1 @@
+../../include/log/log_main.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_radio.h b/liblog/include_vndk/log/log_radio.h
new file mode 120000
index 0000000..1e12b32
--- /dev/null
+++ b/liblog/include_vndk/log/log_radio.h
@@ -0,0 +1 @@
+../../include/log/log_radio.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_read.h b/liblog/include_vndk/log/log_read.h
new file mode 120000
index 0000000..01de8b9
--- /dev/null
+++ b/liblog/include_vndk/log/log_read.h
@@ -0,0 +1 @@
+../../include/log/log_read.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
new file mode 120000
index 0000000..abfe439
--- /dev/null
+++ b/liblog/include_vndk/log/log_time.h
@@ -0,0 +1 @@
+../../include/log/log_time.h
\ No newline at end of file
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
new file mode 100644
index 0000000..599dc90
--- /dev/null
+++ b/liblog/liblog.map.txt
@@ -0,0 +1,19 @@
+LIBLOG {
+ global:
+ __android_log_assert;
+ __android_log_print;
+ __android_log_vprint;
+ __android_log_write;
+ local:
+ *;
+};
+
+LIBLOG_M {
+ global:
+ __android_log_is_loggable;
+};
+
+LIBLOG_O {
+ global:
+ __android_log_is_loggable_len;
+};
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
new file mode 100644
index 0000000..9ac1d30
--- /dev/null
+++ b/liblog/log_event_list.c
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log_event_list.h>
+#include <private/android_logger.h>
+
+#include "log_portability.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;
+
+LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
+ size_t needed, i;
+ android_log_context_internal *context;
+
+ context = calloc(1, sizeof(android_log_context_internal));
+ if (!context) {
+ return NULL;
+ }
+ context->tag = tag;
+ context->read_write_flag = kAndroidLoggerWrite;
+ 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;
+
+ return (android_log_context)context;
+}
+
+LIBLOG_ABI_PUBLIC android_log_context create_android_log_parser(
+ const char *msg,
+ size_t len) {
+ android_log_context_internal *context;
+ size_t i;
+
+ context = calloc(1, sizeof(android_log_context_internal));
+ if (!context) {
+ return NULL;
+ }
+ len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+ context->len = len;
+ memcpy(context->storage, msg, len);
+ context->read_write_flag = kAndroidLoggerRead;
+
+ return (android_log_context)context;
+}
+
+LIBLOG_ABI_PUBLIC int android_log_destroy(android_log_context *ctx) {
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)*ctx;
+ if (!context) {
+ return -EBADF;
+ }
+ memset(context, 0, sizeof(*context));
+ free(context);
+ *ctx = NULL;
+ return 0;
+}
+
+LIBLOG_ABI_PUBLIC int android_log_write_list_begin(android_log_context ctx) {
+ size_t needed;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context ||
+ (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ needed = sizeof(uint8_t) + sizeof(uint8_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ context->list_nest_depth++;
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+ context->storage[context->pos + 1] = 0;
+ context->list[context->list_nest_depth] = context->pos + 1;
+ context->count[context->list_nest_depth] = 0;
+ context->pos += needed;
+ return 0;
+}
+
+static inline void copy4LE(uint8_t *buf, uint32_t val)
+{
+ buf[0] = val & 0xFF;
+ buf[1] = (val >> 8) & 0xFF;
+ buf[2] = (val >> 16) & 0xFF;
+ buf[3] = (val >> 24) & 0xFF;
+}
+
+LIBLOG_ABI_PUBLIC int android_log_write_int32(android_log_context ctx,
+ int32_t value) {
+ size_t needed;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ needed = sizeof(uint8_t) + sizeof(value);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ context->storage[context->pos + 0] = EVENT_TYPE_INT;
+ copy4LE(&context->storage[context->pos + 1], value);
+ context->pos += needed;
+ return 0;
+}
+
+static inline void copy8LE(uint8_t *buf, uint64_t val)
+{
+ buf[0] = val & 0xFF;
+ buf[1] = (val >> 8) & 0xFF;
+ buf[2] = (val >> 16) & 0xFF;
+ buf[3] = (val >> 24) & 0xFF;
+ buf[4] = (val >> 32) & 0xFF;
+ buf[5] = (val >> 40) & 0xFF;
+ buf[6] = (val >> 48) & 0xFF;
+ buf[7] = (val >> 56) & 0xFF;
+}
+
+LIBLOG_ABI_PUBLIC int android_log_write_int64(android_log_context ctx,
+ int64_t value) {
+ size_t needed;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ needed = sizeof(uint8_t) + sizeof(value);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ context->storage[context->pos + 0] = EVENT_TYPE_LONG;
+ copy8LE(&context->storage[context->pos + 1], value);
+ context->pos += needed;
+ return 0;
+}
+
+LIBLOG_ABI_PUBLIC int android_log_write_string8_len(android_log_context ctx,
+ const char *value,
+ size_t maxlen) {
+ size_t needed;
+ ssize_t len;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ if (!value) {
+ value = "";
+ }
+ len = strnlen(value, maxlen);
+ needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ /* Truncate string for delivery */
+ len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+ if (len <= 0) {
+ context->overflow = true;
+ return -EIO;
+ }
+ }
+ context->count[context->list_nest_depth]++;
+ context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+ copy4LE(&context->storage[context->pos + 1], len);
+ if (len) {
+ memcpy(&context->storage[context->pos + 5], value, len);
+ }
+ context->pos += needed;
+ return len;
+}
+
+LIBLOG_ABI_PUBLIC int android_log_write_string8(android_log_context ctx,
+ const char *value) {
+ return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
+}
+
+LIBLOG_ABI_PUBLIC int android_log_write_float32(android_log_context ctx,
+ float value) {
+ size_t needed;
+ uint32_t ivalue;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ needed = sizeof(uint8_t) + sizeof(ivalue);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ ivalue = *(uint32_t *)&value;
+ context->count[context->list_nest_depth]++;
+ context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
+ copy4LE(&context->storage[context->pos + 1], ivalue);
+ context->pos += needed;
+ return 0;
+}
+
+LIBLOG_ABI_PUBLIC int android_log_write_list_end(android_log_context ctx) {
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ context->list_nest_depth--;
+ return -EOVERFLOW;
+ }
+ if (!context->list_nest_depth) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ if (context->list[context->list_nest_depth] <= 0) {
+ context->list_nest_depth--;
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ context->storage[context->list[context->list_nest_depth]] =
+ context->count[context->list_nest_depth];
+ context->list_nest_depth--;
+ return 0;
+}
+
+/*
+ * Logs the list of elements to the event log.
+ */
+LIBLOG_ABI_PUBLIC int android_log_write_list(android_log_context ctx,
+ log_id_t id) {
+ android_log_context_internal *context;
+ const char *msg;
+ ssize_t len;
+
+ if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY)) {
+ return -EINVAL;
+ }
+
+ 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);
+ }
+ return (id == LOG_ID_EVENTS) ?
+ __android_log_bwrite(context->tag, msg, len) :
+ __android_log_security_bwrite(context->tag, msg, len);
+}
+
+LIBLOG_ABI_PRIVATE int android_log_write_list_buffer(android_log_context ctx,
+ const char **buffer) {
+ 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;
+ }
+ if (buffer == NULL) {
+ return -EFAULT;
+ }
+ /* 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);
+ }
+ *buffer = msg;
+ return len;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+/*
+ * Extract an 8-byte value from a byte stream.
+ */
+static inline uint64_t get8LE(const uint8_t* src)
+{
+ uint32_t low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+ uint32_t high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+ return ((uint64_t) high << 32) | (uint64_t) low;
+}
+
+/*
+ * Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
+ * If there is nothing to process, the complete field is set to non-zero. If
+ * an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
+ * this and continues to call this function, the behavior is undefined
+ * (although it won't crash).
+ */
+static android_log_list_element android_log_read_next_internal(
+ android_log_context ctx, int peek) {
+ android_log_list_element elem;
+ unsigned pos;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+
+ memset(&elem, 0, sizeof(elem));
+
+ /* Nothing to parse from this context, so return complete. */
+ if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
+ (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
+ (context->count[context->list_nest_depth] >=
+ (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ if (context &&
+ (context->list_stop ||
+ ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+ !context->count[context->list_nest_depth]))) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ }
+ elem.complete = true;
+ return elem;
+ }
+
+ /*
+ * Use a different variable to update the position in case this
+ * operation is a "peek".
+ */
+ pos = context->pos;
+ if (context->list_stop) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ elem.complete = !context->count[0] && (!context->list_nest_depth ||
+ ((context->list_nest_depth == 1) && !context->count[1]));
+ if (!peek) {
+ /* Suck in superfluous stop */
+ if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
+ context->pos = pos + 1;
+ }
+ if (context->list_nest_depth) {
+ --context->list_nest_depth;
+ if (context->count[context->list_nest_depth]) {
+ context->list_stop = false;
+ }
+ } else {
+ context->list_stop = false;
+ }
+ }
+ return elem;
+ }
+ if ((pos + 1) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+
+ elem.type = context->storage[pos++];
+ switch ((int)elem.type) {
+ case EVENT_TYPE_FLOAT:
+ /* Rely on union to translate elem.data.int32 into elem.data.float32 */
+ /* FALLTHRU */
+ case EVENT_TYPE_INT:
+ elem.len = sizeof(int32_t);
+ if ((pos + elem.len) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ elem.data.int32 = get4LE(&context->storage[pos]);
+ /* common tangeable object suffix */
+ pos += elem.len;
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+
+ case EVENT_TYPE_LONG:
+ elem.len = sizeof(int64_t);
+ if ((pos + elem.len) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ elem.data.int64 = get8LE(&context->storage[pos]);
+ /* common tangeable object suffix */
+ pos += elem.len;
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+
+ case EVENT_TYPE_STRING:
+ if ((pos + sizeof(int32_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+ elem.len = get4LE(&context->storage[pos]);
+ pos += sizeof(int32_t);
+ if ((pos + elem.len) > context->len) {
+ elem.len = context->len - pos; /* truncate string */
+ elem.complete = true;
+ if (!elem.len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ }
+ elem.data.string = (char *)&context->storage[pos];
+ /* common tangeable object suffix */
+ pos += elem.len;
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+
+ case EVENT_TYPE_LIST:
+ if ((pos + sizeof(uint8_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+ elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
+ if (peek) {
+ return elem;
+ }
+ if (context->count[context->list_nest_depth]) {
+ context->count[context->list_nest_depth]--;
+ }
+ context->list_stop = !context->storage[pos];
+ context->list_nest_depth++;
+ if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->count[context->list_nest_depth] = context->storage[pos];
+ }
+ context->pos = pos + sizeof(uint8_t);
+ return elem;
+
+ case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+ if (!peek) {
+ context->pos = pos;
+ }
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = !context->list_nest_depth;
+ if (context->list_nest_depth > 0) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ if (!peek) {
+ context->list_nest_depth--;
+ }
+ }
+ return elem;
+
+ default:
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+}
+
+LIBLOG_ABI_PUBLIC android_log_list_element android_log_read_next(
+ android_log_context ctx) {
+ return android_log_read_next_internal(ctx, 0);
+}
+
+LIBLOG_ABI_PUBLIC android_log_list_element android_log_peek_next(
+ android_log_context ctx) {
+ return android_log_read_next_internal(ctx, 1);
+}
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c
new file mode 100644
index 0000000..14d6482
--- /dev/null
+++ b/liblog/log_event_write.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <log/log.h>
+#include <log/log_event_list.h>
+
+#include "log_portability.h"
+
+#define MAX_SUBTAG_LEN 32
+
+LIBLOG_ABI_PUBLIC int __android_log_error_write(
+ int tag,
+ const char *subTag,
+ int32_t uid,
+ const char *data, uint32_t dataLen)
+{
+ int ret = -EINVAL;
+
+ if (subTag && (data || !dataLen)) {
+ android_log_context ctx = create_android_logger(tag);
+
+ ret = -ENOMEM;
+ if (ctx) {
+ ret = android_log_write_string8_len(ctx, subTag, MAX_SUBTAG_LEN);
+ if (ret >= 0) {
+ ret = android_log_write_int32(ctx, uid);
+ if (ret >= 0) {
+ ret = android_log_write_string8_len(ctx, data, dataLen);
+ if (ret >= 0) {
+ ret = android_log_write_list(ctx, LOG_ID_EVENTS);
+ }
+ }
+ }
+ android_log_destroy(&ctx);
+ }
+ }
+ return ret;
+}
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
deleted file mode 100644
index 7a8e33f..0000000
--- a/liblog/log_is_loggable.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <ctype.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
-#include <android/log.h>
-
-struct cache {
- const prop_info *pinfo;
- uint32_t serial;
- char c;
-};
-
-static void refresh_cache(struct cache *cache, const char *key)
-{
- uint32_t serial;
- char buf[PROP_VALUE_MAX];
-
- if (!cache->pinfo) {
- cache->pinfo = __system_property_find(key);
- if (!cache->pinfo) {
- return;
- }
- }
- serial = __system_property_serial(cache->pinfo);
- if (serial == cache->serial) {
- return;
- }
- cache->serial = serial;
- __system_property_read(cache->pinfo, 0, buf);
- cache->c = buf[0];
-}
-
-static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-
-static int __android_log_level(const char *tag, int def)
-{
- /* sizeof() is used on this array below */
- static const char log_namespace[] = "persist.log.tag.";
- static const size_t base_offset = 8; /* skip "persist." */
- /* calculate the size of our key temporary buffer */
- const size_t taglen = (tag && *tag) ? strlen(tag) : 0;
- /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
- char key[sizeof(log_namespace) + taglen];
- char *kp;
- size_t i;
- char c = 0;
- /*
- * Single layer cache of four properties. Priorities are:
- * log.tag.<tag>
- * persist.log.tag.<tag>
- * log.tag
- * persist.log.tag
- * Where the missing tag matches all tags and becomes the
- * system global default. We do not support ro.log.tag* .
- */
- static char *last_tag;
- static uint32_t global_serial;
- uint32_t current_global_serial;
- static struct cache tag_cache[2] = {
- { NULL, -1, 0 },
- { NULL, -1, 0 }
- };
- static struct cache global_cache[2] = {
- { NULL, -1, 0 },
- { NULL, -1, 0 }
- };
-
- strcpy(key, log_namespace);
-
- pthread_mutex_lock(&lock);
-
- current_global_serial = __system_property_area_serial();
-
- if (taglen) {
- uint32_t current_local_serial = current_global_serial;
-
- if (!last_tag || strcmp(last_tag, tag)) {
- /* invalidate log.tag.<tag> cache */
- for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
- tag_cache[i].pinfo = NULL;
- tag_cache[i].serial = -1;
- tag_cache[i].c = '\0';
- }
- free(last_tag);
- last_tag = NULL;
- current_global_serial = -1;
- }
- if (!last_tag) {
- last_tag = strdup(tag);
- }
- strcpy(key + sizeof(log_namespace) - 1, tag);
-
- kp = key;
- for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
- if (current_local_serial != global_serial) {
- refresh_cache(&tag_cache[i], kp);
- }
-
- if (tag_cache[i].c) {
- c = tag_cache[i].c;
- break;
- }
-
- kp = key + base_offset;
- }
- }
-
- switch (toupper(c)) { /* if invalid, resort to global */
- case 'V':
- case 'D':
- case 'I':
- case 'W':
- case 'E':
- case 'F': /* Not officially supported */
- case 'A':
- case 'S':
- break;
- default:
- /* clear '.' after log.tag */
- key[sizeof(log_namespace) - 2] = '\0';
-
- kp = key;
- for(i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
- if (current_global_serial != global_serial) {
- refresh_cache(&global_cache[i], kp);
- }
-
- if (global_cache[i].c) {
- c = global_cache[i].c;
- break;
- }
-
- kp = key + base_offset;
- }
- break;
- }
-
- global_serial = current_global_serial;
-
- pthread_mutex_unlock(&lock);
-
- switch (toupper(c)) {
- case 'V': return ANDROID_LOG_VERBOSE;
- case 'D': return ANDROID_LOG_DEBUG;
- case 'I': return ANDROID_LOG_INFO;
- case 'W': return ANDROID_LOG_WARN;
- case 'E': return ANDROID_LOG_ERROR;
- case 'F': /* FALLTHRU */ /* Not officially supported */
- case 'A': return ANDROID_LOG_FATAL;
- case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
- }
- return def;
-}
-
-int __android_log_is_loggable(int prio, const char *tag, int def)
-{
- int logLevel = __android_log_level(tag, def);
- return logLevel >= 0 && prio >= logLevel;
-}
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
new file mode 100644
index 0000000..3ad2060
--- /dev/null
+++ b/liblog/log_portability.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_PORTABILITY_H__
+#define _LIBLOG_PORTABILITY_H__
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+/* Helpful private sys/cdefs.h like definitions */
+
+/* Declare this library function hidden and internal */
+#if defined(_WIN32)
+#define LIBLOG_HIDDEN
+#else
+#define LIBLOG_HIDDEN __attribute__((visibility("hidden")))
+#endif
+
+/* Declare this library function visible and external */
+#if defined(_WIN32)
+#define LIBLOG_ABI_PUBLIC
+#else
+#define LIBLOG_ABI_PUBLIC __attribute__((visibility("default")))
+#endif
+
+/* Declare this library function visible but private */
+#define LIBLOG_ABI_PRIVATE LIBLOG_ABI_PUBLIC
+
+/*
+ * Declare this library function as reimplementation.
+ * Prevent circular dependencies, but allow _real_ library to hijack
+ */
+#if defined(_WIN32)
+#define LIBLOG_WEAK static /* Accept that it is totally private */
+#else
+#define LIBLOG_WEAK __attribute__((weak,visibility("default")))
+#endif
+
+/* possible missing definitions in sys/cdefs.h */
+
+/* DECLS */
+#ifndef __BEGIN_DECLS
+#if defined(__cplusplus)
+#define __BEGIN_DECLS extern "C" {
+#define __END_DECLS }
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+#endif
+
+/* Unused argument. For C code only, remove symbol name for C++ */
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+/* possible missing definitions in unistd.h */
+
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ __typeof__(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+#endif /* _LIBLOG_PORTABILITY_H__ */
diff --git a/liblog/log_ratelimit.cpp b/liblog/log_ratelimit.cpp
new file mode 100644
index 0000000..dfd4b8f
--- /dev/null
+++ b/liblog/log_ratelimit.cpp
@@ -0,0 +1,86 @@
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+#include <log/log.h>
+
+#include "log_portability.h"
+
+// Global default if 'last' argument in __android_log_ratelimit is NULL
+static time_t g_last_clock;
+// Global above can not deal well with callers playing games with the
+// seconds argument, so we will also hold on to the maximum value
+// ever provided and use that to gain consistency. If the caller
+// provides their own 'last' argument, then they can play such games
+// of varying the 'seconds' argument to their pleasure.
+static time_t g_last_seconds;
+static const time_t last_seconds_default = 10;
+static const time_t last_seconds_max = 24 * 60 * 60; // maximum of a day
+static const time_t last_seconds_min = 2; // granularity
+// Lock to protect last_clock and last_seconds, but also 'last'
+// argument (not NULL) as supplied to __android_log_ratelimit.
+static pthread_mutex_t lock_ratelimit = PTHREAD_MUTEX_INITIALIZER;
+
+// if last is NULL, caller _must_ provide a consistent value for
+// seconds, otherwise we will take the maximum ever issued and hold
+// on to that. Preserves value of non-zero errno. Return -1 if we
+// can not acquire a lock, 0 if we are not to log a message, and 1
+// if we are ok to log a message. Caller should check > 0 for true.
+LIBLOG_ABI_PUBLIC int __android_log_ratelimit(time_t seconds, time_t* last) {
+ int save_errno = errno;
+
+ // Two reasons for trylock failure:
+ // 1. In a signal handler. Must prevent deadlock
+ // 2. Too many threads calling __android_log_ratelimit.
+ // Bonus to not print if they race here because that
+ // dovetails the goal of ratelimiting. One may print
+ // and the others will wait their turn ...
+ if (pthread_mutex_trylock(&lock_ratelimit)) {
+ if (save_errno) errno = save_errno;
+ return -1;
+ }
+
+ if (seconds == 0) {
+ seconds = last_seconds_default;
+ } else if (seconds < last_seconds_min) {
+ seconds = last_seconds_min;
+ } else if (seconds > last_seconds_max) {
+ seconds = last_seconds_max;
+ }
+
+ if (!last) {
+ if (g_last_seconds > seconds) {
+ seconds = g_last_seconds;
+ } else if (g_last_seconds < seconds) {
+ g_last_seconds = seconds;
+ }
+ last = &g_last_clock;
+ }
+
+ time_t now = time(NULL);
+ if ((now == (time_t)-1) || ((*last + seconds) > now)) {
+ pthread_mutex_unlock(&lock_ratelimit);
+ if (save_errno) errno = save_errno;
+ return 0;
+ }
+ *last = now;
+ pthread_mutex_unlock(&lock_ratelimit);
+ if (save_errno) errno = save_errno;
+ return 1;
+}
diff --git a/liblog/log_read.c b/liblog/log_read.c
deleted file mode 100644
index cfc8a7a..0000000
--- a/liblog/log_read.c
+++ /dev/null
@@ -1,900 +0,0 @@
-/*
-** Copyright 2013-2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stddef.h>
-#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
-#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <unistd.h>
-
-#include <cutils/list.h>
-#include <cutils/sockets.h>
-#include <log/log.h>
-#include <log/logger.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-/* branchless on many architectures. */
-#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-
-#if defined(_WIN32)
-#define WEAK static
-#else
-#define WEAK __attribute__((weak))
-#endif
-#ifndef __unused
-#define __unused __attribute__((unused))
-#endif
-
-/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
-
-#if defined(_WIN32)
-
-int WEAK socket_local_client(const char *name, int namespaceId, int type)
-{
- errno = ENOSYS;
- return -ENOSYS;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-
-/* Private copy of ../libcutils/socket_local.h prevent library loops */
-#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
-#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
-/* End of ../libcutils/socket_local.h */
-
-#define LISTEN_BACKLOG 4
-
-/* Documented in header file. */
-int WEAK socket_make_sockaddr_un(const char *name, int namespaceId,
- struct sockaddr_un *p_addr, socklen_t *alen)
-{
- memset (p_addr, 0, sizeof (*p_addr));
- size_t namelen;
-
- switch (namespaceId) {
- case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
-#if defined(__linux__)
- namelen = strlen(name);
-
- /* Test with length +1 for the *initial* '\0'. */
- if ((namelen + 1) > sizeof(p_addr->sun_path)) {
- goto error;
- }
-
- /*
- * Note: The path in this case is *not* supposed to be
- * '\0'-terminated. ("man 7 unix" for the gory details.)
- */
-
- p_addr->sun_path[0] = 0;
- memcpy(p_addr->sun_path + 1, name, namelen);
-#else
- /* this OS doesn't have the Linux abstract namespace */
-
- namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr)
- - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
- strcat(p_addr->sun_path, name);
-#endif
- break;
-
- case ANDROID_SOCKET_NAMESPACE_RESERVED:
- namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr)
- - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
- strcat(p_addr->sun_path, name);
- break;
-
- case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
- namelen = strlen(name);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr)
- - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, name);
- break;
-
- default:
- /* invalid namespace id */
- return -1;
- }
-
- p_addr->sun_family = AF_LOCAL;
- *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
- return 0;
-error:
- return -1;
-}
-
-/**
- * connect to peer named "name" on fd
- * returns same fd or -1 on error.
- * fd is not closed on error. that's your job.
- *
- * Used by AndroidSocketImpl
- */
-int WEAK socket_local_client_connect(int fd, const char *name, int namespaceId,
- int type __unused)
-{
- struct sockaddr_un addr;
- socklen_t alen;
- int err;
-
- err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
-
- if (err < 0) {
- goto error;
- }
-
- if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
- goto error;
- }
-
- return fd;
-
-error:
- return -1;
-}
-
-/**
- * connect to peer named "name"
- * returns fd or -1 on error
- */
-int WEAK socket_local_client(const char *name, int namespaceId, int type)
-{
- int s;
-
- s = socket(AF_LOCAL, type, 0);
- if(s < 0) return -1;
-
- if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
- close(s);
- return -1;
- }
-
- return s;
-}
-
-#endif /* !_WIN32 */
-/* End of ../libcutils/socket_local_client.c */
-
-#define logger_for_each(logger, logger_list) \
- for (logger = node_to_item((logger_list)->node.next, struct logger, node); \
- logger != node_to_item(&(logger_list)->node, struct logger, node); \
- logger = node_to_item((logger)->node.next, struct logger, node))
-
-/* In the future, we would like to make this list extensible */
-static const char *LOG_NAME[LOG_ID_MAX] = {
- [LOG_ID_MAIN] = "main",
- [LOG_ID_RADIO] = "radio",
- [LOG_ID_EVENTS] = "events",
- [LOG_ID_SYSTEM] = "system",
- [LOG_ID_CRASH] = "crash",
- [LOG_ID_KERNEL] = "kernel",
-};
-
-const char *android_log_id_to_name(log_id_t log_id)
-{
- if (log_id >= LOG_ID_MAX) {
- log_id = LOG_ID_MAIN;
- }
- return LOG_NAME[log_id];
-}
-
-log_id_t android_name_to_log_id(const char *logName)
-{
- const char *b;
- int ret;
-
- if (!logName) {
- return -1; /* NB: log_id_t is unsigned */
- }
- b = strrchr(logName, '/');
- if (!b) {
- b = logName;
- } else {
- ++b;
- }
-
- for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
- const char *l = LOG_NAME[ret];
- if (l && !strcmp(b, l)) {
- return ret;
- }
- }
- return -1; /* should never happen */
-}
-
-struct logger_list {
- struct listnode node;
- int mode;
- unsigned int tail;
- log_time start;
- pid_t pid;
- int sock;
-};
-
-struct logger {
- struct listnode node;
- struct logger_list *top;
- log_id_t id;
-};
-
-/* android_logger_alloc unimplemented, no use case */
-/* android_logger_free not exported */
-static void android_logger_free(struct logger *logger)
-{
- if (!logger) {
- return;
- }
-
- list_remove(&logger->node);
-
- free(logger);
-}
-
-/* android_logger_alloc unimplemented, no use case */
-
-/* method for getting the associated sublog id */
-log_id_t android_logger_get_id(struct logger *logger)
-{
- return logger->id;
-}
-
-/* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct logger *logger,
- const char *msg, char *buf, size_t buf_size)
-{
- ssize_t ret;
- size_t len;
- char *cp;
- int errno_save = 0;
- int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM);
- if (sock < 0) {
- return sock;
- }
-
- if (msg) {
- snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1);
- }
-
- len = strlen(buf) + 1;
- ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
- if (ret <= 0) {
- goto done;
- }
-
- len = buf_size;
- cp = buf;
- while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
- struct pollfd p;
-
- if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
- break;
- }
-
- len -= ret;
- cp += ret;
-
- memset(&p, 0, sizeof(p));
- p.fd = sock;
- p.events = POLLIN;
-
- /* Give other side 20ms to refill pipe */
- ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
-
- if (ret <= 0) {
- break;
- }
-
- if (!(p.revents & POLLIN)) {
- ret = 0;
- break;
- }
- }
-
- if (ret >= 0) {
- ret += buf_size - len;
- }
-
-done:
- if ((ret == -1) && errno) {
- errno_save = errno;
- }
- close(sock);
- if (errno_save) {
- errno = errno_save;
- }
- return ret;
-}
-
-static int check_log_success(char *buf, ssize_t ret)
-{
- if (ret < 0) {
- return ret;
- }
-
- if (strncmp(buf, "success", 7)) {
- errno = EINVAL;
- return -1;
- }
-
- return 0;
-}
-
-/* Determine the credentials of the caller */
-static bool uid_has_log_permission(uid_t uid)
-{
- return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT);
-}
-
-static uid_t get_best_effective_uid()
-{
- uid_t euid;
- uid_t uid;
- gid_t gid;
- ssize_t i;
- static uid_t last_uid = (uid_t) -1;
-
- if (last_uid != (uid_t) -1) {
- return last_uid;
- }
- uid = getuid();
- if (uid_has_log_permission(uid)) {
- return last_uid = uid;
- }
- euid = geteuid();
- if (uid_has_log_permission(euid)) {
- return last_uid = euid;
- }
- gid = getgid();
- if (uid_has_log_permission(gid)) {
- return last_uid = gid;
- }
- gid = getegid();
- if (uid_has_log_permission(gid)) {
- return last_uid = gid;
- }
- i = getgroups((size_t) 0, NULL);
- if (i > 0) {
- gid_t list[i];
-
- getgroups(i, list);
- while (--i >= 0) {
- if (uid_has_log_permission(list[i])) {
- return last_uid = list[i];
- }
- }
- }
- return last_uid = uid;
-}
-
-int android_logger_clear(struct logger *logger)
-{
- char buf[512];
-
- if (logger->top->mode & ANDROID_LOG_PSTORE) {
- if (uid_has_log_permission(get_best_effective_uid())) {
- return unlink("/sys/fs/pstore/pmsg-ramoops-0");
- }
- errno = EPERM;
- return -1;
- }
- return check_log_success(buf,
- send_log_msg(logger, "clear %d", buf, sizeof(buf)));
-}
-
-/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger *logger)
-{
- char buf[512];
-
- ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
- if (ret < 0) {
- return ret;
- }
-
- if ((buf[0] < '0') || ('9' < buf[0])) {
- return -1;
- }
-
- return atol(buf);
-}
-
-int android_logger_set_log_size(struct logger *logger, unsigned long size)
-{
- char buf[512];
-
- snprintf(buf, sizeof(buf), "setLogSize %d %lu",
- logger ? logger->id : (unsigned) -1, size);
-
- return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-long android_logger_get_log_readable_size(struct logger *logger)
-{
- char buf[512];
-
- ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
- if (ret < 0) {
- return ret;
- }
-
- if ((buf[0] < '0') || ('9' < buf[0])) {
- return -1;
- }
-
- return atol(buf);
-}
-
-/*
- * returns the logger version
- */
-int android_logger_get_log_version(struct logger *logger __unused)
-{
- return 3;
-}
-
-/*
- * returns statistics
- */
-ssize_t android_logger_get_statistics(struct logger_list *logger_list,
- char *buf, size_t len)
-{
- struct logger *logger;
- char *cp = buf;
- size_t remaining = len;
- size_t n;
-
- n = snprintf(cp, remaining, "getStatistics");
- n = min(n, remaining);
- remaining -= n;
- cp += n;
-
- logger_for_each(logger, logger_list) {
- n = snprintf(cp, remaining, " %d", logger->id);
- n = min(n, remaining);
- remaining -= n;
- cp += n;
- }
- return send_log_msg(NULL, NULL, buf, len);
-}
-
-ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused,
- char *buf, size_t len)
-{
- return send_log_msg(NULL, "getPruneList", buf, len);
-}
-
-int android_logger_set_prune_list(struct logger_list *logger_list __unused,
- char *buf, size_t len)
-{
- const char cmd[] = "setPruneList ";
- const size_t cmdlen = sizeof(cmd) - 1;
-
- if (strlen(buf) > (len - cmdlen)) {
- return -ENOMEM; /* KISS */
- }
- memmove(buf + cmdlen, buf, len - cmdlen);
- buf[len - 1] = '\0';
- memcpy(buf, cmd, cmdlen);
-
- return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
-}
-
-struct logger_list *android_logger_list_alloc(int mode,
- unsigned int tail,
- pid_t pid)
-{
- struct logger_list *logger_list;
-
- logger_list = calloc(1, sizeof(*logger_list));
- if (!logger_list) {
- return NULL;
- }
-
- list_init(&logger_list->node);
- logger_list->mode = mode;
- logger_list->start.tv_sec = 0;
- logger_list->start.tv_nsec = 0;
- logger_list->tail = tail;
- logger_list->pid = pid;
- logger_list->sock = -1;
-
- return logger_list;
-}
-
-struct logger_list *android_logger_list_alloc_time(int mode,
- log_time start,
- pid_t pid)
-{
- struct logger_list *logger_list;
-
- logger_list = calloc(1, sizeof(*logger_list));
- if (!logger_list) {
- return NULL;
- }
-
- list_init(&logger_list->node);
- logger_list->mode = mode;
- logger_list->start = start;
- logger_list->tail = 0;
- logger_list->pid = pid;
- logger_list->sock = -1;
-
- return logger_list;
-}
-
-/* android_logger_list_register unimplemented, no use case */
-/* android_logger_list_unregister unimplemented, no use case */
-
-/* Open the named log and add it to the logger list */
-struct logger *android_logger_open(struct logger_list *logger_list,
- log_id_t id)
-{
- struct logger *logger;
-
- if (!logger_list || (id >= LOG_ID_MAX)) {
- goto err;
- }
-
- logger_for_each(logger, logger_list) {
- if (logger->id == id) {
- goto ok;
- }
- }
-
- logger = calloc(1, sizeof(*logger));
- if (!logger) {
- goto err;
- }
-
- logger->id = id;
- list_add_tail(&logger_list->node, &logger->node);
- logger->top = logger_list;
- goto ok;
-
-err:
- logger = NULL;
-ok:
- return logger;
-}
-
-/* Open the single named log and make it part of a new logger list */
-struct logger_list *android_logger_list_open(log_id_t id,
- int mode,
- unsigned int tail,
- pid_t pid)
-{
- struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid);
- if (!logger_list) {
- return NULL;
- }
-
- if (!android_logger_open(logger_list, id)) {
- android_logger_list_free(logger_list);
- return NULL;
- }
-
- return logger_list;
-}
-
-static int android_logger_list_read_pstore(struct logger_list *logger_list,
- struct log_msg *log_msg)
-{
- ssize_t ret;
- off_t current, next;
- uid_t uid;
- struct logger *logger;
- struct __attribute__((__packed__)) {
- android_pmsg_log_header_t p;
- android_log_header_t l;
- } buf;
- static uint8_t preread_count;
-
- memset(log_msg, 0, sizeof(*log_msg));
-
- if (logger_list->sock < 0) {
- int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY);
-
- if (fd < 0) {
- return -errno;
- }
- logger_list->sock = fd;
- preread_count = 0;
- }
-
- ret = 0;
- while(1) {
- if (preread_count < sizeof(buf)) {
- ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
- &buf.p.magic + preread_count,
- sizeof(buf) - preread_count));
- if (ret < 0) {
- return -errno;
- }
- preread_count += ret;
- }
- if (preread_count != sizeof(buf)) {
- return preread_count ? -EIO : -EAGAIN;
- }
- if ((buf.p.magic != LOGGER_MAGIC)
- || (buf.p.len <= sizeof(buf))
- || (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD))
- || (buf.l.id >= LOG_ID_MAX)
- || (buf.l.realtime.tv_nsec >= NS_PER_SEC)) {
- do {
- memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
- } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
- continue;
- }
- preread_count = 0;
-
- logger_for_each(logger, logger_list) {
- if (buf.l.id != logger->id) {
- continue;
- }
-
- if ((logger_list->start.tv_sec || logger_list->start.tv_nsec)
- && ((logger_list->start.tv_sec > buf.l.realtime.tv_sec)
- || ((logger_list->start.tv_sec == buf.l.realtime.tv_sec)
- && (logger_list->start.tv_nsec > buf.l.realtime.tv_nsec)))) {
- break;
- }
-
- if (logger_list->pid && (logger_list->pid != buf.p.pid)) {
- break;
- }
-
- uid = get_best_effective_uid();
- if (!uid_has_log_permission(uid) && (uid != buf.p.uid)) {
- break;
- }
-
- ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
- log_msg->entry_v3.msg,
- buf.p.len - sizeof(buf)));
- if (ret < 0) {
- return -errno;
- }
- if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
- return -EIO;
- }
-
- log_msg->entry_v3.len = buf.p.len - sizeof(buf);
- log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3);
- log_msg->entry_v3.pid = buf.p.pid;
- log_msg->entry_v3.tid = buf.l.tid;
- log_msg->entry_v3.sec = buf.l.realtime.tv_sec;
- log_msg->entry_v3.nsec = buf.l.realtime.tv_nsec;
- log_msg->entry_v3.lid = buf.l.id;
-
- return ret;
- }
-
- current = TEMP_FAILURE_RETRY(lseek(logger_list->sock,
- (off_t)0, SEEK_CUR));
- if (current < 0) {
- return -errno;
- }
- next = TEMP_FAILURE_RETRY(lseek(logger_list->sock,
- (off_t)(buf.p.len - sizeof(buf)),
- SEEK_CUR));
- if (next < 0) {
- return -errno;
- }
- if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
- return -EIO;
- }
- }
-}
-
-static void caught_signal(int signum __unused)
-{
-}
-
-/* Read from the selected logs */
-int android_logger_list_read(struct logger_list *logger_list,
- struct log_msg *log_msg)
-{
- int ret, e;
- struct logger *logger;
- struct sigaction ignore;
- struct sigaction old_sigaction;
- unsigned int old_alarm = 0;
-
- if (!logger_list) {
- return -EINVAL;
- }
-
- if (logger_list->mode & ANDROID_LOG_PSTORE) {
- return android_logger_list_read_pstore(logger_list, log_msg);
- }
-
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
- memset(&ignore, 0, sizeof(ignore));
- ignore.sa_handler = caught_signal;
- sigemptyset(&ignore.sa_mask);
- }
-
- if (logger_list->sock < 0) {
- char buffer[256], *cp, c;
-
- int sock = socket_local_client("logdr",
- ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_SEQPACKET);
- if (sock < 0) {
- if ((sock == -1) && errno) {
- return -errno;
- }
- return sock;
- }
-
- strcpy(buffer,
- (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
- cp = buffer + strlen(buffer);
-
- strcpy(cp, " lids");
- cp += 5;
- c = '=';
- int remaining = sizeof(buffer) - (cp - buffer);
- logger_for_each(logger, logger_list) {
- ret = snprintf(cp, remaining, "%c%u", c, logger->id);
- ret = min(ret, remaining);
- remaining -= ret;
- cp += ret;
- c = ',';
- }
-
- if (logger_list->tail) {
- ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
- ret = min(ret, remaining);
- remaining -= ret;
- cp += ret;
- }
-
- if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
- ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32,
- logger_list->start.tv_sec,
- logger_list->start.tv_nsec);
- ret = min(ret, remaining);
- remaining -= ret;
- cp += ret;
- }
-
- if (logger_list->pid) {
- ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
- ret = min(ret, remaining);
- remaining -= ret;
- cp += ret;
- }
-
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
- /* Deal with an unresponsive logd */
- sigaction(SIGALRM, &ignore, &old_sigaction);
- old_alarm = alarm(30);
- }
- ret = write(sock, buffer, cp - buffer);
- e = errno;
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
- if (e == EINTR) {
- e = ETIMEDOUT;
- }
- alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
- }
-
- if (ret <= 0) {
- close(sock);
- if ((ret == -1) && e) {
- return -e;
- }
- if (ret == 0) {
- return -EIO;
- }
- return ret;
- }
-
- logger_list->sock = sock;
- }
-
- ret = 0;
- while(1) {
- memset(log_msg, 0, sizeof(*log_msg));
-
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
- /* particularily useful if tombstone is reporting for logd */
- sigaction(SIGALRM, &ignore, &old_sigaction);
- old_alarm = alarm(30);
- }
- /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
- ret = recv(logger_list->sock, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
- e = errno;
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
- if ((ret == 0) || (e == EINTR)) {
- e = EAGAIN;
- ret = -1;
- }
- alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
- }
-
- if (ret <= 0) {
- if ((ret == -1) && e) {
- return -e;
- }
- return ret;
- }
-
- logger_for_each(logger, logger_list) {
- if (log_msg->entry.lid == logger->id) {
- return ret;
- }
- }
- }
- /* NOTREACH */
- return ret;
-}
-
-/* Close all the logs */
-void android_logger_list_free(struct logger_list *logger_list)
-{
- if (logger_list == NULL) {
- return;
- }
-
- while (!list_empty(&logger_list->node)) {
- struct listnode *node = list_head(&logger_list->node);
- struct logger *logger = node_to_item(node, struct logger, node);
- android_logger_free(logger);
- }
-
- if (logger_list->sock >= 0) {
- close (logger_list->sock);
- }
-
- free(logger_list);
-}
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 9d5ea0e..dfd2d44 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -18,16 +18,17 @@
#include <limits.h>
#include <stdio.h>
#include <string.h>
-#include <sys/cdefs.h>
-#include <log/log_read.h>
+#include <private/android_logger.h>
-const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
-const timespec log_time::EPOCH = { 0, 0 };
+#include "log_portability.h"
+
+LIBLOG_ABI_PRIVATE const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
+LIBLOG_ABI_PRIVATE const timespec log_time::EPOCH = { 0, 0 };
// Add %#q for fractional seconds to standard strptime function
-char *log_time::strptime(const char *s, const char *format) {
+LIBLOG_ABI_PRIVATE char *log_time::strptime(const char *s, const char *format) {
time_t now;
#ifdef __linux__
*this = log_time(CLOCK_REALTIME);
@@ -133,7 +134,7 @@
return ret;
}
-log_time log_time::operator-= (const timespec &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator-= (const timespec &T) {
// No concept of negative time, clamp to EPOCH
if (*this <= T) {
return *this = EPOCH;
@@ -150,7 +151,7 @@
return *this;
}
-log_time log_time::operator+= (const timespec &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator+= (const timespec &T) {
this->tv_nsec += (unsigned long int)T.tv_nsec;
if (this->tv_nsec >= NS_PER_SEC) {
this->tv_nsec -= NS_PER_SEC;
@@ -161,7 +162,7 @@
return *this;
}
-log_time log_time::operator-= (const log_time &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator-= (const log_time &T) {
// No concept of negative time, clamp to EPOCH
if (*this <= T) {
return *this = EPOCH;
@@ -178,7 +179,7 @@
return *this;
}
-log_time log_time::operator+= (const log_time &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator+= (const log_time &T) {
this->tv_nsec += T.tv_nsec;
if (this->tv_nsec >= NS_PER_SEC) {
this->tv_nsec -= NS_PER_SEC;
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
new file mode 100644
index 0000000..ccc7da8
--- /dev/null
+++ b/liblog/logd_reader.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.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/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_read.h"
+#include "log_portability.h"
+#include "logd_reader.h"
+#include "logger.h"
+
+/* branchless on many architectures. */
+#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+static int logdAvailable(log_id_t LogId);
+static int logdVersion(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+static int logdRead(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ struct log_msg *log_msg);
+static int logdPoll(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp);
+static void logdClose(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp);
+static int logdClear(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+static ssize_t logdSetSize(struct android_log_logger *logger,
+ struct android_log_transport_context *transp,
+ size_t size);
+static ssize_t logdGetSize(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+static ssize_t logdGetReadableSize(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+static ssize_t logdGetPrune(struct android_log_logger_list *logger,
+ struct android_log_transport_context *transp,
+ char *buf, size_t len);
+static ssize_t logdSetPrune(struct android_log_logger_list *logger,
+ struct android_log_transport_context *transp,
+ char *buf, size_t len);
+static ssize_t logdGetStats(struct android_log_logger_list *logger,
+ struct android_log_transport_context *transp,
+ char *buf, size_t len);
+
+LIBLOG_HIDDEN struct android_log_transport_read logdLoggerRead = {
+ .node = { &logdLoggerRead.node, &logdLoggerRead.node },
+ .name = "logd",
+ .available = logdAvailable,
+ .version = logdVersion,
+ .read = logdRead,
+ .poll = logdPoll,
+ .close = logdClose,
+ .clear = logdClear,
+ .getSize = logdGetSize,
+ .setSize = logdSetSize,
+ .getReadableSize = logdGetReadableSize,
+ .getPrune = logdGetPrune,
+ .setPrune = logdSetPrune,
+ .getStats = logdGetStats,
+};
+
+static int logdAvailable(log_id_t logId)
+{
+ if (logId > LOG_ID_KERNEL) {
+ return -EINVAL;
+ }
+ if (logId == LOG_ID_SECURITY) {
+ uid_t uid = __android_log_uid();
+ if (uid != AID_SYSTEM) {
+ return -EPERM;
+ }
+ }
+ if (access("/dev/socket/logdw", W_OK) == 0) {
+ return 0;
+ }
+ return -EBADF;
+}
+
+/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
+
+#if defined(_WIN32)
+
+LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -ENOSYS;
+}
+
+#else /* !_WIN32 */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+/* Private copy of ../libcutils/socket_local.h prevent library loops */
+#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
+#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
+/* End of ../libcutils/socket_local.h */
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+LIBLOG_WEAK int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr,
+ socklen_t *alen)
+{
+ memset (p_addr, 0, sizeof (*p_addr));
+ size_t namelen;
+
+ switch (namespaceId) {
+ case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#if defined(__linux__)
+ namelen = strlen(name);
+
+ /* Test with length +1 for the *initial* '\0'. */
+ if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+ goto error;
+ }
+
+ /*
+ * Note: The path in this case is *not* supposed to be
+ * '\0'-terminated. ("man 7 unix" for the gory details.)
+ */
+
+ p_addr->sun_path[0] = 0;
+ memcpy(p_addr->sun_path + 1, name, namelen);
+#else
+ /* this OS doesn't have the Linux abstract namespace */
+
+ namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+#endif
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_RESERVED:
+ namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+ namelen = strlen(name);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, name);
+ break;
+
+ default:
+ /* invalid namespace id */
+ return -1;
+ }
+
+ p_addr->sun_family = AF_LOCAL;
+ *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ *
+ * Used by AndroidSocketImpl
+ */
+LIBLOG_WEAK int socket_local_client_connect(int fd, const char *name,
+ int namespaceId, int type __unused)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ goto error;
+ }
+
+ if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+ goto error;
+ }
+
+ return fd;
+
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
+{
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if(s < 0) return -1;
+
+ if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+#endif /* !_WIN32 */
+/* End of ../libcutils/socket_local_client.c */
+
+/* worker for sending the command to the logger */
+static ssize_t send_log_msg(struct android_log_logger *logger,
+ const char *msg, char *buf, size_t buf_size)
+{
+ ssize_t ret;
+ size_t len;
+ char *cp;
+ int errno_save = 0;
+ int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (sock < 0) {
+ return sock;
+ }
+
+ if (msg) {
+ snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned) -1);
+ }
+
+ len = strlen(buf) + 1;
+ ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
+ if (ret <= 0) {
+ goto done;
+ }
+
+ len = buf_size;
+ cp = buf;
+ while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
+ struct pollfd p;
+
+ if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
+ break;
+ }
+
+ len -= ret;
+ cp += ret;
+
+ memset(&p, 0, sizeof(p));
+ p.fd = sock;
+ p.events = POLLIN;
+
+ /* Give other side 20ms to refill pipe */
+ ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
+
+ if (ret <= 0) {
+ break;
+ }
+
+ if (!(p.revents & POLLIN)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret >= 0) {
+ ret += buf_size - len;
+ }
+
+done:
+ if ((ret == -1) && errno) {
+ errno_save = errno;
+ }
+ close(sock);
+ if (errno_save) {
+ errno = errno_save;
+ }
+ return ret;
+}
+
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size)
+{
+ return send_log_msg(NULL, NULL, buf, buf_size);
+}
+
+static int check_log_success(char *buf, ssize_t ret)
+{
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (strncmp(buf, "success", 7)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int logdClear(struct android_log_logger *logger,
+ struct android_log_transport_context *transp __unused)
+{
+ char buf[512];
+
+ return check_log_success(buf,
+ send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+}
+
+/* returns the total size of the log's ring buffer */
+static ssize_t logdGetSize(struct android_log_logger *logger,
+ struct android_log_transport_context *transp __unused)
+{
+ char buf[512];
+
+ ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if ((buf[0] < '0') || ('9' < buf[0])) {
+ return -1;
+ }
+
+ return atol(buf);
+}
+
+static ssize_t logdSetSize(
+ struct android_log_logger *logger,
+ struct android_log_transport_context *transp __unused,
+ size_t size)
+{
+ char buf[512];
+
+ snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
+
+ return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+}
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+static ssize_t logdGetReadableSize(
+ struct android_log_logger *logger,
+ struct android_log_transport_context *transp __unused)
+{
+ char buf[512];
+
+ ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if ((buf[0] < '0') || ('9' < buf[0])) {
+ return -1;
+ }
+
+ return atol(buf);
+}
+
+/*
+ * returns the logger version
+ */
+static int logdVersion(
+ struct android_log_logger *logger __unused,
+ struct android_log_transport_context *transp __unused)
+{
+ uid_t uid = __android_log_uid();
+ return ((uid != AID_ROOT) && (uid != AID_LOG) && (uid != AID_SYSTEM)) ? 3 : 4;
+}
+
+/*
+ * returns statistics
+ */
+static ssize_t logdGetStats(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp __unused,
+ char *buf, size_t len)
+{
+ struct android_log_logger *logger;
+ char *cp = buf;
+ size_t remaining = len;
+ size_t n;
+
+ n = snprintf(cp, remaining, "getStatistics");
+ n = min(n, remaining);
+ remaining -= n;
+ cp += n;
+
+ logger_for_each(logger, logger_list) {
+ n = snprintf(cp, remaining, " %d", logger->logId);
+ n = min(n, remaining);
+ remaining -= n;
+ cp += n;
+ }
+
+ if (logger_list->pid) {
+ snprintf(cp, remaining, " pid=%u", logger_list->pid);
+ }
+
+ return send_log_msg(NULL, NULL, buf, len);
+}
+
+static ssize_t logdGetPrune(
+ struct android_log_logger_list *logger_list __unused,
+ struct android_log_transport_context *transp __unused,
+ char *buf, size_t len)
+{
+ return send_log_msg(NULL, "getPruneList", buf, len);
+}
+
+static ssize_t logdSetPrune(
+ struct android_log_logger_list *logger_list __unused,
+ struct android_log_transport_context *transp __unused,
+ char *buf, size_t len)
+{
+ const char cmd[] = "setPruneList ";
+ const size_t cmdlen = sizeof(cmd) - 1;
+
+ if (strlen(buf) > (len - cmdlen)) {
+ return -ENOMEM; /* KISS */
+ }
+ memmove(buf + cmdlen, buf, len - cmdlen);
+ buf[len - 1] = '\0';
+ memcpy(buf, cmd, cmdlen);
+
+ return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+}
+
+
+static void caught_signal(int signum __unused)
+{
+}
+
+static int logdOpen(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp)
+{
+ struct android_log_logger *logger;
+ struct sigaction ignore;
+ struct sigaction old_sigaction;
+ unsigned int old_alarm = 0;
+ char buffer[256], *cp, c;
+ int e, ret, remaining, sock;
+
+ if (!logger_list) {
+ return -EINVAL;
+ }
+
+ sock = atomic_load(&transp->context.sock);
+ if (sock > 0) {
+ return sock;
+ }
+
+ sock = socket_local_client("logdr",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ if (sock == 0) {
+ /* Guarantee not file descriptor zero */
+ int newsock = socket_local_client("logdr",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ close(sock);
+ sock = newsock;
+ }
+ if (sock <= 0) {
+ if ((sock == -1) && errno) {
+ return -errno;
+ }
+ return sock;
+ }
+
+ strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ?
+ "dumpAndClose" : "stream");
+ cp = buffer + strlen(buffer);
+
+ strcpy(cp, " lids");
+ cp += 5;
+ c = '=';
+ remaining = sizeof(buffer) - (cp - buffer);
+ logger_for_each(logger, logger_list) {
+ ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ c = ',';
+ }
+
+ if (logger_list->tail) {
+ ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+
+ if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
+ if (logger_list->mode & ANDROID_LOG_WRAP) {
+ // ToDo: alternate API to allow timeout to be adjusted.
+ ret = snprintf(cp, remaining, " timeout=%u",
+ ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+ ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32,
+ logger_list->start.tv_sec,
+ logger_list->start.tv_nsec);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+
+ if (logger_list->pid) {
+ ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
+ ret = min(ret, remaining);
+ cp += ret;
+ }
+
+ if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ /* Deal with an unresponsive logd */
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ /* particularily useful if tombstone is reporting for logd */
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ old_alarm = alarm(30);
+ }
+ ret = write(sock, buffer, cp - buffer);
+ e = errno;
+ if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ if (e == EINTR) {
+ e = ETIMEDOUT;
+ }
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+ }
+
+ if (ret <= 0) {
+ close(sock);
+ if ((ret == -1) && e) {
+ return -e;
+ }
+ if (ret == 0) {
+ return -EIO;
+ }
+ return ret;
+ }
+
+ ret = atomic_exchange(&transp->context.sock, sock);
+ if ((ret > 0) && (ret != sock)) {
+ close(ret);
+ }
+ return sock;
+}
+
+/* Read from the selected logs */
+static int logdRead(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ struct log_msg *log_msg)
+{
+ int ret, e;
+ struct sigaction ignore;
+ struct sigaction old_sigaction;
+ unsigned int old_alarm = 0;
+
+ ret = logdOpen(logger_list, transp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ memset(log_msg, 0, sizeof(*log_msg));
+
+ if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ /* particularily useful if tombstone is reporting for logd */
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ old_alarm = alarm(30);
+ }
+
+ /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
+ ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
+ e = errno;
+
+ if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ if ((ret == 0) || (e == EINTR)) {
+ e = EAGAIN;
+ ret = -1;
+ }
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+ }
+
+ if ((ret == -1) && e) {
+ return -e;
+ }
+ return ret;
+}
+
+static int logdPoll(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp)
+{
+ struct pollfd p;
+
+ int ret = logdOpen(logger_list, transp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ memset(&p, 0, sizeof(p));
+ p.fd = ret;
+ p.events = POLLIN;
+ ret = poll(&p, 1, 20);
+ if ((ret > 0) && !(p.revents & POLLIN)) {
+ ret = 0;
+ }
+ if ((ret == -1) && errno) {
+ return -errno;
+ }
+ return ret;
+}
+
+/* Close all the logs */
+static void logdClose(struct android_log_logger_list *logger_list __unused,
+ struct android_log_transport_context *transp)
+{
+ int sock = atomic_exchange(&transp->context.sock, -1);
+ if (sock > 0) {
+ close (sock);
+ }
+}
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
new file mode 100644
index 0000000..04c2cf2
--- /dev/null
+++ b/liblog/logd_reader.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_LOGD_READER_H__
+#define _LIBLOG_LOGD_READER_H__
+
+#include <unistd.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size);
+
+__END_DECLS
+
+#endif /* _LIBLOG_LOGD_READER_H__ */
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
deleted file mode 100644
index bdee28f..0000000
--- a/liblog/logd_write.c
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * Copyright (C) 2007-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#if (FAKE_LOG_DEVICE == 0)
-#include <endian.h>
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
-#include <stdarg.h>
-#include <stdatomic.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#if (FAKE_LOG_DEVICE == 0)
-#include <sys/socket.h>
-#include <sys/un.h>
-#endif
-#include <time.h>
-#include <unistd.h>
-
-#ifdef __BIONIC__
-#include <android/set_abort_message.h>
-#endif
-
-#include <log/logd.h>
-#include <log/logger.h>
-#include <log/log_read.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#define LOG_BUF_SIZE 1024
-
-#if FAKE_LOG_DEVICE
-/* This will be defined when building for the host. */
-#include "fake_log_device.h"
-#endif
-
-static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
-static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
-#if !defined(_WIN32)
-static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-#if FAKE_LOG_DEVICE
-static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 };
-#else
-static int logd_fd = -1;
-static int pstore_fd = -1;
-#endif
-
-/*
- * This is used by the C++ code to decide if it should write logs through
- * the C code. Basically, if /dev/socket/logd is available, we're running in
- * the simulator rather than a desktop tool and want to use the device.
- */
-static enum {
- kLogUninitialized, kLogNotAvailable, kLogAvailable
-} g_log_status = kLogUninitialized;
-
-int __android_log_dev_available(void)
-{
- if (g_log_status == kLogUninitialized) {
- if (access("/dev/socket/logdw", W_OK) == 0)
- g_log_status = kLogAvailable;
- else
- g_log_status = kLogNotAvailable;
- }
-
- return (g_log_status == kLogAvailable);
-}
-
-/* log_init_lock assumed */
-static int __write_to_log_initialize()
-{
- int i, ret = 0;
-
-#if FAKE_LOG_DEVICE
- for (i = 0; i < LOG_ID_MAX; i++) {
- char buf[sizeof("/dev/log_system")];
- snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
- log_fds[i] = fakeLogOpen(buf, O_WRONLY);
- }
-#else
- if (pstore_fd < 0) {
- pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
- }
-
- if (logd_fd < 0) {
- i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
- if (i < 0) {
- ret = -errno;
- } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) {
- ret = -errno;
- close(i);
- } else {
- struct sockaddr_un un;
- memset(&un, 0, sizeof(struct sockaddr_un));
- un.sun_family = AF_UNIX;
- strcpy(un.sun_path, "/dev/socket/logdw");
-
- if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,
- sizeof(struct sockaddr_un))) < 0) {
- ret = -errno;
- close(i);
- } else {
- logd_fd = i;
- }
- }
- }
-#endif
-
- return ret;
-}
-
-static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr)
-{
- ssize_t ret;
-#if FAKE_LOG_DEVICE
- int log_fd;
-
- if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
- log_fd = log_fds[(int)log_id];
- } else {
- return -EBADF;
- }
- do {
- ret = fakeLogWritev(log_fd, vec, nr);
- if (ret < 0) {
- ret = -errno;
- }
- } while (ret == -EINTR);
-#else
- static const unsigned header_length = 2;
- struct iovec newVec[nr + header_length];
- android_log_header_t header;
- android_pmsg_log_header_t pmsg_header;
- struct timespec ts;
- size_t i, payload_size;
- static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */
- static pid_t last_pid = (pid_t) -1;
- static atomic_int_fast32_t dropped;
-
- if (!nr) {
- return -EINVAL;
- }
-
- if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */
- last_uid = getuid();
- }
- if (last_pid == (pid_t) -1) {
- last_pid = getpid();
- }
- /*
- * struct {
- * // what we provide to pstore
- * android_pmsg_log_header_t pmsg_header;
- * // 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;
- * };
- * };
- */
-
- clock_gettime(CLOCK_REALTIME, &ts);
-
- pmsg_header.magic = LOGGER_MAGIC;
- pmsg_header.len = sizeof(pmsg_header) + sizeof(header);
- pmsg_header.uid = last_uid;
- pmsg_header.pid = last_pid;
-
- header.tid = gettid();
- header.realtime.tv_sec = ts.tv_sec;
- header.realtime.tv_nsec = ts.tv_nsec;
-
- newVec[0].iov_base = (unsigned char *) &pmsg_header;
- newVec[0].iov_len = sizeof(pmsg_header);
- newVec[1].iov_base = (unsigned char *) &header;
- newVec[1].iov_len = sizeof(header);
-
- if (logd_fd > 0) {
- int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
- if (snapshot) {
- android_log_event_int_t buffer;
-
- header.id = LOG_ID_EVENTS;
- buffer.header.tag = htole32(LIBLOG_LOG_TAG);
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = htole32(snapshot);
-
- newVec[2].iov_base = &buffer;
- newVec[2].iov_len = sizeof(buffer);
-
- ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
- if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
- }
- }
- }
-
- header.id = log_id;
-
- for (payload_size = 0, i = header_length; i < nr + header_length; i++) {
- newVec[i].iov_base = vec[i - header_length].iov_base;
- payload_size += newVec[i].iov_len = vec[i - header_length].iov_len;
-
- if (payload_size > LOGGER_ENTRY_MAX_PAYLOAD) {
- newVec[i].iov_len -= payload_size - LOGGER_ENTRY_MAX_PAYLOAD;
- if (newVec[i].iov_len) {
- ++i;
- }
- payload_size = LOGGER_ENTRY_MAX_PAYLOAD;
- break;
- }
- }
- pmsg_header.len += payload_size;
-
- if (pstore_fd >= 0) {
- TEMP_FAILURE_RETRY(writev(pstore_fd, newVec, i));
- }
-
- if (last_uid == AID_LOGD) { /* logd, after initialization and priv drop */
- /*
- * ignore log messages we send to ourself (logd).
- * Such log messages are often generated by libraries we depend on
- * which use standard Android logging.
- */
- return 0;
- }
-
- if (logd_fd < 0) {
- return -EBADF;
- }
-
- /*
- * The write below could be lost, but will never block.
- *
- * To logd, we drop the pmsg_header
- *
- * ENOTCONN occurs if logd dies.
- * EAGAIN occurs if logd is overloaded.
- */
- ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1));
- if (ret < 0) {
- ret = -errno;
- if (ret == -ENOTCONN) {
-#if !defined(_WIN32)
- pthread_mutex_lock(&log_init_lock);
-#endif
- close(logd_fd);
- logd_fd = -1;
- ret = __write_to_log_initialize();
-#if !defined(_WIN32)
- pthread_mutex_unlock(&log_init_lock);
-#endif
-
- if (ret < 0) {
- return ret;
- }
-
- ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1));
- if (ret < 0) {
- ret = -errno;
- }
- }
- }
-
- if (ret > (ssize_t)sizeof(header)) {
- ret -= sizeof(header);
- } else if (ret == -EAGAIN) {
- atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
- }
-#endif
-
- return ret;
-}
-
-#if FAKE_LOG_DEVICE
-static const char *LOG_NAME[LOG_ID_MAX] = {
- [LOG_ID_MAIN] = "main",
- [LOG_ID_RADIO] = "radio",
- [LOG_ID_EVENTS] = "events",
- [LOG_ID_SYSTEM] = "system",
- [LOG_ID_CRASH] = "crash",
- [LOG_ID_KERNEL] = "kernel",
-};
-
-const char *android_log_id_to_name(log_id_t log_id)
-{
- if (log_id >= LOG_ID_MAX) {
- log_id = LOG_ID_MAIN;
- }
- return LOG_NAME[log_id];
-}
-#endif
-
-static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
-{
-#if !defined(_WIN32)
- pthread_mutex_lock(&log_init_lock);
-#endif
-
- if (write_to_log == __write_to_log_init) {
- int ret;
-
- ret = __write_to_log_initialize();
- if (ret < 0) {
-#if !defined(_WIN32)
- pthread_mutex_unlock(&log_init_lock);
-#endif
-#if (FAKE_LOG_DEVICE == 0)
- if (pstore_fd >= 0) {
- __write_to_log_daemon(log_id, vec, nr);
- }
-#endif
- return ret;
- }
-
- write_to_log = __write_to_log_daemon;
- }
-
-#if !defined(_WIN32)
- pthread_mutex_unlock(&log_init_lock);
-#endif
-
- return write_to_log(log_id, vec, nr);
-}
-
-int __android_log_write(int prio, const char *tag, const char *msg)
-{
- return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
-}
-
-int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
-{
- struct iovec vec[3];
- char tmp_tag[32];
-
- if (!tag)
- tag = "";
-
- /* XXX: This needs to go! */
- if ((bufID != LOG_ID_RADIO) &&
- (!strcmp(tag, "HTC_RIL") ||
- !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
- !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
- !strcmp(tag, "AT") ||
- !strcmp(tag, "GSM") ||
- !strcmp(tag, "STK") ||
- !strcmp(tag, "CDMA") ||
- !strcmp(tag, "PHONE") ||
- !strcmp(tag, "SMS"))) {
- bufID = LOG_ID_RADIO;
- /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
- snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
- tag = tmp_tag;
- }
-
-#if __BIONIC__
- if (prio == ANDROID_LOG_FATAL) {
- android_set_abort_message(msg);
- }
-#endif
-
- vec[0].iov_base = (unsigned char *) &prio;
- vec[0].iov_len = 1;
- vec[1].iov_base = (void *) tag;
- vec[1].iov_len = strlen(tag) + 1;
- vec[2].iov_base = (void *) msg;
- vec[2].iov_len = strlen(msg) + 1;
-
- return write_to_log(bufID, vec, 3);
-}
-
-int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
-{
- char buf[LOG_BUF_SIZE];
-
- vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-
- return __android_log_write(prio, tag, buf);
-}
-
-int __android_log_print(int prio, const char *tag, const char *fmt, ...)
-{
- va_list ap;
- char buf[LOG_BUF_SIZE];
-
- va_start(ap, fmt);
- vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
- va_end(ap);
-
- return __android_log_write(prio, tag, buf);
-}
-
-int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
-{
- va_list ap;
- char buf[LOG_BUF_SIZE];
-
- va_start(ap, fmt);
- vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
- va_end(ap);
-
- return __android_log_buf_write(bufID, prio, tag, buf);
-}
-
-void __android_log_assert(const char *cond, const char *tag,
- const char *fmt, ...)
-{
- char buf[LOG_BUF_SIZE];
-
- if (fmt) {
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
- va_end(ap);
- } else {
- /* Msg not provided, log condition. N.B. Do not use cond directly as
- * format string as it could contain spurious '%' syntax (e.g.
- * "%d" in "blocks%devs == 0").
- */
- if (cond)
- snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
- else
- strcpy(buf, "Unspecified assertion failed");
- }
-
- __android_log_write(ANDROID_LOG_FATAL, tag, buf);
- abort(); /* abort so we have a chance to debug the situation */
- /* NOTREACHED */
-}
-
-int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
-{
- struct iovec vec[2];
-
- vec[0].iov_base = &tag;
- vec[0].iov_len = sizeof(tag);
- vec[1].iov_base = (void*)payload;
- vec[1].iov_len = len;
-
- return write_to_log(LOG_ID_EVENTS, vec, 2);
-}
-
-/*
- * Like __android_log_bwrite, but takes the type as well. Doesn't work
- * for the general case where we're generating lists of stuff, but very
- * handy if we just want to dump an integer into the log.
- */
-int __android_log_btwrite(int32_t tag, char type, const void *payload,
- size_t len)
-{
- struct iovec vec[3];
-
- vec[0].iov_base = &tag;
- vec[0].iov_len = sizeof(tag);
- vec[1].iov_base = &type;
- vec[1].iov_len = sizeof(type);
- vec[2].iov_base = (void*)payload;
- vec[2].iov_len = len;
-
- return write_to_log(LOG_ID_EVENTS, vec, 3);
-}
-
-/*
- * Like __android_log_bwrite, but used for writing strings to the
- * event log.
- */
-int __android_log_bswrite(int32_t tag, const char *payload)
-{
- struct iovec vec[4];
- char type = EVENT_TYPE_STRING;
- uint32_t len = strlen(payload);
-
- vec[0].iov_base = &tag;
- vec[0].iov_len = sizeof(tag);
- vec[1].iov_base = &type;
- vec[1].iov_len = sizeof(type);
- vec[2].iov_base = &len;
- vec[2].iov_len = sizeof(len);
- vec[3].iov_base = (void*)payload;
- vec[3].iov_len = len;
-
- return write_to_log(LOG_ID_EVENTS, vec, 4);
-}
diff --git a/liblog/logd_writer.c b/liblog/logd_writer.c
new file mode 100644
index 0000000..2bab92e
--- /dev/null
+++ b/liblog/logd_writer.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.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/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+
+/* branchless on many architectures. */
+#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+static int logdAvailable(log_id_t LogId);
+static int logdOpen();
+static void logdClose();
+static int logdWrite(log_id_t logId, struct timespec *ts,
+ struct iovec *vec, size_t nr);
+
+LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = {
+ .node = { &logdLoggerWrite.node, &logdLoggerWrite.node },
+ .context.sock = -EBADF,
+ .name = "logd",
+ .available = logdAvailable,
+ .open = logdOpen,
+ .close = logdClose,
+ .write = logdWrite,
+};
+
+/* log_init_lock assumed */
+static int logdOpen()
+{
+ int i, ret = 0;
+
+ i = atomic_load(&logdLoggerWrite.context.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/logdw");
+
+ if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr *)&un,
+ sizeof(struct sockaddr_un))) < 0) {
+ ret = -errno;
+ switch (ret) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ i = atomic_exchange(&logdLoggerWrite.context.sock, ret);
+ /* FALLTHRU */
+ default:
+ break;
+ }
+ close(sock);
+ } else {
+ ret = atomic_exchange(&logdLoggerWrite.context.sock, sock);
+ if ((ret >= 0) && (ret != sock)) {
+ close(ret);
+ }
+ ret = 0;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void __logdClose(int negative_errno)
+{
+ int sock = atomic_exchange(&logdLoggerWrite.context.sock, negative_errno);
+ if (sock >= 0) {
+ close(sock);
+ }
+}
+
+static void logdClose()
+{
+ __logdClose(-EBADF);
+}
+
+static int logdAvailable(log_id_t logId)
+{
+ if (logId > LOG_ID_SECURITY) {
+ return -EINVAL;
+ }
+ if (atomic_load(&logdLoggerWrite.context.sock) < 0) {
+ if (access("/dev/socket/logdw", W_OK) == 0) {
+ return 0;
+ }
+ return -EBADF;
+ }
+ return 1;
+}
+
+static int logdWrite(log_id_t logId, struct timespec *ts,
+ struct iovec *vec, size_t nr)
+{
+ ssize_t ret;
+ int sock;
+ static const unsigned headerLength = 1;
+ struct iovec newVec[nr + headerLength];
+ android_log_header_t header;
+ size_t i, payloadSize;
+ static atomic_int_fast32_t dropped;
+ static atomic_int_fast32_t droppedSecurity;
+
+ sock = atomic_load(&logdLoggerWrite.context.sock);
+ if (sock < 0) switch (sock) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ break;
+ default:
+ return -EBADF;
+ }
+
+ /* logd, after initialization and priv drop */
+ if (__android_log_uid() == AID_LOGD) {
+ /*
+ * ignore log messages we send to ourself (logd).
+ * Such log messages are often generated by libraries we depend on
+ * which use standard Android logging.
+ */
+ return 0;
+ }
+
+ /*
+ * 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 (sock >= 0) {
+ int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0,
+ memory_order_relaxed);
+ if (snapshot) {
+ android_log_event_int_t buffer;
+
+ header.id = LOG_ID_SECURITY;
+ 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(&droppedSecurity, snapshot,
+ memory_order_relaxed);
+ }
+ }
+ snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+ if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO,
+ "liblog",
+ strlen("liblog"),
+ ANDROID_LOG_VERBOSE)) {
+ android_log_event_int_t buffer;
+
+ header.id = LOG_ID_EVENTS;
+ 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 = logId;
+
+ 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 logd has died.
+ * ENOENT occurs if logd is not running and socket is missing.
+ * ECONNREFUSED occurs if we can not reconnect to logd.
+ * EAGAIN occurs if logd 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 (__android_log_trylock()) {
+ return ret; /* in a signal handler? try again when less stressed */
+ }
+ __logdClose(ret);
+ ret = logdOpen();
+ __android_log_unlock();
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = TEMP_FAILURE_RETRY(writev(
+ atomic_load(&logdLoggerWrite.context.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);
+ if (logId == LOG_ID_SECURITY) {
+ atomic_fetch_add_explicit(&droppedSecurity, 1,
+ memory_order_relaxed);
+ }
+ }
+
+ return ret;
+}
diff --git a/liblog/logger.h b/liblog/logger.h
new file mode 100644
index 0000000..50d1cb4
--- /dev/null
+++ b/liblog/logger.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_LOGGER_H__
+#define _LIBLOG_LOGGER_H__
+
+#include <stdatomic.h>
+#include <stdbool.h>
+
+#include <cutils/list.h>
+#include <log/log.h>
+#include <log/uio.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+/* Union, sock or fd of zero is not allowed unless static initialized */
+union android_log_context {
+ void *private;
+ atomic_int sock;
+ atomic_int fd;
+ struct listnode *node;
+ atomic_uintptr_t atomic_pointer;
+};
+
+struct android_log_transport_write {
+ struct listnode node;
+ const char *name;
+ unsigned logMask; /* cache of available success */
+ union android_log_context context; /* Initialized by static allocation */
+
+ int (*available)(log_id_t logId);
+ int (*open)();
+ void (*close)();
+ int (*write)(log_id_t logId, struct timespec *ts, struct iovec *vec, size_t nr);
+};
+
+struct android_log_logger_list;
+struct android_log_transport_context;
+struct android_log_logger;
+
+struct android_log_transport_read {
+ struct listnode node;
+ const char *name;
+
+ int (*available)(log_id_t logId);
+ int (*version)(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+ void (*close)(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp);
+
+ /*
+ * Expect all to instantiate open on any call, so we do not have
+ * an expicit open call
+ */
+ int (*read)(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ struct log_msg *log_msg);
+ /* Assumption is only called if not ANDROID_LOG_NONBLOCK */
+ int (*poll)(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp);
+
+ int (*clear)(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+ ssize_t (*setSize)(struct android_log_logger *logger,
+ struct android_log_transport_context *transp,
+ size_t size);
+ ssize_t (*getSize)(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+ ssize_t (*getReadableSize)(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+
+ ssize_t (*getPrune)(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ char *buf, size_t len);
+ ssize_t (*setPrune)(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ char *buf, size_t len);
+ ssize_t (*getStats)(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ char *buf, size_t len);
+};
+
+struct android_log_logger_list {
+ struct listnode logger;
+ struct listnode transport;
+ int mode;
+ unsigned int tail;
+ log_time start;
+ pid_t pid;
+};
+
+struct android_log_logger {
+ struct listnode node;
+ struct android_log_logger_list *parent;
+
+ log_id_t logId;
+};
+
+struct android_log_transport_context {
+ struct listnode node;
+ union android_log_context context; /* zero init per-transport context */
+ struct android_log_logger_list *parent;
+
+ struct android_log_transport_read *transport;
+ unsigned logMask;
+ int ret;
+ struct log_msg logMsg; /* valid is logMsg.len != 0 */
+};
+
+/* assumes caller has structures read-locked, single threaded, or fenced */
+#define transport_context_for_each(transp, logger_list) \
+ for ((transp) = node_to_item((logger_list)->transport.next, \
+ struct android_log_transport_context, \
+ node); \
+ ((transp) != node_to_item(&(logger_list)->transport, \
+ struct android_log_transport_context, \
+ node)) && \
+ ((transp)->parent == (logger_list)); \
+ (transp) = node_to_item((transp)->node.next, \
+ struct android_log_transport_context, node))
+
+#define logger_for_each(logp, logger_list) \
+ for ((logp) = node_to_item((logger_list)->logger.next, \
+ struct android_log_logger, node); \
+ ((logp) != node_to_item(&(logger_list)->logger, \
+ struct android_log_logger, node)) && \
+ ((logp)->parent == (logger_list)); \
+ (logp) = node_to_item((logp)->node.next, \
+ struct android_log_logger, node))
+
+/* OS specific dribs and drabs */
+
+#if defined(_WIN32)
+#include <private/android_filesystem_config.h>
+typedef uint32_t uid_t;
+static inline uid_t __android_log_uid() { return AID_SYSTEM; }
+#else
+static inline uid_t __android_log_uid() { return getuid(); }
+#endif
+
+LIBLOG_HIDDEN void __android_log_lock();
+LIBLOG_HIDDEN int __android_log_trylock();
+LIBLOG_HIDDEN void __android_log_unlock();
+
+__END_DECLS
+
+#endif /* _LIBLOG_LOGGER_H__ */
diff --git a/liblog/logger_lock.c b/liblog/logger_lock.c
new file mode 100644
index 0000000..14feee0
--- /dev/null
+++ b/liblog/logger_lock.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Some OS specific dribs and drabs (locking etc).
+ */
+
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+
+#include "logger.h"
+
+#if !defined(_WIN32)
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+LIBLOG_HIDDEN void __android_log_lock()
+{
+#if !defined(_WIN32)
+ /*
+ * 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);
+#endif
+}
+
+LIBLOG_HIDDEN int __android_log_trylock()
+{
+#if !defined(_WIN32)
+ return pthread_mutex_trylock(&log_init_lock);
+#else
+ return 0;
+#endif
+}
+
+LIBLOG_HIDDEN void __android_log_unlock()
+{
+#if !defined(_WIN32)
+ pthread_mutex_unlock(&log_init_lock);
+#endif
+}
diff --git a/liblog/logger_name.c b/liblog/logger_name.c
new file mode 100644
index 0000000..5c4feaf
--- /dev/null
+++ b/liblog/logger_name.c
@@ -0,0 +1,64 @@
+/*
+** Copyright 2013-2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <string.h>
+
+#include <log/log.h>
+
+#include "log_portability.h"
+
+/* In the future, we would like to make this list extensible */
+static const char *LOG_NAME[LOG_ID_MAX] = {
+ [LOG_ID_MAIN] = "main",
+ [LOG_ID_RADIO] = "radio",
+ [LOG_ID_EVENTS] = "events",
+ [LOG_ID_SYSTEM] = "system",
+ [LOG_ID_CRASH] = "crash",
+ [LOG_ID_SECURITY] = "security",
+ [LOG_ID_KERNEL] = "kernel",
+};
+
+LIBLOG_ABI_PUBLIC const char *android_log_id_to_name(log_id_t log_id)
+{
+ if (log_id >= LOG_ID_MAX) {
+ log_id = LOG_ID_MAIN;
+ }
+ return LOG_NAME[log_id];
+}
+
+LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char *logName)
+{
+ const char *b;
+ int ret;
+
+ if (!logName) {
+ return -1; /* NB: log_id_t is unsigned */
+ }
+ b = strrchr(logName, '/');
+ if (!b) {
+ b = logName;
+ } else {
+ ++b;
+ }
+
+ for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
+ const char *l = LOG_NAME[ret];
+ if (l && !strcmp(b, l)) {
+ return ret;
+ }
+ }
+ return -1; /* should never happen */
+}
diff --git a/liblog/logger_read.c b/liblog/logger_read.c
new file mode 100644
index 0000000..c3cb7ad
--- /dev/null
+++ b/liblog/logger_read.c
@@ -0,0 +1,525 @@
+/*
+** Copyright 2013-2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <cutils/list.h>
+#include <private/android_filesystem_config.h>
+
+#include "config_read.h"
+#include "log_portability.h"
+#include "logger.h"
+
+/* android_logger_alloc unimplemented, no use case */
+/* android_logger_free not exported */
+static void android_logger_free(struct logger *logger)
+{
+ struct android_log_logger *logger_internal =
+ (struct android_log_logger *)logger;
+
+ if (!logger_internal) {
+ return;
+ }
+
+ list_remove(&logger_internal->node);
+
+ free(logger_internal);
+}
+
+/* android_logger_alloc unimplemented, no use case */
+
+/* method for getting the associated sublog id */
+LIBLOG_ABI_PUBLIC log_id_t android_logger_get_id(struct logger *logger)
+{
+ return ((struct android_log_logger *)logger)->logId;
+}
+
+static int init_transport_context(struct android_log_logger_list *logger_list)
+{
+ struct android_log_transport_read *transport;
+ struct listnode *node;
+
+ if (!logger_list) {
+ return -EINVAL;
+ }
+
+ if (list_empty(&logger_list->logger)) {
+ return -EINVAL;
+ }
+
+ if (!list_empty(&logger_list->transport)) {
+ return 0;
+ }
+
+ __android_log_lock();
+ /* mini __write_to_log_initialize() to populate transports */
+ if (list_empty(&__android_log_transport_read) &&
+ list_empty(&__android_log_persist_read)) {
+ __android_log_config_read();
+ }
+ __android_log_unlock();
+
+ node = (logger_list->mode & ANDROID_LOG_PSTORE) ?
+ &__android_log_persist_read : &__android_log_transport_read;
+
+ read_transport_for_each(transport, node) {
+ struct android_log_transport_context *transp;
+ struct android_log_logger *logger;
+ unsigned logMask = 0;
+
+ logger_for_each(logger, logger_list) {
+ log_id_t logId = logger->logId;
+
+ if ((logId == LOG_ID_SECURITY) &&
+ (__android_log_uid() != AID_SYSTEM)) {
+ continue;
+ }
+ if (transport->read &&
+ (!transport->available ||
+ (transport->available(logId) >= 0))) {
+ logMask |= 1 << logId;
+ }
+ }
+ if (!logMask) {
+ continue;
+ }
+ transp = calloc(1, sizeof(*transp));
+ if (!transp) {
+ return -ENOMEM;
+ }
+ transp->parent = logger_list;
+ transp->transport = transport;
+ transp->logMask = logMask;
+ transp->ret = 1;
+ list_add_tail(&logger_list->transport, &transp->node);
+ }
+ if (list_empty(&logger_list->transport)) {
+ return -ENODEV;
+ }
+ return 0;
+}
+
+#define LOGGER_FUNCTION(logger, def, func, args...) \
+ ssize_t ret = -EINVAL; \
+ struct android_log_transport_context *transp; \
+ struct android_log_logger *logger_internal = \
+ (struct android_log_logger *)(logger); \
+ \
+ if (!logger_internal) { \
+ return ret; \
+ } \
+ ret = init_transport_context(logger_internal->parent); \
+ if (ret < 0) { \
+ return ret; \
+ } \
+ \
+ ret = (def); \
+ transport_context_for_each(transp, logger_internal->parent) { \
+ if ((transp->logMask & (1 << logger_internal->logId)) && \
+ transp->transport && transp->transport->func) { \
+ ssize_t retval = (transp->transport->func)(logger_internal, \
+ transp, ## args); \
+ if ((ret >= 0) || (ret == (def))) { \
+ ret = retval; \
+ } \
+ } \
+ } \
+ return ret
+
+LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger *logger)
+{
+ LOGGER_FUNCTION(logger, -ENODEV, clear);
+}
+
+/* returns the total size of the log's ring buffer */
+LIBLOG_ABI_PUBLIC long android_logger_get_log_size(struct logger *logger)
+{
+ LOGGER_FUNCTION(logger, -ENODEV, getSize);
+}
+
+LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger *logger,
+ unsigned long size)
+{
+ LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
+}
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size(
+ struct logger *logger)
+{
+ LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
+}
+
+/*
+ * returns the logger version
+ */
+LIBLOG_ABI_PUBLIC int android_logger_get_log_version(struct logger *logger)
+{
+ LOGGER_FUNCTION(logger, 4, version);
+}
+
+#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...) \
+ struct android_log_transport_context *transp; \
+ struct android_log_logger_list *logger_list_internal = \
+ (struct android_log_logger_list *)(logger_list); \
+ \
+ ssize_t ret = init_transport_context(logger_list_internal); \
+ if (ret < 0) { \
+ return ret; \
+ } \
+ \
+ ret = (def); \
+ transport_context_for_each(transp, logger_list_internal) { \
+ if (transp->transport && (transp->transport->func)) { \
+ ssize_t retval = (transp->transport->func)(logger_list_internal, \
+ transp, ## args); \
+ if ((ret >= 0) || (ret == (def))) { \
+ ret = retval; \
+ } \
+ } \
+ } \
+ return ret
+
+/*
+ * returns statistics
+ */
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics(
+ struct logger_list *logger_list,
+ char *buf, size_t len)
+{
+ LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
+}
+
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list(
+ struct logger_list *logger_list,
+ char *buf, size_t len)
+{
+ LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
+}
+
+LIBLOG_ABI_PUBLIC int android_logger_set_prune_list(
+ struct logger_list *logger_list,
+ char *buf, size_t len)
+{
+ LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
+}
+
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc(
+ int mode,
+ unsigned int tail,
+ pid_t pid)
+{
+ struct android_log_logger_list *logger_list;
+
+ logger_list = calloc(1, sizeof(*logger_list));
+ if (!logger_list) {
+ return NULL;
+ }
+
+ list_init(&logger_list->logger);
+ list_init(&logger_list->transport);
+ logger_list->mode = mode;
+ logger_list->tail = tail;
+ logger_list->pid = pid;
+
+ return (struct logger_list *)logger_list;
+}
+
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc_time(
+ int mode,
+ log_time start,
+ pid_t pid)
+{
+ struct android_log_logger_list *logger_list;
+
+ logger_list = calloc(1, sizeof(*logger_list));
+ if (!logger_list) {
+ return NULL;
+ }
+
+ list_init(&logger_list->logger);
+ list_init(&logger_list->transport);
+ logger_list->mode = mode;
+ logger_list->start = start;
+ logger_list->pid = pid;
+
+ return (struct logger_list *)logger_list;
+}
+
+/* android_logger_list_register unimplemented, no use case */
+/* android_logger_list_unregister unimplemented, no use case */
+
+/* Open the named log and add it to the logger list */
+LIBLOG_ABI_PUBLIC struct logger *android_logger_open(
+ struct logger_list *logger_list,
+ log_id_t logId)
+{
+ struct android_log_logger_list *logger_list_internal =
+ (struct android_log_logger_list *)logger_list;
+ struct android_log_logger *logger;
+
+ if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
+ goto err;
+ }
+
+ logger_for_each(logger, logger_list_internal) {
+ if (logger->logId == logId) {
+ goto ok;
+ }
+ }
+
+ logger = calloc(1, sizeof(*logger));
+ if (!logger) {
+ goto err;
+ }
+
+ logger->logId = logId;
+ list_add_tail(&logger_list_internal->logger, &logger->node);
+ logger->parent = logger_list_internal;
+
+ /* Reset known transports to re-evaluate, we just added one */
+ while (!list_empty(&logger_list_internal->transport)) {
+ struct listnode *node = list_head(&logger_list_internal->transport);
+ struct android_log_transport_context *transp =
+ node_to_item(node, struct android_log_transport_context, node);
+
+ list_remove(&transp->node);
+ free(transp);
+ }
+ goto ok;
+
+err:
+ logger = NULL;
+ok:
+ return (struct logger *)logger;
+}
+
+/* Open the single named log and make it part of a new logger list */
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_open(
+ log_id_t logId,
+ int mode,
+ unsigned int tail,
+ pid_t pid)
+{
+ struct logger_list *logger_list =
+ android_logger_list_alloc(mode, tail, pid);
+
+ if (!logger_list) {
+ return NULL;
+ }
+
+ if (!android_logger_open(logger_list, logId)) {
+ android_logger_list_free(logger_list);
+ return NULL;
+ }
+
+ return logger_list;
+}
+
+/* Validate log_msg packet, read function has already been null checked */
+static int android_transport_read(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ struct log_msg *log_msg)
+{
+ int ret = (*transp->transport->read)(logger_list, transp, log_msg);
+
+ if (ret > (int)sizeof(*log_msg)) {
+ ret = sizeof(*log_msg);
+ }
+
+ transp->ret = ret;
+
+ /* propagate errors, or make sure len & hdr_size members visible */
+ if (ret < (int)(sizeof(log_msg->entry.len) +
+ sizeof(log_msg->entry.hdr_size))) {
+ if (ret >= (int)sizeof(log_msg->entry.len)) {
+ log_msg->entry.len = 0;
+ }
+ return ret;
+ }
+
+ /* hdr_size correction (logger_entry -> logger_entry_v2+ conversion) */
+ if (log_msg->entry_v2.hdr_size == 0) {
+ log_msg->entry_v2.hdr_size = sizeof(struct logger_entry);
+ }
+ if ((log_msg->entry_v2.hdr_size < sizeof(log_msg->entry_v1)) ||
+ (log_msg->entry_v2.hdr_size > sizeof(log_msg->entry))) {
+ return -EINVAL;
+ }
+
+ /* len validation */
+ if (ret <= log_msg->entry_v2.hdr_size) {
+ log_msg->entry.len = 0;
+ } else {
+ log_msg->entry.len = ret - log_msg->entry_v2.hdr_size;
+ }
+
+ return ret;
+}
+
+/* Read from the selected logs */
+LIBLOG_ABI_PUBLIC int android_logger_list_read(struct logger_list *logger_list,
+ struct log_msg *log_msg)
+{
+ struct android_log_transport_context *transp;
+ struct android_log_logger_list *logger_list_internal =
+ (struct android_log_logger_list *)logger_list;
+
+ int ret = init_transport_context(logger_list_internal);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* at least one transport */
+ transp = node_to_item(logger_list_internal->transport.next,
+ struct android_log_transport_context, node);
+
+ /* more than one transport? */
+ if (transp->node.next != &logger_list_internal->transport) {
+ /* Poll and merge sort the entries if from multiple transports */
+ struct android_log_transport_context *oldest = NULL;
+ int ret;
+ int polled = 0;
+ do {
+ if (polled) {
+ sched_yield();
+ }
+ ret = -1000;
+ polled = 0;
+ do {
+ int retval = transp->ret;
+ if ((retval > 0) && !transp->logMsg.entry.len) {
+ if (!transp->transport->read) {
+ retval = transp->ret = 0;
+ } else if ((logger_list_internal->mode &
+ ANDROID_LOG_NONBLOCK) ||
+ !transp->transport->poll) {
+ retval = android_transport_read(
+ logger_list_internal,
+ transp,
+ &transp->logMsg);
+ } else {
+ int pollval = (*transp->transport->poll)(
+ logger_list_internal, transp);
+ if (pollval <= 0) {
+ sched_yield();
+ pollval = (*transp->transport->poll)(
+ logger_list_internal, transp);
+ }
+ polled = 1;
+ if (pollval < 0) {
+ if ((pollval == -EINTR) || (pollval == -EAGAIN)) {
+ return -EAGAIN;
+ }
+ retval = transp->ret = pollval;
+ } else if (pollval > 0) {
+ retval = android_transport_read(
+ logger_list_internal,
+ transp,
+ &transp->logMsg);
+ }
+ }
+ }
+ if (ret < retval) {
+ ret = retval;
+ }
+ if ((transp->ret > 0) && transp->logMsg.entry.len &&
+ (!oldest ||
+ (oldest->logMsg.entry.sec >
+ transp->logMsg.entry.sec) ||
+ ((oldest->logMsg.entry.sec ==
+ transp->logMsg.entry.sec) &&
+ (oldest->logMsg.entry.nsec >
+ transp->logMsg.entry.nsec)))) {
+ oldest = transp;
+ }
+ transp = node_to_item(transp->node.next,
+ struct android_log_transport_context,
+ node);
+ } while (transp != node_to_item(
+ &logger_list_internal->transport,
+ struct android_log_transport_context,
+ node));
+ if (!oldest &&
+ (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) {
+ return (ret < 0) ? ret : -EAGAIN;
+ }
+ transp = node_to_item(logger_list_internal->transport.next,
+ struct android_log_transport_context, node);
+ } while (!oldest && (ret > 0));
+ if (!oldest) {
+ return ret;
+ }
+ // ret is a positive value less than sizeof(struct log_msg)
+ ret = oldest->ret;
+ if (ret < oldest->logMsg.entry.hdr_size) {
+ // zero truncated header fields.
+ memset(log_msg, 0,
+ (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg) ?
+ sizeof(oldest->logMsg) :
+ oldest->logMsg.entry.hdr_size));
+ }
+ memcpy(log_msg, &oldest->logMsg, ret);
+ oldest->logMsg.entry.len = 0; /* Mark it as copied */
+ return ret;
+ }
+
+ /* if only one, no need to copy into transport_context and merge-sort */
+ return android_transport_read(logger_list_internal, transp, log_msg);
+}
+
+/* Close all the logs */
+LIBLOG_ABI_PUBLIC void android_logger_list_free(struct logger_list *logger_list)
+{
+ struct android_log_logger_list *logger_list_internal =
+ (struct android_log_logger_list *)logger_list;
+
+ if (logger_list_internal == NULL) {
+ return;
+ }
+
+ while (!list_empty(&logger_list_internal->transport)) {
+ struct listnode *node = list_head(&logger_list_internal->transport);
+ struct android_log_transport_context *transp =
+ node_to_item(node, struct android_log_transport_context, node);
+
+ if (transp->transport && transp->transport->close) {
+ (*transp->transport->close)(logger_list_internal, transp);
+ }
+ list_remove(&transp->node);
+ free(transp);
+ }
+
+ while (!list_empty(&logger_list_internal->logger)) {
+ struct listnode *node = list_head(&logger_list_internal->logger);
+ struct android_log_logger *logger =
+ node_to_item(node, struct android_log_logger, node);
+ android_logger_free((struct logger *)logger);
+ }
+
+ free(logger_list_internal);
+}
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
new file mode 100644
index 0000000..1a2d506
--- /dev/null
+++ b/liblog/logger_write.c
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#ifdef __BIONIC__
+#include <android/set_abort_message.h>
+#endif
+
+#include <log/event_tag_map.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+
+#define LOG_BUF_SIZE 1024
+
+static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
+static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
+
+/*
+ * This is used by the C++ code to decide if it should write logs through
+ * the C code. Basically, if /dev/socket/logd is available, we're running in
+ * the simulator rather than a desktop tool and want to use the device.
+ */
+static enum {
+ kLogUninitialized, kLogNotAvailable, kLogAvailable
+} g_log_status = kLogUninitialized;
+
+static int check_log_uid_permissions()
+{
+#if defined(__ANDROID__)
+ uid_t uid = __android_log_uid();
+
+ /* Matches clientHasLogCredentials() in logd */
+ if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+ uid = geteuid();
+ if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+ gid_t gid = getgid();
+ if ((gid != AID_SYSTEM) &&
+ (gid != AID_ROOT) &&
+ (gid != AID_LOG)) {
+ gid = getegid();
+ if ((gid != AID_SYSTEM) &&
+ (gid != AID_ROOT) &&
+ (gid != AID_LOG)) {
+ int num_groups;
+ gid_t *groups;
+
+ num_groups = getgroups(0, NULL);
+ if (num_groups <= 0) {
+ return -EPERM;
+ }
+ groups = calloc(num_groups, sizeof(gid_t));
+ if (!groups) {
+ return -ENOMEM;
+ }
+ num_groups = getgroups(num_groups, groups);
+ while (num_groups > 0) {
+ if (groups[num_groups - 1] == AID_LOG) {
+ break;
+ }
+ --num_groups;
+ }
+ free(groups);
+ if (num_groups <= 0) {
+ return -EPERM;
+ }
+ }
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+static void __android_log_cache_available(
+ struct android_log_transport_write *node)
+{
+ size_t i;
+
+ if (node->logMask) {
+ return;
+ }
+
+ for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ if (node->write &&
+ (i != LOG_ID_KERNEL) &&
+ ((i != LOG_ID_SECURITY) ||
+ (check_log_uid_permissions() == 0)) &&
+ (!node->available || ((*node->available)(i) >= 0))) {
+ node->logMask |= 1 << i;
+ }
+ }
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_dev_available()
+{
+ struct android_log_transport_write *node;
+
+ if (list_empty(&__android_log_transport_write)) {
+ return kLogUninitialized;
+ }
+
+ write_transport_for_each(node, &__android_log_transport_write) {
+ __android_log_cache_available(node);
+ if (node->logMask) {
+ return kLogAvailable;
+ }
+ }
+ return kLogNotAvailable;
+}
+
+#if defined(__ANDROID__)
+static atomic_uintptr_t tagMap;
+#endif
+
+/*
+ * Release any logger resources. A new log write will immediately re-acquire.
+ */
+LIBLOG_ABI_PUBLIC void __android_log_close()
+{
+ struct android_log_transport_write *transport;
+#if defined(__ANDROID__)
+ EventTagMap *m;
+#endif
+
+ __android_log_lock();
+
+ write_to_log = __write_to_log_init;
+
+ /*
+ * Threads that are actively writing at this point are not held back
+ * by a lock and are at risk of dropping the messages with a return code
+ * -EBADF. Prefer to return error code than add the overhead of a lock to
+ * each log writing call to guarantee delivery. In addition, anyone
+ * calling this is doing so to release the logging resources and shut down,
+ * for them to do so with outstanding log requests in other threads is a
+ * disengenuous use of this function.
+ */
+
+ write_transport_for_each(transport, &__android_log_persist_write) {
+ if (transport->close) {
+ (*transport->close)();
+ }
+ }
+
+ write_transport_for_each(transport, &__android_log_transport_write) {
+ if (transport->close) {
+ (*transport->close)();
+ }
+ }
+
+#if defined(__ANDROID__)
+ /*
+ * Additional risk here somewhat mitigated by immediately unlock flushing
+ * the processor cache. The multi-threaded race that we choose to accept,
+ * to minimize locking, is an atomic_load in a writer picking up a value
+ * just prior to entering this routine. There will be an use after free.
+ *
+ * Again, anyone calling this is doing so to release the logging resources
+ * is most probably going to quiesce then shut down; or to restart after
+ * a fork so the risk should be non-existent. For this reason we
+ * choose a mitigation stance for efficiency instead of incuring the cost
+ * of a lock for every log write.
+ */
+ m = (EventTagMap *)atomic_exchange(&tagMap, (uintptr_t)0);
+#endif
+
+ __android_log_unlock();
+
+#if defined(__ANDROID__)
+ if (m != (EventTagMap *)(uintptr_t)-1LL) android_closeEventTagMap(m);
+#endif
+
+}
+
+/* log_init_lock assumed */
+static int __write_to_log_initialize()
+{
+ struct android_log_transport_write *transport;
+ struct listnode *n;
+ int i = 0, ret = 0;
+
+ __android_log_config_write();
+ write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
+ __android_log_cache_available(transport);
+ if (!transport->logMask) {
+ list_remove(&transport->node);
+ continue;
+ }
+ if (!transport->open || ((*transport->open)() < 0)) {
+ if (transport->close) {
+ (*transport->close)();
+ }
+ list_remove(&transport->node);
+ continue;
+ }
+ ++ret;
+ }
+ write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
+ __android_log_cache_available(transport);
+ if (!transport->logMask) {
+ list_remove(&transport->node);
+ continue;
+ }
+ if (!transport->open || ((*transport->open)() < 0)) {
+ if (transport->close) {
+ (*transport->close)();
+ }
+ list_remove(&transport->node);
+ continue;
+ }
+ ++i;
+ }
+ if (!ret && !i) {
+ return -ENODEV;
+ }
+
+ return ret;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream. le32toh open coded
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+ struct android_log_transport_write *node;
+ int ret;
+ struct timespec ts;
+ size_t len, i;
+
+ for (len = i = 0; i < nr; ++i) {
+ len += vec[i].iov_len;
+ }
+ if (!len) {
+ return -EINVAL;
+ }
+
+#if defined(__ANDROID__)
+ clock_gettime(android_log_clockid(), &ts);
+
+ if (log_id == LOG_ID_SECURITY) {
+ if (vec[0].iov_len < 4) {
+ return -EINVAL;
+ }
+
+ ret = check_log_uid_permissions();
+ if (ret < 0) {
+ return ret;
+ }
+ if (!__android_log_security()) {
+ /* If only we could reset downstream logd counter */
+ return -EPERM;
+ }
+ } else if (log_id == LOG_ID_EVENTS) {
+ const char *tag;
+ size_t len;
+ EventTagMap *m, *f;
+
+ if (vec[0].iov_len < 4) {
+ return -EINVAL;
+ }
+
+ tag = NULL;
+ len = 0;
+ f = NULL;
+ m = (EventTagMap *)atomic_load(&tagMap);
+
+ if (!m) {
+ ret = __android_log_trylock();
+ m = (EventTagMap *)atomic_load(&tagMap); /* trylock flush cache */
+ if (!m) {
+ m = android_openEventTagMap(NULL);
+ if (ret) { /* trylock failed, use local copy, mark for close */
+ f = m;
+ } else {
+ if (!m) { /* One chance to open map file */
+ m = (EventTagMap *)(uintptr_t)-1LL;
+ }
+ atomic_store(&tagMap, (uintptr_t)m);
+ }
+ }
+ if (!ret) { /* trylock succeeded, unlock */
+ __android_log_unlock();
+ }
+ }
+ if (m && (m != (EventTagMap *)(uintptr_t)-1LL)) {
+ tag = android_lookupEventTag_len(m, &len, get4LE(vec[0].iov_base));
+ }
+ ret = __android_log_is_loggable_len(ANDROID_LOG_INFO,
+ tag, len,
+ ANDROID_LOG_VERBOSE);
+ if (f) { /* local copy marked for close */
+ android_closeEventTagMap(f);
+ }
+ if (!ret) {
+ return -EPERM;
+ }
+ } else {
+ /* Validate the incoming tag, tag content can not split across iovec */
+ char prio = ANDROID_LOG_VERBOSE;
+ const char *tag = vec[0].iov_base;
+ size_t len = vec[0].iov_len;
+ if (!tag) {
+ len = 0;
+ }
+ if (len > 0) {
+ prio = *tag;
+ if (len > 1) {
+ --len;
+ ++tag;
+ } else {
+ len = vec[1].iov_len;
+ tag = ((const char *)vec[1].iov_base);
+ if (!tag) {
+ len = 0;
+ }
+ }
+ }
+ /* tag must be nul terminated */
+ if (strnlen(tag, len) >= len) {
+ tag = NULL;
+ }
+
+ if (!__android_log_is_loggable_len(prio,
+ tag, len - 1,
+ ANDROID_LOG_VERBOSE)) {
+ return -EPERM;
+ }
+ }
+#else
+ /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
+ {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ }
+#endif
+
+ ret = 0;
+ i = 1 << log_id;
+ write_transport_for_each(node, &__android_log_transport_write) {
+ if (node->logMask & i) {
+ ssize_t retval;
+ retval = (*node->write)(log_id, &ts, vec, nr);
+ if (ret >= 0) {
+ ret = retval;
+ }
+ }
+ }
+
+ write_transport_for_each(node, &__android_log_persist_write) {
+ if (node->logMask & i) {
+ (void)(*node->write)(log_id, &ts, vec, nr);
+ }
+ }
+
+ return ret;
+}
+
+static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+ __android_log_lock();
+
+ if (write_to_log == __write_to_log_init) {
+ int ret;
+
+ ret = __write_to_log_initialize();
+ if (ret < 0) {
+ __android_log_unlock();
+ if (!list_empty(&__android_log_persist_write)) {
+ __write_to_log_daemon(log_id, vec, nr);
+ }
+ return ret;
+ }
+
+ write_to_log = __write_to_log_daemon;
+ }
+
+ __android_log_unlock();
+
+ return write_to_log(log_id, vec, nr);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char *tag,
+ const char *msg)
+{
+ return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
+ const char *tag, const char *msg)
+{
+ struct iovec vec[3];
+ char tmp_tag[32];
+
+ if (!tag)
+ tag = "";
+
+ /* XXX: This needs to go! */
+ if ((bufID != LOG_ID_RADIO) &&
+ (!strcmp(tag, "HTC_RIL") ||
+ !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
+ !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
+ !strcmp(tag, "AT") ||
+ !strcmp(tag, "GSM") ||
+ !strcmp(tag, "STK") ||
+ !strcmp(tag, "CDMA") ||
+ !strcmp(tag, "PHONE") ||
+ !strcmp(tag, "SMS"))) {
+ bufID = LOG_ID_RADIO;
+ /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
+ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+ tag = tmp_tag;
+ }
+
+#if __BIONIC__
+ if (prio == ANDROID_LOG_FATAL) {
+ android_set_abort_message(msg);
+ }
+#endif
+
+ vec[0].iov_base = (unsigned char *)&prio;
+ vec[0].iov_len = 1;
+ vec[1].iov_base = (void *)tag;
+ vec[1].iov_len = strlen(tag) + 1;
+ vec[2].iov_base = (void *)msg;
+ vec[2].iov_len = strlen(msg) + 1;
+
+ return write_to_log(bufID, vec, 3);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char *tag,
+ const char *fmt, va_list ap)
+{
+ char buf[LOG_BUF_SIZE];
+
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+ return __android_log_write(prio, tag, buf);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char *tag,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ return __android_log_write(prio, tag, buf);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio,
+ const char *tag,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ return __android_log_buf_write(bufID, prio, tag, buf);
+}
+
+LIBLOG_ABI_PUBLIC void __android_log_assert(const char *cond, const char *tag,
+ const char *fmt, ...)
+{
+ char buf[LOG_BUF_SIZE];
+
+ if (fmt) {
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+ } else {
+ /* Msg not provided, log condition. N.B. Do not use cond directly as
+ * format string as it could contain spurious '%' syntax (e.g.
+ * "%d" in "blocks%devs == 0").
+ */
+ if (cond)
+ snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+ else
+ strcpy(buf, "Unspecified assertion failed");
+ }
+
+ __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+ abort(); /* abort so we have a chance to debug the situation */
+ /* NOTREACHED */
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag,
+ const void *payload, size_t len)
+{
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 2);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
+ const void *payload,
+ size_t len)
+{
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_SECURITY, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well. Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type,
+ const void *payload, size_t len)
+{
+ struct iovec vec[3];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = (void*)payload;
+ vec[2].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
+
+/*
+ * Like __android_log_bwrite, but used for writing strings to the
+ * event log.
+ */
+LIBLOG_ABI_PUBLIC int __android_log_bswrite(int32_t tag, const char *payload)
+{
+ struct iovec vec[4];
+ char type = EVENT_TYPE_STRING;
+ uint32_t len = strlen(payload);
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = &len;
+ vec[2].iov_len = sizeof(len);
+ vec[3].iov_base = (void*)payload;
+ vec[3].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 4);
+}
+
+/*
+ * Like __android_log_security_bwrite, but used for writing strings to the
+ * security log.
+ */
+LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag,
+ const char *payload)
+{
+ struct iovec vec[4];
+ char type = EVENT_TYPE_STRING;
+ uint32_t len = strlen(payload);
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = &len;
+ vec[2].iov_len = sizeof(len);
+ vec[3].iov_base = (void*)payload;
+ vec[3].iov_len = len;
+
+ return write_to_log(LOG_ID_SECURITY, vec, 4);
+}
diff --git a/liblog/logprint.c b/liblog/logprint.c
index c2f1545..18af9de 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -21,19 +21,24 @@
#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <inttypes.h>
+#include <pwd.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <inttypes.h>
#include <sys/param.h>
+#include <sys/types.h>
-#include <log/logd.h>
+#include <cutils/list.h>
+#include <log/log.h>
#include <log/logprint.h>
-/* open coded fragment, prevent circular dependencies */
-#define WEAK static
+#include "log_portability.h"
+
+#define MS_PER_NSEC 1000000
+#define US_PER_NSEC 1000
typedef struct FilterInfo_t {
char *mTag;
@@ -47,10 +52,23 @@
AndroidLogPrintFormat format;
bool colored_output;
bool usec_time_output;
+ bool nsec_time_output;
bool printable_output;
+ bool year_output;
+ bool zone_output;
+ bool epoch_output;
+ bool monotonic_output;
+ bool uid_output;
+ bool descriptive_output;
};
/*
+ * API issues prevent us from exposing "descriptive" in AndroidLogFormat_t
+ * during android_log_processBinaryLogBuffer(), so we break layering.
+ */
+static bool descriptive_output = false;
+
+/*
* gnome-terminal color tags
* See http://misc.flogisoft.com/bash/tip_colors_and_formatting
* for ideas on how to set the forground color of the text for xterm.
@@ -175,13 +193,15 @@
* returns 1 if this log line should be printed based on its priority
* and tag, and 0 if it should not
*/
-int android_log_shouldPrintLine (
- AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
+LIBLOG_ABI_PUBLIC int android_log_shouldPrintLine (
+ AndroidLogFormat *p_format,
+ const char *tag,
+ android_LogPriority pri)
{
return pri >= filterPriForTag(p_format, tag);
}
-AndroidLogFormat *android_log_format_new()
+LIBLOG_ABI_PUBLIC AndroidLogFormat *android_log_format_new()
{
AndroidLogFormat *p_ret;
@@ -191,12 +211,22 @@
p_ret->format = FORMAT_BRIEF;
p_ret->colored_output = false;
p_ret->usec_time_output = false;
+ p_ret->nsec_time_output = false;
p_ret->printable_output = false;
+ p_ret->year_output = false;
+ p_ret->zone_output = false;
+ p_ret->epoch_output = false;
+ p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
+ p_ret->uid_output = false;
+ p_ret->descriptive_output = false;
+ descriptive_output = false;
return p_ret;
}
-void android_log_format_free(AndroidLogFormat *p_format)
+static list_declare(convertHead);
+
+LIBLOG_ABI_PUBLIC void android_log_format_free(AndroidLogFormat *p_format)
{
FilterInfo *p_info, *p_info_old;
@@ -210,11 +240,17 @@
}
free(p_format);
+
+ /* Free conversion resource, can always be reconstructed */
+ while (!list_empty(&convertHead)) {
+ struct listnode *node = list_head(&convertHead);
+ list_remove(node);
+ free(node);
+ }
}
-
-
-int android_log_setPrintFormat(AndroidLogFormat *p_format,
+LIBLOG_ABI_PUBLIC int android_log_setPrintFormat(
+ AndroidLogFormat *p_format,
AndroidLogPrintFormat format)
{
switch (format) {
@@ -224,9 +260,31 @@
case FORMAT_MODIFIER_TIME_USEC:
p_format->usec_time_output = true;
return 0;
+ case FORMAT_MODIFIER_TIME_NSEC:
+ p_format->nsec_time_output = true;
+ return 0;
case FORMAT_MODIFIER_PRINTABLE:
p_format->printable_output = true;
return 0;
+ case FORMAT_MODIFIER_YEAR:
+ p_format->year_output = true;
+ return 0;
+ case FORMAT_MODIFIER_ZONE:
+ p_format->zone_output = !p_format->zone_output;
+ return 0;
+ case FORMAT_MODIFIER_EPOCH:
+ p_format->epoch_output = true;
+ return 0;
+ case FORMAT_MODIFIER_MONOTONIC:
+ p_format->monotonic_output = true;
+ return 0;
+ case FORMAT_MODIFIER_UID:
+ p_format->uid_output = true;
+ return 0;
+ case FORMAT_MODIFIER_DESCRIPT:
+ p_format->descriptive_output = true;
+ descriptive_output = true;
+ return 0;
default:
break;
}
@@ -234,10 +292,14 @@
return 1;
}
+static const char tz[] = "TZ";
+static const char utc[] = "UTC";
+
/**
* Returns FORMAT_OFF on invalid string
*/
-AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
+LIBLOG_ABI_PUBLIC AndroidLogPrintFormat android_log_formatFromString(
+ const char * formatString)
{
static AndroidLogPrintFormat format;
@@ -250,9 +312,47 @@
else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR;
+ else if (strcmp(formatString, "colour") == 0) format = FORMAT_MODIFIER_COLOR;
else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC;
+ else if (strcmp(formatString, "nsec") == 0) format = FORMAT_MODIFIER_TIME_NSEC;
else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
- else format = FORMAT_OFF;
+ else if (strcmp(formatString, "year") == 0) format = FORMAT_MODIFIER_YEAR;
+ else if (strcmp(formatString, "zone") == 0) format = FORMAT_MODIFIER_ZONE;
+ else if (strcmp(formatString, "epoch") == 0) format = FORMAT_MODIFIER_EPOCH;
+ else if (strcmp(formatString, "monotonic") == 0) format = FORMAT_MODIFIER_MONOTONIC;
+ else if (strcmp(formatString, "uid") == 0) format = FORMAT_MODIFIER_UID;
+ else if (strcmp(formatString, "descriptive") == 0) format = FORMAT_MODIFIER_DESCRIPT;
+ else {
+ extern char *tzname[2];
+ static const char gmt[] = "GMT";
+ char *cp = getenv(tz);
+ if (cp) {
+ cp = strdup(cp);
+ }
+ setenv(tz, formatString, 1);
+ /*
+ * Run tzset here to determine if the timezone is legitimate. If the
+ * zone is GMT, check if that is what was asked for, if not then
+ * did not match any on the system; report an error to caller.
+ */
+ tzset();
+ if (!tzname[0]
+ || ((!strcmp(tzname[0], utc)
+ || !strcmp(tzname[0], gmt)) /* error? */
+ && strcasecmp(formatString, utc)
+ && strcasecmp(formatString, gmt))) { /* ok */
+ if (cp) {
+ setenv(tz, cp, 1);
+ } else {
+ unsetenv(tz);
+ }
+ tzset();
+ format = FORMAT_OFF;
+ } else {
+ format = FORMAT_MODIFIER_ZONE;
+ }
+ free(cp);
+ }
return format;
}
@@ -266,7 +366,8 @@
* Assumes single threaded execution
*/
-int android_log_addFilterRule(AndroidLogFormat *p_format,
+LIBLOG_ABI_PUBLIC int android_log_addFilterRule(
+ AndroidLogFormat *p_format,
const char *filterExpression)
{
size_t tagNameLength;
@@ -344,7 +445,8 @@
*
*/
-int android_log_addFilterString(AndroidLogFormat *p_format,
+LIBLOG_ABI_PUBLIC int android_log_addFilterString(
+ AndroidLogFormat *p_format,
const char *filterString)
{
char *filterStringCopy = strdup (filterString);
@@ -378,11 +480,13 @@
* Returns 0 on success and -1 on invalid wire format (entry will be
* in unspecified state)
*/
-int android_log_processLogBuffer(struct logger_entry *buf,
- AndroidLogEntry *entry)
+LIBLOG_ABI_PUBLIC int android_log_processLogBuffer(
+ struct logger_entry *buf,
+ AndroidLogEntry *entry)
{
entry->tv_sec = buf->sec;
entry->tv_nsec = buf->nsec;
+ entry->uid = -1;
entry->pid = buf->pid;
entry->tid = buf->tid;
@@ -413,7 +517,15 @@
char *msg = buf->msg;
struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
if (buf2->hdr_size) {
+ if ((buf2->hdr_size < sizeof(((struct log_msg *)NULL)->entry_v1)) ||
+ (buf2->hdr_size > sizeof(((struct log_msg *)NULL)->entry))) {
+ fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+ return -1;
+ }
msg = ((char *)buf2) + buf2->hdr_size;
+ if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
+ entry->uid = ((struct logger_entry_v4 *)buf)->uid;
+ }
}
for (i = 1; i < buf->len; i++) {
if (msg[i] == '\0') {
@@ -427,19 +539,30 @@
}
if (msgStart == -1) {
- fprintf(stderr, "+++ LOG: malformed log message\n");
- return -1;
+ /* +++ LOG: malformed log message, DYB */
+ for (i = 1; i < buf->len; i++) {
+ /* odd characters in tag? */
+ if ((msg[i] <= ' ') || (msg[i] == ':') || (msg[i] >= 0x7f)) {
+ msg[i] = '\0';
+ msgStart = i + 1;
+ break;
+ }
+ }
+ if (msgStart == -1) {
+ msgStart = buf->len - 1; /* All tag, no message, print truncates */
+ }
}
if (msgEnd == -1) {
/* incoming message not null-terminated; force it */
- msgEnd = buf->len - 1;
+ msgEnd = buf->len - 1; /* may result in msgEnd < msgStart */
msg[msgEnd] = '\0';
}
entry->priority = msg[0];
entry->tag = msg + 1;
+ entry->tagLen = msgStart - 1;
entry->message = msg + msgStart;
- entry->messageLen = msgEnd - msgStart;
+ entry->messageLen = (msgEnd < msgStart) ? 0 : (msgEnd - msgStart);
return 0;
}
@@ -464,6 +587,19 @@
return ((uint64_t) high << 32) | (uint64_t) low;
}
+static bool findChar(const char** cp, size_t* len, int c) {
+ while (*len && isspace(**cp)) {
+ ++*cp;
+ --*len;
+ }
+ if (c == INT_MAX) return *len;
+ if (*len && (**cp == c)) {
+ ++*cp;
+ --*len;
+ return true;
+ }
+ return false;
+}
/*
* Recursively convert binary log data to printable form.
@@ -476,63 +612,151 @@
*
* Returns 0 on success, 1 on buffer full, -1 on failure.
*/
+enum objectType {
+ TYPE_OBJECTS = '1',
+ TYPE_BYTES = '2',
+ TYPE_MILLISECONDS = '3',
+ TYPE_ALLOCATIONS = '4',
+ TYPE_ID = '5',
+ TYPE_PERCENT = '6'
+};
+
static int android_log_printBinaryEvent(const unsigned char** pEventData,
- size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
+ size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen,
+ const char** fmtStr, size_t* fmtLen)
{
const unsigned char* eventData = *pEventData;
size_t eventDataLen = *pEventDataLen;
char* outBuf = *pOutBuf;
+ char* outBufSave = outBuf;
size_t outBufLen = *pOutBufLen;
+ size_t outBufLenSave = outBufLen;
unsigned char type;
size_t outCount;
int result = 0;
+ const char* cp;
+ size_t len;
+ int64_t lval;
- if (eventDataLen < 1)
- return -1;
+ if (eventDataLen < 1) return -1;
+
type = *eventData++;
eventDataLen--;
+ cp = NULL;
+ len = 0;
+ if (fmtStr && *fmtStr && fmtLen && *fmtLen && **fmtStr) {
+ cp = *fmtStr;
+ len = *fmtLen;
+ }
+ /*
+ * event.logtag format specification:
+ *
+ * Optionally, after the tag names can be put a description for the value(s)
+ * of the tag. Description are in the format
+ * (<name>|data type[|data unit])
+ * Multiple values are separated by commas.
+ *
+ * The data type is a number from the following values:
+ * 1: int
+ * 2: long
+ * 3: string
+ * 4: list
+ * 5: float
+ *
+ * The data unit is a number taken from the following list:
+ * 1: Number of objects
+ * 2: Number of bytes
+ * 3: Number of milliseconds
+ * 4: Number of allocations
+ * 5: Id
+ * 6: Percent
+ * Default value for data of type int/long is 2 (bytes).
+ */
+ if (!cp || !findChar(&cp, &len, '(')) {
+ len = 0;
+ } else {
+ char* outBufLastSpace = NULL;
+
+ findChar(&cp, &len, INT_MAX);
+ while (len && *cp && (*cp != '|') && (*cp != ')')) {
+ if (outBufLen <= 0) {
+ /* halt output */
+ goto no_room;
+ }
+ outBufLastSpace = isspace(*cp) ? outBuf : NULL;
+ *outBuf = *cp;
+ ++outBuf;
+ ++cp;
+ --outBufLen;
+ --len;
+ }
+ if (outBufLastSpace) {
+ outBufLen += outBuf - outBufLastSpace;
+ outBuf = outBufLastSpace;
+ }
+ if (outBufLen <= 0) {
+ /* halt output */
+ goto no_room;
+ }
+ if (outBufSave != outBuf) {
+ *outBuf = '=';
+ ++outBuf;
+ --outBufLen;
+ }
+
+ if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+ static const unsigned char typeTable[] = {
+ EVENT_TYPE_INT,
+ EVENT_TYPE_LONG,
+ EVENT_TYPE_STRING,
+ EVENT_TYPE_LIST,
+ EVENT_TYPE_FLOAT
+ };
+
+ if ((*cp >= '1') &&
+ (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
+ (type != typeTable[(size_t)(*cp - '1')])) len = 0;
+
+ if (len) {
+ ++cp;
+ --len;
+ } else {
+ /* reset the format */
+ outBuf = outBufSave;
+ outBufLen = outBufLenSave;
+ }
+ }
+ }
+ lval = 0;
switch (type) {
case EVENT_TYPE_INT:
/* 32-bit signed int */
{
- int ival;
+ int32_t ival;
- if (eventDataLen < 4)
- return -1;
+ if (eventDataLen < 4) return -1;
ival = get4LE(eventData);
eventData += 4;
eventDataLen -= 4;
- outCount = snprintf(outBuf, outBufLen, "%d", ival);
- if (outCount < outBufLen) {
- outBuf += outCount;
- outBufLen -= outCount;
- } else {
- /* halt output */
- goto no_room;
- }
+ lval = ival;
}
- break;
+ goto pr_lval;
case EVENT_TYPE_LONG:
/* 64-bit signed long */
- {
- uint64_t lval;
-
- if (eventDataLen < 8)
- return -1;
- lval = get8LE(eventData);
- eventData += 8;
- eventDataLen -= 8;
-
- outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
- if (outCount < outBufLen) {
- outBuf += outCount;
- outBufLen -= outCount;
- } else {
- /* halt output */
- goto no_room;
- }
+ if (eventDataLen < 8) return -1;
+ lval = get8LE(eventData);
+ eventData += 8;
+ eventDataLen -= 8;
+ pr_lval:
+ outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else {
+ /* halt output */
+ goto no_room;
}
break;
case EVENT_TYPE_FLOAT:
@@ -541,8 +765,7 @@
uint32_t ival;
float fval;
- if (eventDataLen < 4)
- return -1;
+ if (eventDataLen < 4) return -1;
ival = get4LE(eventData);
fval = *(float*)&ival;
eventData += 4;
@@ -563,15 +786,18 @@
{
unsigned int strLen;
- if (eventDataLen < 4)
- return -1;
+ if (eventDataLen < 4) return -1;
strLen = get4LE(eventData);
eventData += 4;
eventDataLen -= 4;
- if (eventDataLen < strLen)
- return -1;
+ if (eventDataLen < strLen) return -1;
+ if (cp && (strLen == 0)) {
+ /* reset the format if no content */
+ outBuf = outBufSave;
+ outBufLen = outBufLenSave;
+ }
if (strLen < outBufLen) {
memcpy(outBuf, eventData, strLen);
outBuf += strLen;
@@ -593,53 +819,122 @@
unsigned char count;
int i;
- if (eventDataLen < 1)
- return -1;
+ if (eventDataLen < 1) return -1;
count = *eventData++;
eventDataLen--;
- if (outBufLen > 0) {
- *outBuf++ = '[';
- outBufLen--;
- } else {
- goto no_room;
- }
+ if (outBufLen <= 0) goto no_room;
+
+ *outBuf++ = '[';
+ outBufLen--;
for (i = 0; i < count; i++) {
result = android_log_printBinaryEvent(&eventData, &eventDataLen,
- &outBuf, &outBufLen);
- if (result != 0)
- goto bail;
+ &outBuf, &outBufLen, fmtStr, fmtLen);
+ if (result != 0) goto bail;
- if (i < count-1) {
- if (outBufLen > 0) {
- *outBuf++ = ',';
- outBufLen--;
- } else {
- goto no_room;
- }
+ if (i < (count - 1)) {
+ if (outBufLen <= 0) goto no_room;
+ *outBuf++ = ',';
+ outBufLen--;
}
}
- if (outBufLen > 0) {
- *outBuf++ = ']';
- outBufLen--;
- } else {
- goto no_room;
- }
+ if (outBufLen <= 0) goto no_room;
+
+ *outBuf++ = ']';
+ outBufLen--;
}
break;
default:
fprintf(stderr, "Unknown binary event type %d\n", type);
return -1;
}
+ if (cp && len) {
+ if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+ switch (*cp) {
+ case TYPE_OBJECTS:
+ outCount = 0;
+ /* outCount = snprintf(outBuf, outBufLen, " objects"); */
+ break;
+ case TYPE_BYTES:
+ if ((lval != 0) && ((lval % 1024) == 0)) {
+ /* repaint with multiplier */
+ static const char suffixTable[] = { 'K', 'M', 'G', 'T' };
+ size_t idx = 0;
+ outBuf -= outCount;
+ outBufLen += outCount;
+ do {
+ lval /= 1024;
+ if ((lval % 1024) != 0) break;
+ } while (++idx < ((sizeof(suffixTable) /
+ sizeof(suffixTable[0])) - 1));
+ outCount = snprintf(outBuf, outBufLen,
+ "%" PRId64 "%cB",
+ lval, suffixTable[idx]);
+ } else {
+ outCount = snprintf(outBuf, outBufLen, "B");
+ }
+ break;
+ case TYPE_MILLISECONDS:
+ if (((lval <= -1000) || (1000 <= lval)) &&
+ (outBufLen || (outBuf[-1] == '0'))) {
+ /* repaint as (fractional) seconds, possibly saving space */
+ if (outBufLen) outBuf[0] = outBuf[-1];
+ outBuf[-1] = outBuf[-2];
+ outBuf[-2] = outBuf[-3];
+ outBuf[-3] = '.';
+ while ((outBufLen == 0) || (*outBuf == '0')) {
+ --outBuf;
+ ++outBufLen;
+ }
+ if (*outBuf != '.') {
+ ++outBuf;
+ --outBufLen;
+ }
+ outCount = snprintf(outBuf, outBufLen, "s");
+ } else {
+ outCount = snprintf(outBuf, outBufLen, "ms");
+ }
+ break;
+ case TYPE_ALLOCATIONS:
+ outCount = 0;
+ /* outCount = snprintf(outBuf, outBufLen, " allocations"); */
+ break;
+ case TYPE_ID:
+ outCount = 0;
+ break;
+ case TYPE_PERCENT:
+ outCount = snprintf(outBuf, outBufLen, "%%");
+ break;
+ default: /* ? */
+ outCount = 0;
+ break;
+ }
+ ++cp;
+ --len;
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else if (outCount) {
+ /* halt output */
+ goto no_room;
+ }
+ }
+ if (!findChar(&cp, &len, ')')) len = 0;
+ if (!findChar(&cp, &len, ',')) len = 0;
+ }
bail:
*pEventData = eventData;
*pEventDataLen = eventDataLen;
*pOutBuf = outBuf;
*pOutBufLen = outBufLen;
+ if (cp) {
+ *fmtStr = cp;
+ *fmtLen = len;
+ }
return result;
no_room:
@@ -655,37 +950,53 @@
* it however we choose, which means we can't really use a fixed-size buffer
* here.
*/
-int android_log_processBinaryLogBuffer(struct logger_entry *buf,
- AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
- int messageBufLen)
+LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
+ struct logger_entry *buf,
+ AndroidLogEntry *entry,
+ const EventTagMap *map,
+ char *messageBuf, int messageBufLen)
{
size_t inCount;
- unsigned int tagIndex;
+ uint32_t tagIndex;
const unsigned char* eventData;
entry->tv_sec = buf->sec;
entry->tv_nsec = buf->nsec;
entry->priority = ANDROID_LOG_INFO;
+ entry->uid = -1;
entry->pid = buf->pid;
entry->tid = buf->tid;
/*
- * Pull the tag out.
+ * Pull the tag out, fill in some additional details based on incoming
+ * buffer version (v3 adds lid, v4 adds uid).
*/
eventData = (const unsigned char*) buf->msg;
struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
if (buf2->hdr_size) {
+ if ((buf2->hdr_size < sizeof(((struct log_msg *)NULL)->entry_v1)) ||
+ (buf2->hdr_size > sizeof(((struct log_msg *)NULL)->entry))) {
+ fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+ return -1;
+ }
eventData = ((unsigned char *)buf2) + buf2->hdr_size;
+ if ((buf2->hdr_size >= sizeof(struct logger_entry_v3)) &&
+ (((struct logger_entry_v3 *)buf)->lid == LOG_ID_SECURITY)) {
+ entry->priority = ANDROID_LOG_WARN;
+ }
+ if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
+ entry->uid = ((struct logger_entry_v4 *)buf)->uid;
+ }
}
inCount = buf->len;
- if (inCount < 4)
- return -1;
+ if (inCount < 4) return -1;
tagIndex = get4LE(eventData);
eventData += 4;
inCount -= 4;
+ entry->tagLen = 0;
if (map != NULL) {
- entry->tag = android_lookupEventTag(map, tagIndex);
+ entry->tag = android_lookupEventTag_len(map, &entry->tagLen, tagIndex);
} else {
entry->tag = NULL;
}
@@ -696,36 +1007,61 @@
* shift the buffer pointers down.
*/
if (entry->tag == NULL) {
- int tagLen;
+ size_t tagLen;
- tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
+ tagLen = snprintf(messageBuf, messageBufLen, "[%" PRIu32 "]", tagIndex);
+ if (tagLen >= (size_t)messageBufLen) {
+ tagLen = messageBufLen - 1;
+ }
entry->tag = messageBuf;
- messageBuf += tagLen+1;
- messageBufLen -= tagLen+1;
+ entry->tagLen = tagLen;
+ messageBuf += tagLen + 1;
+ messageBufLen -= tagLen + 1;
}
/*
* Format the event log data into the buffer.
*/
+ const char* fmtStr = NULL;
+ size_t fmtLen = 0;
+ if (descriptive_output && map) {
+ fmtStr = android_lookupEventFormat_len(map, &fmtLen, tagIndex);
+ }
+
char* outBuf = messageBuf;
- size_t outRemaining = messageBufLen-1; /* leave one for nul byte */
- int result;
- result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
- &outRemaining);
+ size_t outRemaining = messageBufLen - 1; /* leave one for nul byte */
+ int result = 0;
+
+ if ((inCount > 0) || fmtLen) {
+ result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
+ &outRemaining, &fmtStr, &fmtLen);
+ }
+ if ((result == 1) && fmtStr) {
+ /* We overflowed :-(, let's repaint the line w/o format dressings */
+ eventData = (const unsigned char*)buf->msg;
+ if (buf2->hdr_size) {
+ eventData = ((unsigned char *)buf2) + buf2->hdr_size;
+ }
+ eventData += 4;
+ outBuf = messageBuf;
+ outRemaining = messageBufLen - 1;
+ result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
+ &outRemaining, NULL, NULL);
+ }
if (result < 0) {
fprintf(stderr, "Binary log entry conversion failed\n");
- return -1;
- } else if (result == 1) {
- if (outBuf > messageBuf) {
- /* leave an indicator */
- *(outBuf-1) = '!';
- } else {
- /* no room to output anything at all */
- *outBuf++ = '!';
- outRemaining--;
+ }
+ if (result) {
+ if (!outRemaining) {
+ /* make space to leave an indicator */
+ --outBuf;
+ ++outRemaining;
}
- /* pretend we ate all the data */
+ *outBuf++ = (result < 0) ? '!' : '^'; /* Error or Truncation? */
+ outRemaining--;
+ /* pretend we ate all the data to prevent log stutter */
inCount = 0;
+ if (result > 0) result = 0;
}
/* eat the silly terminating '\n' */
@@ -749,7 +1085,7 @@
entry->message = messageBuf;
- return 0;
+ return result;
}
/*
@@ -764,7 +1100,7 @@
* _also_ be part of libutils/Unicode.cpp if its usefullness needs to
* propagate globally.
*/
-WEAK ssize_t utf8_character_length(const char *src, size_t len)
+LIBLOG_WEAK ssize_t utf8_character_length(const char *src, size_t len)
{
const char *cur = src;
const char first_char = *cur++;
@@ -774,7 +1110,7 @@
uint32_t utf32;
if ((first_char & 0x80) == 0) { /* ASCII */
- return 1;
+ return first_char ? 1 : -1;
}
/*
@@ -840,7 +1176,7 @@
} else if (*message == '\b') {
strcpy(buf, "\\b");
} else if (*message == '\t') {
- strcpy(buf, "\\t");
+ strcpy(buf, "\t"); // Do not escape tabs
} else if (*message == '\v') {
strcpy(buf, "\\v");
} else if (*message == '\f') {
@@ -868,6 +1204,285 @@
return p - begin;
}
+static char *readSeconds(char *e, struct timespec *t)
+{
+ unsigned long multiplier;
+ char *p;
+ t->tv_sec = strtoul(e, &p, 10);
+ if (*p != '.') {
+ return NULL;
+ }
+ t->tv_nsec = 0;
+ multiplier = NS_PER_SEC;
+ while (isdigit(*++p) && (multiplier /= 10)) {
+ t->tv_nsec += (*p - '0') * multiplier;
+ }
+ return p;
+}
+
+static struct timespec *sumTimespec(struct timespec *left,
+ struct timespec *right)
+{
+ left->tv_nsec += right->tv_nsec;
+ left->tv_sec += right->tv_sec;
+ if (left->tv_nsec >= (long)NS_PER_SEC) {
+ left->tv_nsec -= NS_PER_SEC;
+ left->tv_sec += 1;
+ }
+ return left;
+}
+
+static struct timespec *subTimespec(struct timespec *result,
+ struct timespec *left,
+ struct timespec *right)
+{
+ result->tv_nsec = left->tv_nsec - right->tv_nsec;
+ result->tv_sec = left->tv_sec - right->tv_sec;
+ if (result->tv_nsec < 0) {
+ result->tv_nsec += NS_PER_SEC;
+ result->tv_sec -= 1;
+ }
+ return result;
+}
+
+static long long nsecTimespec(struct timespec *now)
+{
+ return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
+}
+
+static void convertMonotonic(struct timespec *result,
+ const AndroidLogEntry *entry)
+{
+ struct listnode *node;
+ struct conversionList {
+ struct listnode node; /* first */
+ struct timespec time;
+ struct timespec convert;
+ } *list, *next;
+ struct timespec time, convert;
+
+ /* If we do not have a conversion list, build one up */
+ if (list_empty(&convertHead)) {
+ bool suspended_pending = false;
+ struct timespec suspended_monotonic = { 0, 0 };
+ struct timespec suspended_diff = { 0, 0 };
+
+ /*
+ * Read dmesg for _some_ synchronization markers and insert
+ * Anything in the Android Logger before the dmesg logging span will
+ * be highly suspect regarding the monotonic time calculations.
+ */
+ FILE *p = popen("/system/bin/dmesg", "re");
+ if (p) {
+ char *line = NULL;
+ size_t len = 0;
+ while (getline(&line, &len, p) > 0) {
+ static const char suspend[] = "PM: suspend entry ";
+ static const char resume[] = "PM: suspend exit ";
+ static const char healthd[] = "healthd";
+ static const char battery[] = ": battery ";
+ static const char suspended[] = "Suspended for ";
+ struct timespec monotonic;
+ struct tm tm;
+ char *cp, *e = line;
+ bool add_entry = true;
+
+ if (*e == '<') {
+ while (*e && (*e != '>')) {
+ ++e;
+ }
+ if (*e != '>') {
+ continue;
+ }
+ }
+ if (*e != '[') {
+ continue;
+ }
+ while (*++e == ' ') {
+ ;
+ }
+ e = readSeconds(e, &monotonic);
+ if (!e || (*e != ']')) {
+ continue;
+ }
+
+ if ((e = strstr(e, suspend))) {
+ e += sizeof(suspend) - 1;
+ } else if ((e = strstr(line, resume))) {
+ e += sizeof(resume) - 1;
+ } else if (((e = strstr(line, healthd)))
+ && ((e = strstr(e + sizeof(healthd) - 1, battery)))) {
+ /* NB: healthd is roughly 150us late, worth the price to
+ * deal with ntp-induced or hardware clock drift. */
+ e += sizeof(battery) - 1;
+ } else if ((e = strstr(line, suspended))) {
+ e += sizeof(suspended) - 1;
+ e = readSeconds(e, &time);
+ if (!e) {
+ continue;
+ }
+ add_entry = false;
+ suspended_pending = true;
+ suspended_monotonic = monotonic;
+ suspended_diff = time;
+ } else {
+ continue;
+ }
+ if (add_entry) {
+ /* look for "????-??-?? ??:??:??.????????? UTC" */
+ cp = strstr(e, " UTC");
+ if (!cp || ((cp - e) < 29) || (cp[-10] != '.')) {
+ continue;
+ }
+ e = cp - 29;
+ cp = readSeconds(cp - 10, &time);
+ if (!cp) {
+ continue;
+ }
+ cp = strptime(e, "%Y-%m-%d %H:%M:%S.", &tm);
+ if (!cp) {
+ continue;
+ }
+ cp = getenv(tz);
+ if (cp) {
+ cp = strdup(cp);
+ }
+ setenv(tz, utc, 1);
+ time.tv_sec = mktime(&tm);
+ if (cp) {
+ setenv(tz, cp, 1);
+ free(cp);
+ } else {
+ unsetenv(tz);
+ }
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ subTimespec(&list->convert, &time, &monotonic);
+ list_add_tail(&convertHead, &list->node);
+ }
+ if (suspended_pending && !list_empty(&convertHead)) {
+ list = node_to_item(list_tail(&convertHead),
+ struct conversionList, node);
+ if (subTimespec(&time,
+ subTimespec(&time,
+ &list->time,
+ &list->convert),
+ &suspended_monotonic)->tv_sec > 0) {
+ /* resume, what is convert factor before? */
+ subTimespec(&convert, &list->convert, &suspended_diff);
+ } else {
+ /* suspend */
+ convert = list->convert;
+ }
+ time = suspended_monotonic;
+ sumTimespec(&time, &convert);
+ /* breakpoint just before sleep */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ list->convert = convert;
+ list_add_tail(&convertHead, &list->node);
+ /* breakpoint just after sleep */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ sumTimespec(&list->time, &suspended_diff);
+ list->convert = convert;
+ sumTimespec(&list->convert, &suspended_diff);
+ list_add_tail(&convertHead, &list->node);
+ suspended_pending = false;
+ }
+ }
+ pclose(p);
+ }
+ /* last entry is our current time conversion */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ clock_gettime(CLOCK_REALTIME, &list->time);
+ clock_gettime(CLOCK_MONOTONIC, &convert);
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ /* Correct for instant clock_gettime latency (syscall or ~30ns) */
+ subTimespec(&time, &convert, subTimespec(&time, &time, &convert));
+ /* Calculate conversion factor */
+ subTimespec(&list->convert, &list->time, &time);
+ list_add_tail(&convertHead, &list->node);
+ if (suspended_pending) {
+ /* manufacture a suspend @ point before */
+ subTimespec(&convert, &list->convert, &suspended_diff);
+ time = suspended_monotonic;
+ sumTimespec(&time, &convert);
+ /* breakpoint just after sleep */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ sumTimespec(&list->time, &suspended_diff);
+ list->convert = convert;
+ sumTimespec(&list->convert, &suspended_diff);
+ list_add_head(&convertHead, &list->node);
+ /* breakpoint just before sleep */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ list->convert = convert;
+ list_add_head(&convertHead, &list->node);
+ }
+ }
+
+ /* Find the breakpoint in the conversion list */
+ list = node_to_item(list_head(&convertHead), struct conversionList, node);
+ next = NULL;
+ list_for_each(node, &convertHead) {
+ next = node_to_item(node, struct conversionList, node);
+ if (entry->tv_sec < next->time.tv_sec) {
+ break;
+ } else if (entry->tv_sec == next->time.tv_sec) {
+ if (entry->tv_nsec < next->time.tv_nsec) {
+ break;
+ }
+ }
+ list = next;
+ }
+
+ /* blend time from one breakpoint to the next */
+ convert = list->convert;
+ if (next) {
+ unsigned long long total, run;
+
+ total = nsecTimespec(subTimespec(&time, &next->time, &list->time));
+ time.tv_sec = entry->tv_sec;
+ time.tv_nsec = entry->tv_nsec;
+ run = nsecTimespec(subTimespec(&time, &time, &list->time));
+ if (run < total) {
+ long long crun;
+
+ float f = nsecTimespec(subTimespec(&time, &next->convert, &convert));
+ f *= run;
+ f /= total;
+ crun = f;
+ convert.tv_sec += crun / (long long)NS_PER_SEC;
+ if (crun < 0) {
+ convert.tv_nsec -= (-crun) % NS_PER_SEC;
+ if (convert.tv_nsec < 0) {
+ convert.tv_nsec += NS_PER_SEC;
+ convert.tv_sec -= 1;
+ }
+ } else {
+ convert.tv_nsec += crun % NS_PER_SEC;
+ if (convert.tv_nsec >= (long)NS_PER_SEC) {
+ convert.tv_nsec -= NS_PER_SEC;
+ convert.tv_sec += 1;
+ }
+ }
+ }
+ }
+
+ /* Apply the correction factor */
+ result->tv_sec = entry->tv_sec;
+ result->tv_nsec = entry->tv_nsec;
+ subTimespec(result, result, &convert);
+}
+
/**
* Formats a log message into a buffer
*
@@ -876,22 +1491,25 @@
* Returns NULL on malloc error
*/
-char *android_log_formatLogLine (
- AndroidLogFormat *p_format,
- char *defaultBuffer,
- size_t defaultBufferSize,
- const AndroidLogEntry *entry,
- size_t *p_outLength)
+LIBLOG_ABI_PUBLIC char *android_log_formatLogLine (
+ AndroidLogFormat *p_format,
+ char *defaultBuffer,
+ size_t defaultBufferSize,
+ const AndroidLogEntry *entry,
+ size_t *p_outLength)
{
#if !defined(_WIN32)
struct tm tmBuf;
#endif
struct tm* ptm;
- char timeBuf[32]; /* good margin, 23+nul for msec, 26+nul for usec */
+ /* good margin, 23+nul for msec, 26+nul for usec, 29+nul to nsec */
+ char timeBuf[64];
char prefixBuf[128], suffixBuf[128];
char priChar;
int prefixSuffixIsHeaderFooter = 0;
- char * ret = NULL;
+ char *ret;
+ time_t now;
+ unsigned long nsec;
priChar = filterPriToChar(entry->priority);
size_t prefixLen = 0, suffixLen = 0;
@@ -905,21 +1523,52 @@
* For this reason it's very annoying to have regexp meta characters
* in the time stamp. Don't use forward slashes, parenthesis,
* brackets, asterisks, or other special chars here.
+ *
+ * The caller may have affected the timezone environment, this is
+ * expected to be sensitive to that.
*/
-#if !defined(_WIN32)
- ptm = localtime_r(&(entry->tv_sec), &tmBuf);
-#else
- ptm = localtime(&(entry->tv_sec));
-#endif
- /* strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); */
- strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
- len = strlen(timeBuf);
- if (p_format->usec_time_output) {
- snprintf(timeBuf + len, sizeof(timeBuf) - len,
- ".%06ld", entry->tv_nsec / 1000);
+ now = entry->tv_sec;
+ nsec = entry->tv_nsec;
+ if (p_format->monotonic_output) {
+ // prevent convertMonotonic from being called if logd is monotonic
+ if (android_log_clockid() != CLOCK_MONOTONIC) {
+ struct timespec time;
+ convertMonotonic(&time, entry);
+ now = time.tv_sec;
+ nsec = time.tv_nsec;
+ }
+ }
+ if (now < 0) {
+ nsec = NS_PER_SEC - nsec;
+ }
+ if (p_format->epoch_output || p_format->monotonic_output) {
+ ptm = NULL;
+ snprintf(timeBuf, sizeof(timeBuf),
+ p_format->monotonic_output ? "%6lld" : "%19lld",
+ (long long)now);
} else {
- snprintf(timeBuf + len, sizeof(timeBuf) - len,
- ".%03ld", entry->tv_nsec / 1000000);
+#if !defined(_WIN32)
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&now);
+#endif
+ strftime(timeBuf, sizeof(timeBuf),
+ &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3],
+ ptm);
+ }
+ len = strlen(timeBuf);
+ if (p_format->nsec_time_output) {
+ len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
+ ".%09ld", nsec);
+ } else if (p_format->usec_time_output) {
+ len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
+ ".%06ld", nsec / US_PER_NSEC);
+ } else {
+ len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
+ ".%03ld", nsec / MS_PER_NSEC);
+ }
+ if (p_format->zone_output && ptm) {
+ strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
}
/*
@@ -933,23 +1582,47 @@
suffixLen = MIN(suffixLen, sizeof(suffixBuf));
}
+ char uid[16];
+ uid[0] = '\0';
+ if (p_format->uid_output) {
+ if (entry->uid >= 0) {
+
+ /*
+ * This code is Android specific, bionic guarantees that
+ * calls to non-reentrant getpwuid() are thread safe.
+ */
+#ifndef __BIONIC__
+#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
+#endif
+ struct passwd* pwd = getpwuid(entry->uid);
+ if (pwd && (strlen(pwd->pw_name) <= 5)) {
+ snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
+ } else {
+ // Not worth parsing package list, names all longer than 5
+ snprintf(uid, sizeof(uid), "%5d:", entry->uid);
+ }
+ } else {
+ snprintf(uid, sizeof(uid), " ");
+ }
+ }
+
switch (p_format->format) {
case FORMAT_TAG:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c/%-8s: ", priChar, entry->tag);
+ "%c/%-8.*s: ", priChar, (int)entry->tagLen, entry->tag);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
case FORMAT_PROCESS:
len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen,
- " (%s)\n", entry->tag);
+ " (%.*s)\n", (int)entry->tagLen, entry->tag);
suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c(%5d) ", priChar, entry->pid);
+ "%c(%s%5d) ", priChar, uid, entry->pid);
break;
case FORMAT_THREAD:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
+ "%c(%s%5d:%5d) ", priChar, uid, entry->pid, entry->tid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
@@ -961,21 +1634,27 @@
break;
case FORMAT_TIME:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%s %c/%-8s(%5d): ", timeBuf, priChar, entry->tag, entry->pid);
+ "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar,
+ (int)entry->tagLen, entry->tag, uid, entry->pid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
case FORMAT_THREADTIME:
+ ret = strchr(uid, ':');
+ if (ret) {
+ *ret = ' ';
+ }
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%s %5d %5d %c %-8s: ", timeBuf,
- entry->pid, entry->tid, priChar, entry->tag);
+ "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid,
+ entry->tid, priChar, (int)entry->tagLen, entry->tag);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
case FORMAT_LONG:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "[ %s %5d:%5d %c/%-8s ]\n",
- timeBuf, entry->pid, entry->tid, priChar, entry->tag);
+ "[ %s %s%5d:%5d %c/%-8.*s ]\n",
+ timeBuf, uid, entry->pid, entry->tid, priChar,
+ (int)entry->tagLen, entry->tag);
strcpy(suffixBuf + suffixLen, "\n\n");
suffixLen += 2;
prefixSuffixIsHeaderFooter = 1;
@@ -983,7 +1662,8 @@
case FORMAT_BRIEF:
default:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
+ "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen, entry->tag,
+ uid, entry->pid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
@@ -995,8 +1675,16 @@
* possibly causing heap corruption. To avoid this we double check and
* set the length at the maximum (size minus null byte)
*/
- prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen);
- suffixLen = MIN(suffixLen, sizeof(suffixBuf));
+ prefixLen += len;
+ if (prefixLen >= sizeof(prefixBuf)) {
+ prefixLen = sizeof(prefixBuf) - 1;
+ prefixBuf[sizeof(prefixBuf) - 1] = '\0';
+ }
+ if (suffixLen >= sizeof(suffixBuf)) {
+ suffixLen = sizeof(suffixBuf) - 1;
+ suffixBuf[sizeof(suffixBuf) - 2] = '\n';
+ suffixBuf[sizeof(suffixBuf) - 1] = '\0';
+ }
/* the following code is tragically unreadable */
@@ -1062,7 +1750,7 @@
strcat(p, suffixBuf);
p += suffixLen;
} else {
- while(pm < (entry->message + entry->messageLen)) {
+ do {
const char *lineStart;
size_t lineLen;
lineStart = pm;
@@ -1084,7 +1772,7 @@
p += suffixLen;
if (*pm == '\n') pm++;
- }
+ } while (pm < (entry->message + entry->messageLen));
}
if (p_outLength != NULL) {
@@ -1100,10 +1788,10 @@
* Returns count bytes written
*/
-int android_log_printLogLine(
- AndroidLogFormat *p_format,
- int fd,
- const AndroidLogEntry *entry)
+LIBLOG_ABI_PUBLIC int android_log_printLogLine(
+ AndroidLogFormat *p_format,
+ int fd,
+ const AndroidLogEntry *entry)
{
int ret;
char defaultBuffer[512];
@@ -1113,8 +1801,7 @@
outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
sizeof(defaultBuffer), entry, &totalLen);
- if (!outBuffer)
- return -1;
+ if (!outBuffer) return -1;
do {
ret = write(fd, outBuffer, totalLen);
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c
new file mode 100644
index 0000000..e1b81aa
--- /dev/null
+++ b/liblog/pmsg_reader.c
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_read.h"
+#include "logger.h"
+
+static int pmsgAvailable(log_id_t logId);
+static int pmsgVersion(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+static int pmsgRead(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ struct log_msg *log_msg);
+static void pmsgClose(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp);
+static int pmsgClear(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+
+LIBLOG_HIDDEN struct android_log_transport_read pmsgLoggerRead = {
+ .node = { &pmsgLoggerRead.node, &pmsgLoggerRead.node },
+ .name = "pmsg",
+ .available = pmsgAvailable,
+ .version = pmsgVersion,
+ .read = pmsgRead,
+ .poll = NULL,
+ .close = pmsgClose,
+ .clear = pmsgClear,
+ .setSize = NULL,
+ .getSize = NULL,
+ .getReadableSize = NULL,
+ .getPrune = NULL,
+ .setPrune = NULL,
+ .getStats = NULL,
+};
+
+static int pmsgAvailable(log_id_t logId)
+{
+ if (logId > LOG_ID_SECURITY) {
+ return -EINVAL;
+ }
+ if (access("/dev/pmsg0", W_OK) == 0) {
+ return 0;
+ }
+ return -EBADF;
+}
+
+/* Determine the credentials of the caller */
+static bool uid_has_log_permission(uid_t uid)
+{
+ return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) || (uid == AID_LOGD);
+}
+
+static uid_t get_best_effective_uid()
+{
+ uid_t euid;
+ uid_t uid;
+ gid_t gid;
+ ssize_t i;
+ static uid_t last_uid = (uid_t) -1;
+
+ if (last_uid != (uid_t) -1) {
+ return last_uid;
+ }
+ uid = __android_log_uid();
+ if (uid_has_log_permission(uid)) {
+ return last_uid = uid;
+ }
+ euid = geteuid();
+ if (uid_has_log_permission(euid)) {
+ return last_uid = euid;
+ }
+ gid = getgid();
+ if (uid_has_log_permission(gid)) {
+ return last_uid = gid;
+ }
+ gid = getegid();
+ if (uid_has_log_permission(gid)) {
+ return last_uid = gid;
+ }
+ i = getgroups((size_t) 0, NULL);
+ if (i > 0) {
+ gid_t list[i];
+
+ getgroups(i, list);
+ while (--i >= 0) {
+ if (uid_has_log_permission(list[i])) {
+ return last_uid = list[i];
+ }
+ }
+ }
+ return last_uid = uid;
+}
+
+static int pmsgClear(struct android_log_logger *logger __unused,
+ struct android_log_transport_context *transp __unused)
+{
+ if (uid_has_log_permission(get_best_effective_uid())) {
+ return unlink("/sys/fs/pstore/pmsg-ramoops-0");
+ }
+ errno = EPERM;
+ return -1;
+}
+
+/*
+ * returns the logger version
+ */
+static int pmsgVersion(struct android_log_logger *logger __unused,
+ struct android_log_transport_context *transp __unused)
+{
+ return 4;
+}
+
+static int pmsgRead(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ struct log_msg *log_msg)
+{
+ ssize_t ret;
+ off_t current, next;
+ uid_t uid;
+ struct android_log_logger *logger;
+ struct __attribute__((__packed__)) {
+ android_pmsg_log_header_t p;
+ android_log_header_t l;
+ uint8_t prio;
+ } buf;
+ static uint8_t preread_count;
+ bool is_system;
+
+ memset(log_msg, 0, sizeof(*log_msg));
+
+ if (atomic_load(&transp->context.fd) <= 0) {
+ int i, fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
+
+ if (fd < 0) {
+ return -errno;
+ }
+ if (fd == 0) { /* Argggg */
+ fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
+ close(0);
+ if (fd < 0) {
+ return -errno;
+ }
+ }
+ i = atomic_exchange(&transp->context.fd, fd);
+ if ((i > 0) && (i != fd)) {
+ close(i);
+ }
+ preread_count = 0;
+ }
+
+ while(1) {
+ int fd;
+
+ if (preread_count < sizeof(buf)) {
+ fd = atomic_load(&transp->context.fd);
+ if (fd <= 0) {
+ return -EBADF;
+ }
+ ret = TEMP_FAILURE_RETRY(read(fd,
+ &buf.p.magic + preread_count,
+ sizeof(buf) - preread_count));
+ if (ret < 0) {
+ return -errno;
+ }
+ preread_count += ret;
+ }
+ if (preread_count != sizeof(buf)) {
+ return preread_count ? -EIO : -EAGAIN;
+ }
+ if ((buf.p.magic != LOGGER_MAGIC) ||
+ (buf.p.len <= sizeof(buf)) ||
+ (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) ||
+ (buf.l.id >= LOG_ID_MAX) ||
+ (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
+ ((buf.l.id != LOG_ID_EVENTS) &&
+ (buf.l.id != LOG_ID_SECURITY) &&
+ ((buf.prio == ANDROID_LOG_UNKNOWN) ||
+ (buf.prio == ANDROID_LOG_DEFAULT) ||
+ (buf.prio >= ANDROID_LOG_SILENT)))) {
+ do {
+ memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
+ } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
+ continue;
+ }
+ preread_count = 0;
+
+ if ((transp->logMask & (1 << buf.l.id)) &&
+ ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
+ ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
+ ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
+ (logger_list->start.tv_nsec <=
+ buf.l.realtime.tv_nsec)))) &&
+ (!logger_list->pid || (logger_list->pid == buf.p.pid))) {
+ uid = get_best_effective_uid();
+ is_system = uid_has_log_permission(uid);
+ if (is_system || (uid == buf.p.uid)) {
+ char *msg = is_system ?
+ log_msg->entry_v4.msg :
+ log_msg->entry_v3.msg;
+ *msg = buf.prio;
+ fd = atomic_load(&transp->context.fd);
+ if (fd <= 0) {
+ return -EBADF;
+ }
+ ret = TEMP_FAILURE_RETRY(read(fd,
+ msg + sizeof(buf.prio),
+ buf.p.len - sizeof(buf)));
+ if (ret < 0) {
+ return -errno;
+ }
+ if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
+ return -EIO;
+ }
+
+ log_msg->entry_v4.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
+ log_msg->entry_v4.hdr_size = is_system ?
+ sizeof(log_msg->entry_v4) :
+ sizeof(log_msg->entry_v3);
+ log_msg->entry_v4.pid = buf.p.pid;
+ log_msg->entry_v4.tid = buf.l.tid;
+ log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
+ log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
+ log_msg->entry_v4.lid = buf.l.id;
+ if (is_system) {
+ log_msg->entry_v4.uid = buf.p.uid;
+ }
+
+ return ret + sizeof(buf.prio) + log_msg->entry_v4.hdr_size;
+ }
+ }
+
+ fd = atomic_load(&transp->context.fd);
+ if (fd <= 0) {
+ return -EBADF;
+ }
+ current = TEMP_FAILURE_RETRY(lseek(fd, (off_t)0, SEEK_CUR));
+ if (current < 0) {
+ return -errno;
+ }
+ fd = atomic_load(&transp->context.fd);
+ if (fd <= 0) {
+ return -EBADF;
+ }
+ next = TEMP_FAILURE_RETRY(lseek(fd,
+ (off_t)(buf.p.len - sizeof(buf)),
+ SEEK_CUR));
+ if (next < 0) {
+ return -errno;
+ }
+ if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
+ return -EIO;
+ }
+ }
+}
+
+static void pmsgClose(struct android_log_logger_list *logger_list __unused,
+ struct android_log_transport_context *transp) {
+ int fd = atomic_exchange(&transp->context.fd, 0);
+ if (fd > 0) {
+ close (fd);
+ }
+}
+
+LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_read(
+ log_id_t logId,
+ char prio,
+ const char *prefix,
+ __android_log_pmsg_file_read_fn fn, void *arg) {
+ ssize_t ret;
+ struct android_log_logger_list logger_list;
+ struct android_log_transport_context transp;
+ struct content {
+ struct listnode node;
+ union {
+ struct logger_entry_v4 entry;
+ struct logger_entry_v4 entry_v4;
+ struct logger_entry_v3 entry_v3;
+ struct logger_entry_v2 entry_v2;
+ struct logger_entry entry_v1;
+ };
+ } *content;
+ struct names {
+ struct listnode node;
+ struct listnode content;
+ log_id_t id;
+ char prio;
+ char name[];
+ } *names;
+ struct listnode name_list;
+ struct listnode *node, *n;
+ size_t len, prefix_len;
+
+ if (!fn) {
+ return -EINVAL;
+ }
+
+ /* Add just enough clues in logger_list and transp to make API function */
+ memset(&logger_list, 0, sizeof(logger_list));
+ memset(&transp, 0, sizeof(transp));
+
+ logger_list.mode = ANDROID_LOG_PSTORE |
+ ANDROID_LOG_NONBLOCK |
+ ANDROID_LOG_RDONLY;
+ transp.logMask = (unsigned)-1;
+ if (logId != LOG_ID_ANY) {
+ transp.logMask = (1 << logId);
+ }
+ transp.logMask &= ~((1 << LOG_ID_KERNEL) |
+ (1 << LOG_ID_EVENTS) |
+ (1 << LOG_ID_SECURITY));
+ if (!transp.logMask) {
+ return -EINVAL;
+ }
+
+ /* Initialize name list */
+ list_init(&name_list);
+
+ ret = SSIZE_MAX;
+
+ /* Validate incoming prefix, shift until it contains only 0 or 1 : or / */
+ prefix_len = 0;
+ if (prefix) {
+ const char *prev = NULL, *last = NULL, *cp = prefix;
+ while ((cp = strpbrk(cp, "/:"))) {
+ prev = last;
+ last = cp;
+ cp = cp + 1;
+ }
+ if (prev) {
+ prefix = prev + 1;
+ }
+ prefix_len = strlen(prefix);
+ }
+
+ /* Read the file content */
+ while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) {
+ char *cp;
+ size_t hdr_size = transp.logMsg.entry.hdr_size ?
+ transp.logMsg.entry.hdr_size : sizeof(transp.logMsg.entry_v1);
+ char *msg = (char *)&transp.logMsg + hdr_size;
+ char *split = NULL;
+
+ if ((hdr_size < sizeof(transp.logMsg.entry_v1)) ||
+ (hdr_size > sizeof(transp.logMsg.entry))) {
+ continue;
+ }
+ /* Check for invalid sequence number */
+ if ((transp.logMsg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE) ||
+ ((transp.logMsg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
+ ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE)) {
+ continue;
+ }
+
+ /* Determine if it has <dirbase>:<filebase> format for tag */
+ len = transp.logMsg.entry.len - sizeof(prio);
+ for (cp = msg + sizeof(prio);
+ *cp && isprint(*cp) && !isspace(*cp) && --len;
+ ++cp) {
+ if (*cp == ':') {
+ if (split) {
+ break;
+ }
+ split = cp;
+ }
+ }
+ if (*cp || !split) {
+ continue;
+ }
+
+ /* Filters */
+ if (prefix_len && strncmp(msg + sizeof(prio), prefix, prefix_len)) {
+ size_t offset;
+ /*
+ * Allow : to be a synonym for /
+ * Things we do dealing with const char * and do not alloc
+ */
+ split = strchr(prefix, ':');
+ if (split) {
+ continue;
+ }
+ split = strchr(prefix, '/');
+ if (!split) {
+ continue;
+ }
+ offset = split - prefix;
+ if ((msg[offset + sizeof(prio)] != ':') ||
+ strncmp(msg + sizeof(prio), prefix, offset)) {
+ continue;
+ }
+ ++offset;
+ if ((prefix_len > offset) &&
+ strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) {
+ continue;
+ }
+ }
+
+ if ((prio != ANDROID_LOG_ANY) && (*msg < prio)) {
+ continue;
+ }
+
+ /* check if there is an existing entry */
+ list_for_each(node, &name_list) {
+ names = node_to_item(node, struct names, node);
+ if (!strcmp(names->name, msg + sizeof(prio)) &&
+ (names->id == transp.logMsg.entry.lid) &&
+ (names->prio == *msg)) {
+ break;
+ }
+ }
+
+ /* We do not have an existing entry, create and add one */
+ if (node == &name_list) {
+ static const char numbers[] = "0123456789";
+ unsigned long long nl;
+
+ len = strlen(msg + sizeof(prio)) + 1;
+ names = calloc(1, sizeof(*names) + len);
+ if (!names) {
+ ret = -ENOMEM;
+ break;
+ }
+ strcpy(names->name, msg + sizeof(prio));
+ names->id = transp.logMsg.entry.lid;
+ names->prio = *msg;
+ list_init(&names->content);
+ /*
+ * Insert in reverse numeric _then_ alpha sorted order as
+ * representative of log rotation:
+ *
+ * log.10
+ * klog.10
+ * . . .
+ * log.2
+ * klog.2
+ * log.1
+ * klog.1
+ * log
+ * klog
+ *
+ * thus when we present the content, we are provided the oldest
+ * first, which when 'refreshed' could spill off the end of the
+ * pmsg FIFO but retaining the newest data for last with best
+ * chances to survive.
+ */
+ nl = 0;
+ cp = strpbrk(names->name, numbers);
+ if (cp) {
+ nl = strtoull(cp, NULL, 10);
+ }
+ list_for_each_reverse(node, &name_list) {
+ struct names *a_name = node_to_item(node, struct names, node);
+ const char *r = a_name->name;
+ int compare = 0;
+
+ unsigned long long nr = 0;
+ cp = strpbrk(r, numbers);
+ if (cp) {
+ nr = strtoull(cp, NULL, 10);
+ }
+ if (nr != nl) {
+ compare = (nl > nr) ? 1 : -1;
+ }
+ if (compare == 0) {
+ compare = strcmp(names->name, r);
+ }
+ if (compare <= 0) {
+ break;
+ }
+ }
+ list_add_head(node, &names->node);
+ }
+
+ /* Remove any file fragments that match our sequence number */
+ list_for_each_safe(node, n, &names->content) {
+ content = node_to_item(node, struct content, node);
+ if (transp.logMsg.entry.nsec == content->entry.nsec) {
+ list_remove(&content->node);
+ free(content);
+ }
+ }
+
+ /* Add content */
+ content = calloc(1, sizeof(content->node) +
+ hdr_size + transp.logMsg.entry.len);
+ if (!content) {
+ ret = -ENOMEM;
+ break;
+ }
+ memcpy(&content->entry, &transp.logMsg.entry,
+ hdr_size + transp.logMsg.entry.len);
+
+ /* Insert in sequence number sorted order, to ease reconstruction */
+ list_for_each_reverse(node, &names->content) {
+ if ((node_to_item(node, struct content, node))->entry.nsec <
+ transp.logMsg.entry.nsec) {
+ break;
+ }
+ }
+ list_add_head(node, &content->node);
+ }
+ pmsgClose(&logger_list, &transp);
+
+ /* Progress through all the collected files */
+ list_for_each_safe(node, n, &name_list) {
+ struct listnode *content_node, *m;
+ char *buf;
+ size_t sequence, tag_len;
+
+ names = node_to_item(node, struct names, node);
+
+ /* Construct content into a linear buffer */
+ buf = NULL;
+ len = 0;
+ sequence = 0;
+ tag_len = strlen(names->name) + sizeof(char); /* tag + nul */
+ list_for_each_safe(content_node, m, &names->content) {
+ ssize_t add_len;
+
+ content = node_to_item(content_node, struct content, node);
+ add_len = content->entry.len - tag_len - sizeof(prio);
+ if (add_len <= 0) {
+ list_remove(content_node);
+ free(content);
+ continue;
+ }
+
+ if (!buf) {
+ buf = malloc(sizeof(char));
+ if (!buf) {
+ ret = -ENOMEM;
+ list_remove(content_node);
+ free(content);
+ continue;
+ }
+ *buf = '\0';
+ }
+
+ /* Missing sequence numbers */
+ while (sequence < content->entry.nsec) {
+ /* plus space for enforced nul */
+ buf = realloc(buf, len + sizeof(char) + sizeof(char));
+ if (!buf) {
+ break;
+ }
+ buf[len] = '\f'; /* Mark missing content with a form feed */
+ buf[++len] = '\0';
+ sequence += ANDROID_LOG_PMSG_FILE_SEQUENCE;
+ }
+ if (!buf) {
+ ret = -ENOMEM;
+ list_remove(content_node);
+ free(content);
+ continue;
+ }
+ /* plus space for enforced nul */
+ buf = realloc(buf, len + add_len + sizeof(char));
+ if (!buf) {
+ ret = -ENOMEM;
+ list_remove(content_node);
+ free(content);
+ continue;
+ }
+ memcpy(buf + len,
+ (char *)&content->entry + content->entry.hdr_size +
+ tag_len + sizeof(prio),
+ add_len);
+ len += add_len;
+ buf[len] = '\0'; /* enforce trailing hidden nul */
+ sequence = content->entry.nsec + ANDROID_LOG_PMSG_FILE_SEQUENCE;
+
+ list_remove(content_node);
+ free(content);
+ }
+ if (buf) {
+ if (len) {
+ /* Buffer contains enforced trailing nul just beyond length */
+ ssize_t r;
+ *strchr(names->name, ':') = '/'; /* Convert back to filename */
+ r = (*fn)(names->id, names->prio, names->name, buf, len, arg);
+ if ((ret >= 0) && (r > 0)) {
+ if (ret == SSIZE_MAX) {
+ ret = r;
+ } else {
+ ret += r;
+ }
+ } else if (r < ret) {
+ ret = r;
+ }
+ }
+ free(buf);
+ }
+ list_remove(node);
+ free(names);
+ }
+ return (ret == SSIZE_MAX) ? -ENOENT : ret;
+}
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
new file mode 100644
index 0000000..c1c068e
--- /dev/null
+++ b/liblog/pmsg_writer.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * pmsg write handler
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+
+static int pmsgOpen();
+static void pmsgClose();
+static int pmsgAvailable(log_id_t logId);
+static int pmsgWrite(log_id_t logId, struct timespec *ts,
+ struct iovec *vec, size_t nr);
+
+LIBLOG_HIDDEN struct android_log_transport_write pmsgLoggerWrite = {
+ .node = { &pmsgLoggerWrite.node, &pmsgLoggerWrite.node },
+ .context.fd = -1,
+ .name = "pmsg",
+ .available = pmsgAvailable,
+ .open = pmsgOpen,
+ .close = pmsgClose,
+ .write = pmsgWrite,
+};
+
+static int pmsgOpen()
+{
+ int fd = atomic_load(&pmsgLoggerWrite.context.fd);
+ if (fd < 0) {
+ int i;
+
+ fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ i = atomic_exchange(&pmsgLoggerWrite.context.fd, fd);
+ if ((i >= 0) && (i != fd)) {
+ close(i);
+ }
+ }
+
+ return fd;
+}
+
+static void pmsgClose()
+{
+ int fd = atomic_exchange(&pmsgLoggerWrite.context.fd, -1);
+ if (fd >= 0) {
+ close(fd);
+ }
+}
+
+static int pmsgAvailable(log_id_t logId)
+{
+ if (logId > LOG_ID_SECURITY) {
+ return -EINVAL;
+ }
+ if ((logId != LOG_ID_SECURITY) &&
+ (logId != LOG_ID_EVENTS) &&
+ !__android_log_is_debuggable()) {
+ return -EINVAL;
+ }
+ if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+ if (access("/dev/pmsg0", W_OK) == 0) {
+ return 0;
+ }
+ return -EBADF;
+ }
+ return 1;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static int pmsgWrite(log_id_t logId, struct timespec *ts,
+ struct iovec *vec, size_t nr)
+{
+ static const unsigned headerLength = 2;
+ struct iovec newVec[nr + headerLength];
+ android_log_header_t header;
+ android_pmsg_log_header_t pmsgHeader;
+ size_t i, payloadSize;
+ ssize_t ret;
+
+ if ((logId == LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
+ if (vec[0].iov_len < 4) {
+ return -EINVAL;
+ }
+
+ if (SNET_EVENT_LOG_TAG != get4LE(vec[0].iov_base)) {
+ return -EPERM;
+ }
+ }
+
+ if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+ return -EBADF;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsgHeader;
+ * // what we provide to file
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ pmsgHeader.magic = LOGGER_MAGIC;
+ pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
+ pmsgHeader.uid = __android_log_uid();
+ pmsgHeader.pid = getpid();
+
+ header.id = logId;
+ header.tid = gettid();
+ header.realtime.tv_sec = ts->tv_sec;
+ header.realtime.tv_nsec = ts->tv_nsec;
+
+ newVec[0].iov_base = (unsigned char *)&pmsgHeader;
+ newVec[0].iov_len = sizeof(pmsgHeader);
+ newVec[1].iov_base = (unsigned char *)&header;
+ newVec[1].iov_len = sizeof(header);
+
+ 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;
+ }
+ payloadSize = LOGGER_ENTRY_MAX_PAYLOAD;
+ break;
+ }
+ }
+ pmsgHeader.len += payloadSize;
+
+ ret = TEMP_FAILURE_RETRY(writev(atomic_load(&pmsgLoggerWrite.context.fd),
+ newVec, i));
+ if (ret < 0) {
+ ret = errno ? -errno : -ENOTCONN;
+ }
+
+ if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) {
+ ret -= sizeof(header) - sizeof(pmsgHeader);
+ }
+
+ return ret;
+}
+
+/*
+ * Virtual pmsg filesystem
+ *
+ * Payload will comprise the string "<basedir>:<basefile>\0<content>" to a
+ * maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
+ * file.
+ *
+ * Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
+ */
+
+static inline const char *strnrchr(const char *buf, size_t len, char c) {
+ const char *cp = buf + len;
+ while ((--cp > buf) && (*cp != c));
+ if (cp <= buf) {
+ return buf + len;
+ }
+ return cp;
+}
+
+/* Write a buffer as filename references (tag = <basedir>:<basename>) */
+LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_write(
+ log_id_t logId,
+ char prio,
+ const char *filename,
+ const char *buf, size_t len) {
+ bool weOpened;
+ size_t length, packet_len;
+ const char *tag;
+ char *cp, *slash;
+ struct timespec ts;
+ struct iovec vec[3];
+
+ /* Make sure the logId value is not a bad idea */
+ if ((logId == LOG_ID_KERNEL) || /* Verbotten */
+ (logId == LOG_ID_EVENTS) || /* Do not support binary content */
+ (logId == LOG_ID_SECURITY) || /* Bad idea to allow */
+ ((unsigned)logId >= 32)) { /* fit within logMask on arch32 */
+ return -EINVAL;
+ }
+
+ clock_gettime(android_log_clockid(), &ts);
+
+ cp = strdup(filename);
+ if (!cp) {
+ return -ENOMEM;
+ }
+
+ tag = cp;
+ slash = strrchr(cp, '/');
+ if (slash) {
+ *slash = ':';
+ slash = strrchr(cp, '/');
+ if (slash) {
+ tag = slash + 1;
+ }
+ }
+
+ length = strlen(tag) + 1;
+ packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length;
+
+ vec[0].iov_base = &prio;
+ vec[0].iov_len = sizeof(char);
+ vec[1].iov_base = (unsigned char *)tag;
+ vec[1].iov_len = length;
+
+ weOpened = false;
+ for (ts.tv_nsec = 0, length = len;
+ length;
+ ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
+ ssize_t ret;
+ size_t transfer;
+
+ if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
+ ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+ len -= length;
+ break;
+ }
+
+ transfer = length;
+ if (transfer > packet_len) {
+ transfer = strnrchr(buf, packet_len - 1, '\n') - buf;
+ if ((transfer < length) && (buf[transfer] == '\n')) {
+ ++transfer;
+ }
+ }
+
+ vec[2].iov_base = (unsigned char *)buf;
+ vec[2].iov_len = transfer;
+
+ if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+ if (!weOpened) { /* Impossible for weOpened = true here */
+ __android_log_lock();
+ }
+ weOpened = atomic_load(&pmsgLoggerWrite.context.fd) < 0;
+ if (!weOpened) {
+ __android_log_unlock();
+ } else if (pmsgOpen() < 0) {
+ __android_log_unlock();
+ return -EBADF;
+ }
+ }
+
+ ret = pmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
+
+ if (ret <= 0) {
+ if (weOpened) {
+ pmsgClose();
+ __android_log_unlock();
+ }
+ free(cp);
+ return ret ? ret : (len - length);
+ }
+ length -= transfer;
+ buf += transfer;
+ }
+ if (weOpened) {
+ pmsgClose();
+ __android_log_unlock();
+ }
+ free(cp);
+ return len;
+}
diff --git a/liblog/properties.c b/liblog/properties.c
new file mode 100644
index 0000000..dda09e0
--- /dev/null
+++ b/liblog/properties.c
@@ -0,0 +1,664 @@
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <ctype.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+#include <unistd.h>
+
+#include <private/android_logger.h>
+
+#include "log_portability.h"
+
+static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
+
+static int 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.
+ */
+ /*
+ * Any contention, and we can turn around and use the non-cached method
+ * in less time than the system call associated with a mutex to deal with
+ * the contention.
+ */
+ return pthread_mutex_trylock(&lock_loggable);
+}
+
+static void unlock()
+{
+ pthread_mutex_unlock(&lock_loggable);
+}
+
+struct cache {
+ const prop_info* pinfo;
+ uint32_t serial;
+};
+
+struct cache_char {
+ struct cache cache;
+ unsigned char c;
+};
+
+static int check_cache(struct cache* cache)
+{
+ return cache->pinfo
+ && __system_property_serial(cache->pinfo) != cache->serial;
+}
+
+#define BOOLEAN_TRUE 0xFF
+#define BOOLEAN_FALSE 0xFE
+
+static void refresh_cache(struct cache_char* cache, const char* key)
+{
+ char buf[PROP_VALUE_MAX];
+
+ if (!cache->cache.pinfo) {
+ cache->cache.pinfo = __system_property_find(key);
+ if (!cache->cache.pinfo) {
+ return;
+ }
+ }
+ cache->cache.serial = __system_property_serial(cache->cache.pinfo);
+ __system_property_read(cache->cache.pinfo, 0, buf);
+ switch(buf[0]) {
+ case 't': case 'T':
+ cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE;
+ break;
+ case 'f': case 'F':
+ cache->c = strcasecmp(buf + 1, "alse") ? buf[0] : BOOLEAN_FALSE;
+ break;
+ default:
+ cache->c = buf[0];
+ }
+}
+
+static int __android_log_level(const char* tag, size_t len, int default_prio)
+{
+ /* sizeof() is used on this array below */
+ static const char log_namespace[] = "persist.log.tag.";
+ static const size_t base_offset = 8; /* skip "persist." */
+ /* calculate the size of our key temporary buffer */
+ const size_t taglen = tag ? len : 0;
+ /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
+ char key[sizeof(log_namespace) + taglen]; /* may be > PROP_NAME_MAX */
+ char* kp;
+ size_t i;
+ char c = 0;
+ /*
+ * Single layer cache of four properties. Priorities are:
+ * log.tag.<tag>
+ * persist.log.tag.<tag>
+ * log.tag
+ * persist.log.tag
+ * Where the missing tag matches all tags and becomes the
+ * system global default. We do not support ro.log.tag* .
+ */
+ static char last_tag[PROP_NAME_MAX];
+ static uint32_t global_serial;
+ /* some compilers erroneously see uninitialized use. !not_locked */
+ uint32_t current_global_serial = 0;
+ static struct cache_char tag_cache[2];
+ static struct cache_char global_cache[2];
+ int change_detected;
+ int global_change_detected;
+ int not_locked;
+
+ strcpy(key, log_namespace);
+
+ global_change_detected = change_detected = not_locked = lock();
+
+ if (!not_locked) {
+ /*
+ * check all known serial numbers to changes.
+ */
+ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ if (check_cache(&tag_cache[i].cache)) {
+ change_detected = 1;
+ }
+ }
+ for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+ if (check_cache(&global_cache[i].cache)) {
+ global_change_detected = 1;
+ }
+ }
+
+ current_global_serial = __system_property_area_serial();
+ if (current_global_serial != global_serial) {
+ change_detected = 1;
+ global_change_detected = 1;
+ }
+ }
+
+ if (taglen) {
+ int local_change_detected = change_detected;
+ if (!not_locked) {
+ if (!last_tag[0]
+ || (last_tag[0] != tag[0])
+ || strncmp(last_tag + 1, tag + 1,
+ (len < sizeof(last_tag)) ?
+ (len - 1) :
+ (sizeof(last_tag) - 1))
+ || ((len < sizeof(last_tag)) && last_tag[len])) {
+ /* invalidate log.tag.<tag> cache */
+ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ tag_cache[i].cache.pinfo = NULL;
+ tag_cache[i].c = '\0';
+ }
+ last_tag[0] = '\0';
+ local_change_detected = 1;
+ }
+ if (!last_tag[0]) {
+ if (len < sizeof(last_tag)) {
+ strncpy(last_tag, tag, len);
+ last_tag[len] = '\0';
+ } else {
+ strncpy(last_tag, tag, sizeof(last_tag));
+ }
+ }
+ }
+ strncpy(key + sizeof(log_namespace) - 1, tag, len);
+ key[sizeof(log_namespace) - 1 + len] = '\0';
+
+ kp = key;
+ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ struct cache_char* cache = &tag_cache[i];
+ struct cache_char temp_cache;
+
+ if (not_locked) {
+ temp_cache.cache.pinfo = NULL;
+ temp_cache.c = '\0';
+ cache = &temp_cache;
+ }
+ if (local_change_detected) {
+ refresh_cache(cache, kp);
+ }
+
+ if (cache->c) {
+ c = cache->c;
+ break;
+ }
+
+ kp = key + base_offset;
+ }
+ }
+
+ switch (toupper(c)) { /* if invalid, resort to global */
+ case 'V':
+ case 'D':
+ case 'I':
+ case 'W':
+ case 'E':
+ case 'F': /* Not officially supported */
+ case 'A':
+ case 'S':
+ case BOOLEAN_FALSE: /* Not officially supported */
+ break;
+ default:
+ /* clear '.' after log.tag */
+ key[sizeof(log_namespace) - 2] = '\0';
+
+ kp = key;
+ for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+ struct cache_char* cache = &global_cache[i];
+ struct cache_char temp_cache;
+
+ if (not_locked) {
+ temp_cache = *cache;
+ if (temp_cache.cache.pinfo != cache->cache.pinfo) { /* check atomic */
+ temp_cache.cache.pinfo = NULL;
+ temp_cache.c = '\0';
+ }
+ cache = &temp_cache;
+ }
+ if (global_change_detected) {
+ refresh_cache(cache, kp);
+ }
+
+ if (cache->c) {
+ c = cache->c;
+ break;
+ }
+
+ kp = key + base_offset;
+ }
+ break;
+ }
+
+ if (!not_locked) {
+ global_serial = current_global_serial;
+ unlock();
+ }
+
+ switch (toupper(c)) {
+ case 'V': return ANDROID_LOG_VERBOSE;
+ case 'D': return ANDROID_LOG_DEBUG;
+ case 'I': return ANDROID_LOG_INFO;
+ case 'W': return ANDROID_LOG_WARN;
+ case 'E': return ANDROID_LOG_ERROR;
+ case 'F': /* FALLTHRU */ /* Not officially supported */
+ case 'A': return ANDROID_LOG_FATAL;
+ case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
+ case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
+ }
+ return default_prio;
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio,
+ const char* tag, size_t len,
+ int default_prio)
+{
+ int logLevel = __android_log_level(tag, len, default_prio);
+ return logLevel >= 0 && prio >= logLevel;
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
+ const char* tag,
+ int default_prio)
+{
+ int logLevel = __android_log_level(tag,
+ (tag && *tag) ? strlen(tag) : 0,
+ default_prio);
+ return logLevel >= 0 && prio >= logLevel;
+}
+
+LIBLOG_ABI_PRIVATE int __android_log_is_debuggable()
+{
+ static uint32_t serial;
+ static struct cache_char tag_cache;
+ static const char key[] = "ro.debuggable";
+ int ret;
+
+ if (tag_cache.c) { /* ro property does not change after set */
+ ret = tag_cache.c == '1';
+ } else if (lock()) {
+ struct cache_char temp_cache = { { NULL, -1 }, '\0' };
+ refresh_cache(&temp_cache, key);
+ ret = temp_cache.c == '1';
+ } else {
+ int change_detected = check_cache(&tag_cache.cache);
+ uint32_t current_serial = __system_property_area_serial();
+ if (current_serial != serial) {
+ change_detected = 1;
+ }
+ if (change_detected) {
+ refresh_cache(&tag_cache, key);
+ serial = current_serial;
+ }
+ ret = tag_cache.c == '1';
+
+ unlock();
+ }
+
+ return ret;
+}
+
+/*
+ * For properties that are read often, but generally remain constant.
+ * Since a change is rare, we will accept a trylock failure gracefully.
+ * Use a separate lock from is_loggable to keep contention down b/25563384.
+ */
+struct cache2_char {
+ pthread_mutex_t lock;
+ uint32_t serial;
+ const char* key_persist;
+ struct cache_char cache_persist;
+ const char* key_ro;
+ struct cache_char cache_ro;
+ unsigned char (*const evaluate)(const struct cache2_char *self);
+};
+
+static inline unsigned char do_cache2_char(struct cache2_char *self)
+{
+ uint32_t current_serial;
+ int change_detected;
+ unsigned char c;
+
+ if (pthread_mutex_trylock(&self->lock)) {
+ /* We are willing to accept some race in this context */
+ return self->evaluate(self);
+ }
+
+ change_detected = check_cache(&self->cache_persist.cache)
+ || check_cache(&self->cache_ro.cache);
+ current_serial = __system_property_area_serial();
+ if (current_serial != self->serial) {
+ change_detected = 1;
+ }
+ if (change_detected) {
+ refresh_cache(&self->cache_persist, self->key_persist);
+ refresh_cache(&self->cache_ro, self->key_ro);
+ self->serial = current_serial;
+ }
+ c = self->evaluate(self);
+
+ pthread_mutex_unlock(&self->lock);
+
+ return c;
+}
+
+static unsigned char evaluate_persist_ro(const struct cache2_char *self)
+{
+ unsigned char c = self->cache_persist.c;
+
+ if (c) {
+ return c;
+ }
+
+ return self->cache_ro.c;
+}
+
+/*
+ * Timestamp state generally remains constant, but can change at any time
+ * to handle developer requirements.
+ */
+LIBLOG_ABI_PUBLIC clockid_t android_log_clockid()
+{
+ static struct cache2_char clockid = {
+ PTHREAD_MUTEX_INITIALIZER,
+ 0,
+ "persist.logd.timestamp",
+ { { NULL, -1 }, '\0' },
+ "ro.logd.timestamp",
+ { { NULL, -1 }, '\0' },
+ evaluate_persist_ro
+ };
+
+ return (tolower(do_cache2_char(&clockid)) == 'm')
+ ? CLOCK_MONOTONIC
+ : CLOCK_REALTIME;
+}
+
+/*
+ * Security state generally remains constant, but the DO must be able
+ * to turn off logging should it become spammy after an attack is detected.
+ */
+static unsigned char evaluate_security(const struct cache2_char *self)
+{
+ unsigned char c = self->cache_ro.c;
+
+ return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_security()
+{
+ static struct cache2_char security = {
+ PTHREAD_MUTEX_INITIALIZER,
+ 0,
+ "persist.logd.security",
+ { { NULL, -1 }, BOOLEAN_FALSE },
+ "ro.device_owner",
+ { { NULL, -1 }, BOOLEAN_FALSE },
+ evaluate_security
+ };
+
+ return do_cache2_char(&security);
+}
+
+/*
+ * Interface that represents the logd buffer size determination so that others
+ * need not guess our intentions.
+ */
+
+/* Property helper */
+static bool check_flag(const char* prop, const char* flag) {
+ const char* cp = strcasestr(prop, flag);
+ if (!cp) {
+ return false;
+ }
+ /* We only will document comma (,) */
+ static const char sep[] = ",:;|+ \t\f";
+ if ((cp != prop) && !strchr(sep, cp[-1])) {
+ return false;
+ }
+ cp += strlen(flag);
+ return !*cp || !!strchr(sep, *cp);
+}
+
+/* cache structure */
+struct cache_property {
+ struct cache cache;
+ char property[PROP_VALUE_MAX];
+};
+
+static void refresh_cache_property(struct cache_property* cache, const char* key)
+{
+ if (!cache->cache.pinfo) {
+ cache->cache.pinfo = __system_property_find(key);
+ if (!cache->cache.pinfo) {
+ return;
+ }
+ }
+ cache->cache.serial = __system_property_serial(cache->cache.pinfo);
+ __system_property_read(cache->cache.pinfo, 0, cache->property);
+}
+
+/* get boolean with the logger twist that supports eng adjustments */
+LIBLOG_ABI_PRIVATE bool __android_logger_property_get_bool(const char* key,
+ int flag)
+{
+ struct cache_property property = { { NULL, -1 }, { 0 } };
+ if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
+ char newkey[PROP_NAME_MAX];
+ snprintf(newkey, sizeof(newkey), "ro.%s", key);
+ refresh_cache_property(&property, newkey);
+ property.cache.pinfo = NULL;
+ property.cache.serial = -1;
+ snprintf(newkey, sizeof(newkey), "persist.%s", key);
+ refresh_cache_property(&property, newkey);
+ property.cache.pinfo = NULL;
+ property.cache.serial = -1;
+ }
+
+ refresh_cache_property(&property, key);
+
+ if (check_flag(property.property, "true")) {
+ return true;
+ }
+ if (check_flag(property.property, "false")) {
+ return false;
+ }
+ if (check_flag(property.property, "eng")) {
+ flag |= BOOL_DEFAULT_FLAG_ENG;
+ }
+ /* this is really a "not" flag */
+ if (check_flag(property.property, "svelte")) {
+ flag |= BOOL_DEFAULT_FLAG_SVELTE;
+ }
+
+ /* Sanity Check */
+ if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
+ flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
+ flag |= BOOL_DEFAULT_TRUE;
+ }
+
+ if ((flag & BOOL_DEFAULT_FLAG_SVELTE)
+ && __android_logger_property_get_bool("ro.config.low_ram",
+ BOOL_DEFAULT_FALSE)) {
+ return false;
+ }
+ if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
+ return false;
+ }
+
+ return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
+}
+
+LIBLOG_ABI_PRIVATE bool __android_logger_valid_buffer_size(unsigned long value)
+{
+ static long pages, pagesize;
+ unsigned long maximum;
+
+ if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
+ return false;
+ }
+
+ if (!pages) {
+ pages = sysconf(_SC_PHYS_PAGES);
+ }
+ if (pages < 1) {
+ return true;
+ }
+
+ if (!pagesize) {
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize <= 1) {
+ pagesize = PAGE_SIZE;
+ }
+ }
+
+ /* maximum memory impact a somewhat arbitrary ~3% */
+ pages = (pages + 31) / 32;
+ maximum = pages * pagesize;
+
+ if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
+ return true;
+ }
+
+ return value <= maximum;
+}
+
+struct cache2_property_size {
+ pthread_mutex_t lock;
+ uint32_t serial;
+ const char* key_persist;
+ struct cache_property cache_persist;
+ const char* key_ro;
+ struct cache_property cache_ro;
+ unsigned long (*const evaluate)(const struct cache2_property_size* self);
+};
+
+static inline unsigned long do_cache2_property_size(struct cache2_property_size* self)
+{
+ uint32_t current_serial;
+ int change_detected;
+ unsigned long v;
+
+ if (pthread_mutex_trylock(&self->lock)) {
+ /* We are willing to accept some race in this context */
+ return self->evaluate(self);
+ }
+
+ change_detected = check_cache(&self->cache_persist.cache)
+ || check_cache(&self->cache_ro.cache);
+ current_serial = __system_property_area_serial();
+ if (current_serial != self->serial) {
+ change_detected = 1;
+ }
+ if (change_detected) {
+ refresh_cache_property(&self->cache_persist, self->key_persist);
+ refresh_cache_property(&self->cache_ro, self->key_ro);
+ self->serial = current_serial;
+ }
+ v = self->evaluate(self);
+
+ pthread_mutex_unlock(&self->lock);
+
+ return v;
+}
+
+static unsigned long property_get_size_from_cache(const struct cache_property* cache)
+{
+ char* cp;
+ unsigned long value = strtoul(cache->property, &cp, 10);
+
+ switch(*cp) {
+ case 'm':
+ case 'M':
+ value *= 1024;
+ /* FALLTHRU */
+ case 'k':
+ case 'K':
+ value *= 1024;
+ /* FALLTHRU */
+ case '\0':
+ break;
+
+ default:
+ value = 0;
+ }
+
+ if (!__android_logger_valid_buffer_size(value)) {
+ value = 0;
+ }
+
+ return value;
+}
+
+static unsigned long evaluate_property_get_size(const struct cache2_property_size* self)
+{
+ unsigned long size = property_get_size_from_cache(&self->cache_persist);
+ if (size) {
+ return size;
+ }
+ return property_get_size_from_cache(&self->cache_ro);
+}
+
+LIBLOG_ABI_PRIVATE unsigned long __android_logger_get_buffer_size(log_id_t logId)
+{
+ static const char global_tunable[] = "persist.logd.size"; /* Settings App */
+ static const char global_default[] = "ro.logd.size"; /* BoardConfig.mk */
+ static struct cache2_property_size global = {
+ PTHREAD_MUTEX_INITIALIZER,
+ 0,
+ global_tunable,
+ { { NULL, -1 }, {} },
+ global_default,
+ { { NULL, -1 }, {} },
+ evaluate_property_get_size
+ };
+ char key_persist[PROP_NAME_MAX];
+ char key_ro[PROP_NAME_MAX];
+ struct cache2_property_size local = {
+ PTHREAD_MUTEX_INITIALIZER,
+ 0,
+ key_persist,
+ { { NULL, -1 }, {} },
+ key_ro,
+ { { NULL, -1 }, {} },
+ evaluate_property_get_size
+ };
+ unsigned long property_size, default_size;
+
+ default_size = do_cache2_property_size(&global);
+ if (!default_size) {
+ default_size = __android_logger_property_get_bool("ro.config.low_ram",
+ BOOL_DEFAULT_FALSE)
+ ? LOG_BUFFER_MIN_SIZE /* 64K */
+ : LOG_BUFFER_SIZE; /* 256K */
+ }
+
+ snprintf(key_persist, sizeof(key_persist), "%s.%s",
+ global_tunable, android_log_id_to_name(logId));
+ snprintf(key_ro, sizeof(key_ro), "%s.%s",
+ global_default, android_log_id_to_name(logId));
+ property_size = do_cache2_property_size(&local);
+
+ if (!property_size) {
+ property_size = default_size;
+ }
+
+ if (!property_size) {
+ property_size = LOG_BUFFER_SIZE;
+ }
+
+ return property_size;
+}
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index 8229859..097befc 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -28,7 +28,6 @@
-Wall -Wextra \
-Werror \
-fno-builtin \
- -std=gnu++11
benchmark_src_files := \
benchmark_main.cpp \
@@ -40,7 +39,7 @@
LOCAL_MODULE := $(test_module_prefix)benchmarks
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(benchmark_c_flags)
-LOCAL_SHARED_LIBRARIES += liblog libm
+LOCAL_SHARED_LIBRARIES += liblog libm libbase
LOCAL_SRC_FILES := $(benchmark_src_files)
include $(BUILD_NATIVE_TEST)
@@ -54,10 +53,14 @@
-Wall -Wextra \
-Werror \
-fno-builtin \
- -std=gnu++11
test_src_files := \
- liblog_test.cpp
+ liblog_test.cpp \
+ log_id_test.cpp \
+ log_radio_test.cpp \
+ log_read_test.cpp \
+ log_system_test.cpp \
+ log_time_test.cpp
# to prevent breaking the build if bionic not relatively visible to us
ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp),)
@@ -73,6 +76,41 @@
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
+
+cts_executable := CtsLiblogTestCases
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(cts_executable)
+LOCAL_MODULE_TAGS := tests
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
+LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_CTS_TEST_PACKAGE := android.core.liblog
+include $(BUILD_CTS_EXECUTABLE)
+
+ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(cts_executable)_list
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(test_c_flags) -DHOST
+LOCAL_C_INCLUDES := external/gtest/include
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_CXX_STL := libc++
+LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_NATIVE_TEST)
+
+endif # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
new file mode 100644
index 0000000..b8d87e6
--- /dev/null
+++ b/liblog/tests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Logging Library test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsLiblogTestCases->/data/local/tmp/CtsLiblogTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsLiblogTestCases" />
+ <option name="runtime-hint" value="65s" />
+ </test>
+</configuration>
diff --git a/liblog/tests/benchmark.h b/liblog/tests/benchmark.h
index 7f96e6d..e9280f6 100644
--- a/liblog/tests/benchmark.h
+++ b/liblog/tests/benchmark.h
@@ -38,7 +38,7 @@
Benchmark(const char* name, void (*fn)(int)) : name_(strdup(name)), fn_(fn) {
BenchmarkRegister(this);
}
- Benchmark(const char* name) : name_(strdup(name)), fn_(NULL) {}
+ explicit Benchmark(const char* name) : name_(strdup(name)), fn_(NULL) {}
virtual ~Benchmark() {
free(name_);
@@ -141,7 +141,7 @@
void StopBenchmarkTiming(uint64_t);
#define BENCHMARK(f) \
- static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = \
- (::testing::Benchmark*)::testing::BenchmarkFactory(#f, f)
+ static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = /* NOLINT */ \
+ (::testing::Benchmark*)::testing::BenchmarkFactory(#f, f) /* NOLINT */
#endif // BIONIC_BENCHMARK_H_
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 9dd6f03..8cea7dc 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -14,132 +14,36 @@
* limitations under the License.
*/
-#include <fcntl.h>
-#include <sys/cdefs.h>
-
#include <gtest/gtest.h>
-// Should be in bionic test suite, *but* we are using liblog to confirm
-// end-to-end logging, so let the overly cute oedipus complex begin ...
-#include "../../../../bionic/libc/bionic/libc_logging.cpp" // not Standalone
-#define _ANDROID_LOG_H // Priorities redefined
-#define _LIBS_LOG_LOG_H // log ids redefined
-typedef unsigned char log_id_t; // log_id_t missing as a result
-#define _LIBS_LOG_LOG_READ_H // log_time redefined
-
-#include <log/log.h>
-#include <log/logger.h>
-#include <log/log_read.h>
-
-TEST(libc, __libc_android_log_event_int) {
- struct logger_list *logger_list;
-
- pid_t pid = getpid();
-
- ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- int value = ts.tv_nsec;
-
- __libc_android_log_event_int(0, value);
- usleep(1000000);
-
- int count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- ASSERT_EQ(log_msg.entry.pid, pid);
-
- if ((log_msg.entry.len != (4 + 1 + 4))
- || ((int)log_msg.id() != LOG_ID_EVENTS)) {
- continue;
- }
-
- char *eventData = log_msg.msg();
-
- int incoming = (eventData[0] & 0xFF) |
- ((eventData[1] & 0xFF) << 8) |
- ((eventData[2] & 0xFF) << 16) |
- ((eventData[3] & 0xFF) << 24);
-
- if (incoming != 0) {
- continue;
- }
-
- if (eventData[4] != EVENT_TYPE_INT) {
- continue;
- }
-
- incoming = (eventData[4 + 1 + 0] & 0xFF) |
- ((eventData[4 + 1 + 1] & 0xFF) << 8) |
- ((eventData[4 + 1 + 2] & 0xFF) << 16) |
- ((eventData[4 + 1 + 3] & 0xFF) << 24);
-
- if (incoming == value) {
- ++count;
- }
- }
-
- EXPECT_EQ(1, count);
-
- android_logger_list_close(logger_list);
-}
-
-TEST(libc, __libc_fatal_no_abort) {
- struct logger_list *logger_list;
-
- pid_t pid = getpid();
-
- ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- (log_id_t)LOG_ID_CRASH, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
- char b[80];
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
-
- __libc_fatal_no_abort("%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
- snprintf(b, sizeof(b),"%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
- usleep(1000000);
-
- int count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- ASSERT_EQ(log_msg.entry.pid, pid);
-
- if ((int)log_msg.id() != LOG_ID_CRASH) {
- continue;
- }
-
- char *data = log_msg.msg();
-
- if ((*data == ANDROID_LOG_FATAL)
- && !strcmp(data + 1, "libc")
- && !strcmp(data + 1 + strlen(data + 1) + 1, b)) {
- ++count;
- }
- }
-
- EXPECT_EQ(1, count);
-
- android_logger_list_close(logger_list);
-}
+#include <errno.h>
+#include <stdio.h>
TEST(libc, __pstore_append) {
+#ifdef __ANDROID__
FILE *fp;
ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
static const char message[] = "libc.__pstore_append\n";
ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp));
- ASSERT_EQ(0, fclose(fp));
- fprintf(stderr, "Reboot, ensure string libc.__pstore_append is in /sys/fs/pstore/pmsg-ramoops-0\n");
+ int fflushReturn = fflush(fp);
+ int fflushErrno = fflushReturn ? errno : 0;
+ ASSERT_EQ(0, fflushReturn);
+ ASSERT_EQ(0, fflushErrno);
+ int fcloseReturn = fclose(fp);
+ int fcloseErrno = fcloseReturn ? errno : 0;
+ ASSERT_EQ(0, fcloseReturn);
+ ASSERT_EQ(0, fcloseErrno);
+ if ((fcloseErrno == ENOMEM) || (fflushErrno == ENOMEM)) {
+ fprintf(stderr,
+ "Kernel does not have space allocated to pmsg pstore driver configured\n"
+ );
+ }
+ if (!fcloseReturn && !fcloseErrno && !fflushReturn && !fflushReturn) {
+ fprintf(stderr,
+ "Reboot, ensure string libc.__pstore_append is in /sys/fs/pstore/pmsg-ramoops-0\n"
+ );
+ }
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index b594634..dc411c3 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -14,11 +14,20 @@
* limitations under the License.
*/
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <sys/endian.h>
#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unordered_set>
+
+#include <android-base/file.h>
#include <cutils/sockets.h>
-#include <log/log.h>
-#include <log/logger.h>
-#include <log/log_read.h>
+#include <log/event_tag_map.h>
+#include <private/android_logger.h>
#include "benchmark.h"
@@ -85,6 +94,380 @@
BENCHMARK(BM_clock_overhead);
/*
+ * Measure the time it takes to submit the android logging data to pstore
+ */
+static void BM_pmsg_short(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ android_pmsg_log_header_t pmsg_header;
+ pmsg_header.magic = LOGGER_MAGIC;
+ pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ pmsg_header.uid = getuid();
+ pmsg_header.pid = getpid();
+
+ android_log_header_t header;
+ header.tid = gettid();
+ header.realtime.tv_sec = ts.tv_sec;
+ header.realtime.tv_nsec = ts.tv_nsec;
+
+ static const unsigned nr = 1;
+ static const unsigned header_length = 2;
+ struct iovec newVec[nr + header_length];
+
+ newVec[0].iov_base = (unsigned char *) &pmsg_header;
+ newVec[0].iov_len = sizeof(pmsg_header);
+ newVec[1].iov_base = (unsigned char *) &header;
+ newVec[1].iov_len = sizeof(header);
+
+ android_log_event_int_t buffer;
+
+ header.id = LOG_ID_EVENTS;
+ buffer.header.tag = 0;
+ buffer.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer.payload.data = htole32(snapshot);
+
+ newVec[2].iov_base = &buffer;
+ newVec[2].iov_len = sizeof(buffer);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer.payload.data = htole32(snapshot);
+ writev(pstore_fd, newVec, nr);
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_aligned(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ alignas(8) char buf[sizeof(struct packet) + 8];
+ memset(buf, 0, sizeof(buf));
+ struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+ if (((uintptr_t)&buffer->pmsg_header) & 7) {
+ fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = htole32(snapshot);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer->payload.payload.data = htole32(snapshot);
+ write(pstore_fd, &buffer->pmsg_header,
+ sizeof(android_pmsg_log_header_t) +
+ sizeof(android_log_header_t) +
+ sizeof(android_log_event_int_t));
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_unaligned1(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ alignas(8) char buf[sizeof(struct packet) + 8];
+ memset(buf, 0, sizeof(buf));
+ struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+ if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+ fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = htole32(snapshot);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer->payload.payload.data = htole32(snapshot);
+ write(pstore_fd, &buffer->pmsg_header,
+ sizeof(android_pmsg_log_header_t) +
+ sizeof(android_log_header_t) +
+ sizeof(android_log_event_int_t));
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_unaligned1);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_aligned(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ alignas(8) char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD];
+ memset(buf, 0, sizeof(buf));
+ struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+ if (((uintptr_t)&buffer->pmsg_header) & 7) {
+ fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = htole32(snapshot);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer->payload.payload.data = htole32(snapshot);
+ write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_unaligned1(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ alignas(8) char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD];
+ memset(buf, 0, sizeof(buf));
+ struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+ if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+ fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = htole32(snapshot);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer->payload.payload.data = htole32(snapshot);
+ write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_unaligned1);
+
+/*
* Measure the time it takes to submit the android logging call using
* discrete acquisition under light load. Expect this to be a dozen or so
* syscall periods (40us).
@@ -162,7 +545,7 @@
char* eventData = log_msg.msg();
- if (eventData[4] != EVENT_TYPE_LONG) {
+ if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
continue;
}
log_time tx(eventData + 4 + 1);
@@ -242,7 +625,7 @@
char* eventData = log_msg.msg();
- if (eventData[4] != EVENT_TYPE_LONG) {
+ if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
continue;
}
log_time tx(eventData + 4 + 1);
@@ -271,12 +654,276 @@
* Measure the time it takes for __android_log_is_loggable.
*/
static void BM_is_loggable(int iters) {
+ static const char logd[] = "logd";
+
StartBenchmarkTiming();
for (int i = 0; i < iters; ++i) {
- __android_log_is_loggable(ANDROID_LOG_WARN, "logd", ANDROID_LOG_VERBOSE);
+ __android_log_is_loggable_len(ANDROID_LOG_WARN,
+ logd, strlen(logd),
+ ANDROID_LOG_VERBOSE);
}
StopBenchmarkTiming();
}
BENCHMARK(BM_is_loggable);
+
+/*
+ * Measure the time it takes for android_log_clockid.
+ */
+static void BM_clockid(int iters) {
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ android_log_clockid();
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_clockid);
+
+/*
+ * Measure the time it takes for __android_log_security.
+ */
+static void BM_security(int iters) {
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ __android_log_security();
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_security);
+
+// Keep maps around for multiple iterations
+static std::unordered_set<uint32_t> set;
+static EventTagMap* map;
+
+static bool prechargeEventMap() {
+ if (map) return true;
+
+ fprintf(stderr, "Precharge: start\n");
+
+ map = android_openEventTagMap(NULL);
+ for (uint32_t tag = 1; tag < USHRT_MAX; ++tag) {
+ size_t len;
+ if (android_lookupEventTag_len(map, &len, tag) == NULL) continue;
+ set.insert(tag);
+ }
+
+ fprintf(stderr, "Precharge: stop %zu\n", set.size());
+
+ return true;
+}
+
+/*
+ * Measure the time it takes for android_lookupEventTag_len
+ */
+static void BM_lookupEventTag(int iters) {
+
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ size_t len;
+ android_lookupEventTag_len(map, &len, (*it));
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_lookupEventTag);
+
+/*
+ * Measure the time it takes for android_lookupEventTag_len
+ */
+static uint32_t notTag = 1;
+
+static void BM_lookupEventTag_NOT(int iters) {
+
+ prechargeEventMap();
+
+ while (set.find(notTag) != set.end()) {
+ ++notTag;
+ if (notTag >= USHRT_MAX) notTag = 1;
+ }
+
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ size_t len;
+ android_lookupEventTag_len(map, &len, notTag);
+ }
+
+ StopBenchmarkTiming();
+
+ ++notTag;
+ if (notTag >= USHRT_MAX) notTag = 1;
+}
+BENCHMARK(BM_lookupEventTag_NOT);
+
+/*
+ * Measure the time it takes for android_lookupEventFormat_len
+ */
+static void BM_lookupEventFormat(int iters) {
+
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ size_t len;
+ android_lookupEventFormat_len(map, &len, (*it));
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_lookupEventFormat);
+
+/*
+ * Measure the time it takes for android_lookupEventTagNum plus above
+ */
+static void BM_lookupEventTagNum(int iters) {
+
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ for (int i = 0; i < iters; ++i) {
+ size_t len;
+ const char* name = android_lookupEventTag_len(map, &len, (*it));
+ std::string Name(name, len);
+ const char* format = android_lookupEventFormat_len(map, &len, (*it));
+ std::string Format(format, len);
+ StartBenchmarkTiming();
+ android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
+ ANDROID_LOG_UNKNOWN);
+ StopBenchmarkTiming();
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+
+}
+BENCHMARK(BM_lookupEventTagNum);
+
+// Must be functionally identical to liblog internal __send_log_msg.
+static void send_to_control(char *buf, size_t len)
+{
+ int sock = socket_local_client("logd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (sock < 0) return;
+ size_t writeLen = strlen(buf) + 1;
+
+ ssize_t ret = TEMP_FAILURE_RETRY(write(sock, buf, writeLen));
+ if (ret <= 0) {
+ close(sock);
+ return;
+ }
+ while ((ret = read(sock, buf, len)) > 0) {
+ if (((size_t)ret == len) || (len < PAGE_SIZE)) {
+ break;
+ }
+ len -= ret;
+ buf += ret;
+
+ struct pollfd p = {
+ .fd = sock,
+ .events = POLLIN,
+ .revents = 0
+ };
+
+ ret = poll(&p, 1, 20);
+ if ((ret <= 0) || !(p.revents & POLLIN)) {
+ break;
+ }
+ }
+ close(sock);
+}
+
+static void BM_lookupEventTagNum_logd_new(int iters) {
+ fprintf(stderr, "WARNING: "
+ "This test can cause logd to grow in size and hit DOS limiter\n");
+ // Make copies
+ static const char empty_event_log_tags[] = "# content owned by logd\n";
+ static const char dev_event_log_tags_path[] = "/dev/event-log-tags";
+ std::string dev_event_log_tags;
+ if (android::base::ReadFileToString(dev_event_log_tags_path,
+ &dev_event_log_tags) &&
+ (dev_event_log_tags.length() == 0)) {
+ dev_event_log_tags = empty_event_log_tags;
+ }
+ static const char data_event_log_tags_path[] = "/data/misc/logd/event-log-tags";
+ std::string data_event_log_tags;
+ if (android::base::ReadFileToString(data_event_log_tags_path,
+ &data_event_log_tags) &&
+ (data_event_log_tags.length() == 0)) {
+ data_event_log_tags = empty_event_log_tags;
+ }
+
+ for (int i = 0; i < iters; ++i) {
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ log_time now(CLOCK_MONOTONIC);
+ char name[64];
+ snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+ snprintf(buffer, sizeof(buffer),
+ "getEventTag name=%s format=\"(new|1)\"", name);
+ StartBenchmarkTiming();
+ send_to_control(buffer, sizeof(buffer));
+ StopBenchmarkTiming();
+ }
+
+ // Restore copies (logd still know about them, until crash or reboot)
+ if (dev_event_log_tags.length() &&
+ !android::base::WriteStringToFile(dev_event_log_tags,
+ dev_event_log_tags_path)) {
+ fprintf(stderr, "WARNING: "
+ "failed to restore %s\n", dev_event_log_tags_path);
+ }
+ if (data_event_log_tags.length() &&
+ !android::base::WriteStringToFile(data_event_log_tags,
+ data_event_log_tags_path)) {
+ fprintf(stderr, "WARNING: "
+ "failed to restore %s\n", data_event_log_tags_path);
+ }
+ fprintf(stderr, "WARNING: "
+ "Restarting logd to make it forget what we just did\n");
+ system("stop logd ; start logd");
+}
+BENCHMARK(BM_lookupEventTagNum_logd_new);
+
+static void BM_lookupEventTagNum_logd_existing(int iters) {
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ for (int i = 0; i < iters; ++i) {
+ size_t len;
+ const char* name = android_lookupEventTag_len(map, &len, (*it));
+ std::string Name(name, len);
+ const char* format = android_lookupEventFormat_len(map, &len, (*it));
+ std::string Format(format, len);
+
+ char buffer[256];
+ snprintf(buffer, sizeof(buffer),
+ "getEventTag name=%s format=\"%s\"",
+ Name.c_str(), Format.c_str());
+
+ StartBenchmarkTiming();
+ send_to_control(buffer, sizeof(buffer));
+ StopBenchmarkTiming();
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+}
+BENCHMARK(BM_lookupEventTagNum_logd_existing);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index abe0239..5faf8e1 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Android Open Source Project
+ * Copyright (C) 2013-2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,31 @@
* limitations under the License.
*/
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <pthread.h>
+#include <semaphore.h>
#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#ifdef __ANDROID__ // includes sys/properties.h which does not exist outside
#include <cutils/properties.h>
+#endif
#include <gtest/gtest.h>
-#include <log/log.h>
-#include <log/logger.h>
-#include <log/log_read.h>
#include <log/logprint.h>
+#include <log/log_event_list.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
// non-syscall libs. Since we are only using this in the emergency of
@@ -40,37 +55,8 @@
|| (_rc == -EAGAIN)); \
_rc; })
-TEST(liblog, __android_log_buf_print) {
- EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
- "TEST__android_log_buf_print",
- "radio"));
- usleep(1000);
- EXPECT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
- "TEST__android_log_buf_print",
- "system"));
- usleep(1000);
- EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
- "TEST__android_log_buf_print",
- "main"));
- usleep(1000);
-}
-
-TEST(liblog, __android_log_buf_write) {
- EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
- "TEST__android_log_buf_write",
- "radio"));
- usleep(1000);
- EXPECT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
- "TEST__android_log_buf_write",
- "system"));
- usleep(1000);
- EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
- "TEST__android_log_buf_write",
- "main"));
- usleep(1000);
-}
-
TEST(liblog, __android_log_btwrite) {
+#ifdef __ANDROID__
int intBuf = 0xDEADBEEF;
EXPECT_LT(0, __android_log_btwrite(0,
EVENT_TYPE_INT,
@@ -85,40 +71,81 @@
EVENT_TYPE_STRING,
Buf, sizeof(Buf) - 1));
usleep(1000);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
-static void* ConcurrentPrintFn(void *arg) {
- int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
- "TEST__android_log_print", "Concurrent %" PRIuPTR,
- reinterpret_cast<uintptr_t>(arg));
- return reinterpret_cast<void*>(ret);
-}
+#ifdef __ANDROID__
+std::string popenToString(std::string command) {
+ std::string ret;
-#define NUM_CONCURRENT 64
-#define _concurrent_name(a,n) a##__concurrent##n
-#define concurrent_name(a,n) _concurrent_name(a,n)
-
-TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) {
- pthread_t t[NUM_CONCURRENT];
- int i;
- for (i=0; i < NUM_CONCURRENT; i++) {
- ASSERT_EQ(0, pthread_create(&t[i], NULL,
- ConcurrentPrintFn,
- reinterpret_cast<void *>(i)));
+ FILE* fp = popen(command.c_str(), "r");
+ if (fp) {
+ if (!android::base::ReadFdToString(fileno(fp), &ret)) ret = "";
+ pclose(fp);
}
- int ret = 0;
- for (i=0; i < NUM_CONCURRENT; i++) {
- void* result;
- ASSERT_EQ(0, pthread_join(t[i], &result));
- int this_result = reinterpret_cast<uintptr_t>(result);
- if ((0 == ret) && (0 != this_result)) {
- ret = this_result;
- }
- }
- ASSERT_LT(0, ret);
+ return ret;
}
+static bool isPmsgActive() {
+ pid_t pid = getpid();
+
+ std::string myPidFds = popenToString(android::base::StringPrintf(
+ "ls -l /proc/%d/fd", pid));
+ if (myPidFds.length() == 0) return true; // guess it is?
+
+ return std::string::npos != myPidFds.find(" -> /dev/pmsg0");
+}
+
+static bool isLogdwActive() {
+ std::string logdwSignature = popenToString(
+ "grep /dev/socket/logdw /proc/net/unix");
+ size_t beginning = logdwSignature.find(" ");
+ if (beginning == std::string::npos) return true;
+ beginning = logdwSignature.find(" ", beginning + 1);
+ if (beginning == std::string::npos) return true;
+ size_t end = logdwSignature.find(" ", beginning + 1);
+ if (end == std::string::npos) return true;
+ end = logdwSignature.find(" ", end + 1);
+ if (end == std::string::npos) return true;
+ end = logdwSignature.find(" ", end + 1);
+ if (end == std::string::npos) return true;
+ end = logdwSignature.find(" ", end + 1);
+ if (end == std::string::npos) return true;
+ std::string allLogdwEndpoints = popenToString(
+ "grep ' 00000002" +
+ logdwSignature.substr(beginning, end - beginning) +
+ " ' /proc/net/unix | " +
+ "sed -n 's/.* \\([0-9][0-9]*\\)$/ -> socket:[\\1]/p'");
+ if (allLogdwEndpoints.length() == 0) return true;
+
+ // NB: allLogdwEndpoints has some false positives in it, but those
+ // strangers do not overlap with the simplistic activities inside this
+ // test suite.
+
+ pid_t pid = getpid();
+
+ std::string myPidFds = popenToString(android::base::StringPrintf(
+ "ls -l /proc/%d/fd", pid));
+ if (myPidFds.length() == 0) return true;
+
+ // NB: fgrep with multiple strings is broken in Android
+ for (beginning = 0;
+ (end = allLogdwEndpoints.find("\n", beginning)) != std::string::npos;
+ beginning = end + 1) {
+ if (myPidFds.find(allLogdwEndpoints.substr(beginning,
+ end - beginning)) !=
+ std::string::npos) return true;
+ }
+ return false;
+}
+
+bool tested__android_log_close;
+#endif
+
TEST(liblog, __android_log_btwrite__android_logger_list_read) {
+#ifdef __ANDROID__
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -126,9 +153,118 @@
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+ // Check that we can close and reopen the logger
log_time ts(CLOCK_MONOTONIC);
-
ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ bool pmsgActiveAfter__android_log_btwrite;
+ bool logdwActiveAfter__android_log_btwrite;
+ if (getuid() == AID_ROOT) {
+ tested__android_log_close = true;
+ pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+ logdwActiveAfter__android_log_btwrite = isLogdwActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+ EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+ } else if (!tested__android_log_close) {
+ fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+ }
+ __android_log_close();
+ if (getuid() == AID_ROOT) {
+ bool pmsgActiveAfter__android_log_close = isPmsgActive();
+ bool logdwActiveAfter__android_log_close = isLogdwActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+ EXPECT_FALSE(logdwActiveAfter__android_log_close);
+ }
+
+ log_time ts1(CLOCK_MONOTONIC);
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+ if (getuid() == AID_ROOT) {
+ pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+ logdwActiveAfter__android_log_btwrite = isLogdwActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+ EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+ }
+ usleep(1000000);
+
+ int count = 0;
+ int second_count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != sizeof(android_log_event_long_t))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+
+ if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+ continue;
+ }
+
+ log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+ if (ts == tx) {
+ ++count;
+ } else if (ts1 == tx) {
+ ++second_count;
+ }
+ }
+
+ EXPECT_EQ(1, count);
+ EXPECT_EQ(1, second_count);
+
+ android_logger_list_close(logger_list);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+static inline int32_t get4LE(const char* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+#endif
+
+static void bswrite_test(const char *message) {
+#ifdef __ANDROID__
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ log_time ts(android_log_clockid());
+
+ ASSERT_LT(0, __android_log_bswrite(0, message));
+ size_t num_lines = 1, size = 0, length = 0, total = 0;
+ const char *cp = message;
+ while (*cp) {
+ if (*cp == '\n') {
+ if (cp[1]) {
+ ++num_lines;
+ }
+ } else {
+ ++size;
+ }
+ ++cp;
+ ++total;
+ ++length;
+ if ((LOGGER_ENTRY_MAX_PAYLOAD - 4 - 1 - 4) <= length) {
+ break;
+ }
+ }
+ while (*cp) {
+ ++cp;
+ ++total;
+ }
usleep(1000000);
int count = 0;
@@ -141,32 +277,170 @@
ASSERT_EQ(log_msg.entry.pid, pid);
- if ((log_msg.entry.len != (4 + 1 + 8))
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || ((size_t)log_msg.entry.len != (sizeof(android_log_event_string_t) +
+ length))
|| (log_msg.id() != LOG_ID_EVENTS)) {
continue;
}
- char *eventData = log_msg.msg();
+ android_log_event_string_t* eventData;
+ eventData = reinterpret_cast<android_log_event_string_t*>(log_msg.msg());
- if (eventData[4] != EVENT_TYPE_LONG) {
+ if (!eventData || (eventData->type != EVENT_TYPE_STRING)) {
continue;
}
- log_time tx(eventData + 4 + 1);
- if (ts == tx) {
+ size_t len = get4LE(reinterpret_cast<char*>(&eventData->length));
+ if (len == total) {
++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ char msgBuf[1024];
+ if (length != total) {
+ fprintf(stderr, "Expect \"Binary log entry conversion failed\"\n");
+ }
+ int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
+ &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+ EXPECT_EQ((length == total) ? 0 : -1, processBinaryLogBuffer);
+ if (processBinaryLogBuffer == 0) {
+ fflush(stderr);
+ EXPECT_EQ((int)((20 * num_lines) + size),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
+ }
+ android_log_format_free(logformat);
}
}
EXPECT_EQ(1, count);
android_logger_list_close(logger_list);
+#else
+ message = NULL;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
-static unsigned signaled;
-log_time signal_time;
+TEST(liblog, __android_log_bswrite_and_print) {
+ bswrite_test("Hello World");
+}
-static void caught_blocking(int /*signum*/)
+TEST(liblog, __android_log_bswrite_and_print__empty_string) {
+ bswrite_test("");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__newline_prefix) {
+ bswrite_test("\nHello World\n");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__newline_space_prefix) {
+ bswrite_test("\n Hello World \n");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__multiple_newline) {
+ bswrite_test("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten");
+}
+
+static void buf_write_test(const char *message) {
+#ifdef __ANDROID__
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ static const char tag[] = "TEST__android_log_buf_write";
+ log_time ts(android_log_clockid());
+
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ tag, message));
+ size_t num_lines = 1, size = 0, length = 0;
+ const char *cp = message;
+ while (*cp) {
+ if (*cp == '\n') {
+ if (cp[1]) {
+ ++num_lines;
+ }
+ } else {
+ ++size;
+ }
+ ++length;
+ if ((LOGGER_ENTRY_MAX_PAYLOAD - 2 - sizeof(tag)) <= length) {
+ break;
+ }
+ ++cp;
+ }
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || ((size_t)log_msg.entry.len != (sizeof(tag) + length + 2))
+ || (log_msg.id() != LOG_ID_MAIN)) {
+ continue;
+ }
+
+ ++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ int processLogBuffer = android_log_processLogBuffer(&log_msg.entry_v1,
+ &entry);
+ EXPECT_EQ(0, processLogBuffer);
+ if (processLogBuffer == 0) {
+ fflush(stderr);
+ EXPECT_EQ((int)(((11 + sizeof(tag)) * num_lines) + size),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
+ }
+ android_log_format_free(logformat);
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+#else
+ message = NULL;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_buf_write_and_print__empty) {
+ buf_write_test("");
+}
+
+TEST(liblog, __android_log_buf_write_and_print__newline_prefix) {
+ buf_write_test("\nHello World\n");
+}
+
+TEST(liblog, __android_log_buf_write_and_print__newline_space_prefix) {
+ buf_write_test("\n Hello World \n");
+}
+
+#ifdef __ANDROID__
+static unsigned signaled;
+static log_time signal_time;
+
+/*
+ * Strictly, we are not allowed to log messages in a signal context, but we
+ * do make an effort to keep the failure surface minimized, and this in-effect
+ * should catch any regressions in that effort. The odds of a logged message
+ * in a signal handler causing a lockup problem should be _very_ small.
+ */
+static void caught_blocking_signal(int /*signum*/)
{
unsigned long long v = 0xDEADBEEFA55A0000ULL;
@@ -215,8 +489,10 @@
*uticks = *sticks = 0;
}
}
+#endif
-TEST(liblog, android_logger_list_read__cpu) {
+TEST(liblog, android_logger_list_read__cpu_signal) {
+#ifdef __ANDROID__
struct logger_list *logger_list;
unsigned long long v = 0xDEADBEEFA55A0000ULL;
@@ -239,7 +515,7 @@
memset(&signal_time, 0, sizeof(signal_time));
- signal(SIGALRM, caught_blocking);
+ signal(SIGALRM, caught_blocking_signal);
alarm(alarm_time);
signaled = 0;
@@ -256,25 +532,27 @@
ASSERT_EQ(log_msg.entry.pid, pid);
- if ((log_msg.entry.len != (4 + 1 + 8))
+ if ((log_msg.entry.len != sizeof(android_log_event_long_t))
|| (log_msg.id() != LOG_ID_EVENTS)) {
continue;
}
- char *eventData = log_msg.msg();
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
- if (eventData[4] != EVENT_TYPE_LONG) {
+ if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
continue;
}
- unsigned long long l = eventData[4 + 1 + 0] & 0xFF;
- l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8;
- l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16;
- l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24;
- l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32;
- l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40;
- l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48;
- l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56;
+ char* cp = reinterpret_cast<char*>(&eventData->payload.data);
+ unsigned long long l = cp[0] & 0xFF;
+ l |= (unsigned long long) (cp[1] & 0xFF) << 8;
+ l |= (unsigned long long) (cp[2] & 0xFF) << 16;
+ l |= (unsigned long long) (cp[3] & 0xFF) << 24;
+ l |= (unsigned long long) (cp[4] & 0xFF) << 32;
+ l |= (unsigned long long) (cp[5] & 0xFF) << 40;
+ l |= (unsigned long long) (cp[6] & 0xFF) << 48;
+ l |= (unsigned long long) (cp[7] & 0xFF) << 56;
if (l == v) {
++signals;
@@ -284,7 +562,7 @@
alarm(0);
signal(SIGALRM, SIG_DFL);
- EXPECT_LT(1, count);
+ EXPECT_LE(1, count);
EXPECT_EQ(1, signals);
@@ -301,11 +579,176 @@
EXPECT_GT(one_percent_ticks, user_ticks);
EXPECT_GT(one_percent_ticks, system_ticks);
EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
-static const char max_payload_tag[] = "TEST_max_payload_XXXX";
-static const char max_payload_buf[LOGGER_ENTRY_MAX_PAYLOAD
- - sizeof(max_payload_tag) - 1] = "LEONATO\n\
+#ifdef __ANDROID__
+/*
+ * Strictly, we are not allowed to log messages in a signal context, the
+ * correct way to handle this is to ensure the messages are constructed in
+ * a thread; the signal handler should only unblock the thread.
+ */
+static sem_t thread_trigger;
+
+static void caught_blocking_thread(int /*signum*/)
+{
+ sem_post(&thread_trigger);
+}
+
+static void *running_thread(void *) {
+ unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+ v += getpid() & 0xFFFF;
+
+ struct timespec timeout;
+ clock_gettime(CLOCK_REALTIME, &timeout);
+ timeout.tv_sec += 55;
+ sem_timedwait(&thread_trigger, &timeout);
+
+ ++signaled;
+ if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) {
+ signal_time = log_time(CLOCK_MONOTONIC);
+ signal_time.tv_sec += 2;
+ }
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+ return NULL;
+}
+
+int start_thread()
+{
+ sem_init(&thread_trigger, 0, 0);
+
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr)) {
+ return -1;
+ }
+
+ struct sched_param param;
+
+ memset(¶m, 0, sizeof(param));
+ pthread_attr_setschedparam(&attr, ¶m);
+ pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+ pthread_attr_destroy(&attr);
+ return -1;
+ }
+
+ pthread_t thread;
+ if (pthread_create(&thread, &attr, running_thread, NULL)) {
+ pthread_attr_destroy(&attr);
+ return -1;
+ }
+
+ pthread_attr_destroy(&attr);
+ return 0;
+}
+#endif
+
+TEST(liblog, android_logger_list_read__cpu_thread) {
+#ifdef __ANDROID__
+ struct logger_list *logger_list;
+ unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
+
+ int count = 0;
+
+ int signals = 0;
+
+ unsigned long long uticks_start;
+ unsigned long long sticks_start;
+ get_ticks(&uticks_start, &sticks_start);
+
+ const unsigned alarm_time = 10;
+
+ memset(&signal_time, 0, sizeof(signal_time));
+
+ signaled = 0;
+ EXPECT_EQ(0, start_thread());
+
+ signal(SIGALRM, caught_blocking_thread);
+ alarm(alarm_time);
+
+ do {
+ log_msg log_msg;
+ if (LOG_FAILURE_RETRY(android_logger_list_read(logger_list, &log_msg)) <= 0) {
+ break;
+ }
+
+ alarm(alarm_time);
+
+ ++count;
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != sizeof(android_log_event_long_t))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+
+ if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+ continue;
+ }
+
+ char* cp = reinterpret_cast<char*>(&eventData->payload.data);
+ unsigned long long l = cp[0] & 0xFF;
+ l |= (unsigned long long) (cp[1] & 0xFF) << 8;
+ l |= (unsigned long long) (cp[2] & 0xFF) << 16;
+ l |= (unsigned long long) (cp[3] & 0xFF) << 24;
+ l |= (unsigned long long) (cp[4] & 0xFF) << 32;
+ l |= (unsigned long long) (cp[5] & 0xFF) << 40;
+ l |= (unsigned long long) (cp[6] & 0xFF) << 48;
+ l |= (unsigned long long) (cp[7] & 0xFF) << 56;
+
+ if (l == v) {
+ ++signals;
+ break;
+ }
+ } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time));
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ EXPECT_LE(1, count);
+
+ EXPECT_EQ(1, signals);
+
+ android_logger_list_close(logger_list);
+
+ unsigned long long uticks_end;
+ unsigned long long sticks_end;
+ get_ticks(&uticks_end, &sticks_end);
+
+ // Less than 1% in either user or system time, or both
+ const unsigned long long one_percent_ticks = alarm_time;
+ unsigned long long user_ticks = uticks_end - uticks_start;
+ unsigned long long system_ticks = sticks_end - sticks_start;
+ EXPECT_GT(one_percent_ticks, user_ticks);
+ EXPECT_GT(one_percent_ticks, system_ticks);
+ EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
+#define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - \
+ sizeof(max_payload_tag) - 1)
+#endif
+static const char max_payload_buf[] = "LEONATO\n\
I learn in this letter that Don Peter of Arragon\n\
comes this night to Messina\n\
MESSENGER\n\
@@ -431,9 +874,13 @@
trouble: the fashion of the world is to avoid\n\
cost, and you encounter it\n\
LEONATO\n\
-Never came trouble to my house in the likeness";
+Never came trouble to my house in the likeness of your grace,\n\
+for trouble being gone, comfort should remain, but\n\
+when you depart from me, sorrow abides and happiness\n\
+takes his leave.";
TEST(liblog, max_payload) {
+#ifdef __ANDROID__
pid_t pid = getpid();
char tag[sizeof(max_payload_tag)];
memcpy(tag, max_payload_tag, sizeof(tag));
@@ -461,9 +908,9 @@
continue;
}
- char *data = log_msg.msg() + 1;
+ char *data = log_msg.msg();
- if (strcmp(data, tag)) {
+ if (!data || strcmp(++data, tag)) {
continue;
}
@@ -490,10 +937,74 @@
EXPECT_EQ(true, matches);
- EXPECT_LE(sizeof(max_payload_buf), static_cast<size_t>(max_len));
+ EXPECT_LE(SIZEOF_MAX_PAYLOAD_BUF, static_cast<size_t>(max_len));
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_buf_print__maxtag) {
+#ifdef __ANDROID__
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ log_time ts(android_log_clockid());
+
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ max_payload_buf, max_payload_buf));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD)
+ || (log_msg.id() != LOG_ID_MAIN)) {
+ continue;
+ }
+
+ ++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ int processLogBuffer = android_log_processLogBuffer(&log_msg.entry_v1,
+ &entry);
+ EXPECT_EQ(0, processLogBuffer);
+ if (processLogBuffer == 0) {
+ fflush(stderr);
+ int printLogLine =
+ android_log_printLogLine(logformat, fileno(stderr), &entry);
+ // Legacy tag truncation
+ EXPECT_LE(128, printLogLine);
+ // Measured maximum if we try to print part of the tag as message
+ EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD * 13 / 8, printLogLine);
+ }
+ android_log_format_free(logformat);
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
TEST(liblog, too_big_payload) {
+#ifdef __ANDROID__
pid_t pid = getpid();
static const char big_payload_tag[] = "TEST_big_payload_XXXX";
char tag[sizeof(big_payload_tag)];
@@ -522,9 +1033,9 @@
continue;
}
- char *data = log_msg.msg() + 1;
+ char *data = log_msg.msg();
- if (strcmp(data, tag)) {
+ if (!data || strcmp(++data, tag)) {
continue;
}
@@ -548,9 +1059,13 @@
static_cast<size_t>(max_len));
EXPECT_EQ(ret, max_len + static_cast<ssize_t>(sizeof(big_payload_tag)));
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
TEST(liblog, dual_reader) {
+#ifdef __ANDROID__
struct logger_list *logger_list1;
// >25 messages due to liblog.__android_log_buf_print__concurrentXX above.
@@ -595,38 +1110,20 @@
EXPECT_EQ(25, count1);
EXPECT_EQ(15, count2);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
-TEST(liblog, android_logger_get_) {
- struct logger_list * logger_list = android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0);
-
- for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
- log_id_t id = static_cast<log_id_t>(i);
- const char *name = android_log_id_to_name(id);
- if (id != android_name_to_log_id(name)) {
- continue;
- }
- fprintf(stderr, "log buffer %s\r", name);
- struct logger * logger;
- EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
- EXPECT_EQ(id, android_logger_get_id(logger));
- /* crash buffer is allowed to be empty, that is actually healthy! */
- if (android_logger_get_log_size(logger) || strcmp("crash", name)) {
- EXPECT_LT(0, android_logger_get_log_size(logger));
- }
- EXPECT_LT(0, android_logger_get_log_readable_size(logger));
- EXPECT_LT(0, android_logger_get_log_version(logger));
- }
-
- android_logger_list_close(logger_list);
-}
-
+#ifdef __ANDROID__
static bool checkPriForTag(AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) {
return android_log_shouldPrintLine(p_format, tag, pri)
&& !android_log_shouldPrintLine(p_format, tag, (android_LogPriority)(pri - 1));
}
+#endif
TEST(liblog, filterRule) {
+#ifdef __ANDROID__
static const char tag[] = "random";
AndroidLogFormat *p_format = android_log_format_new();
@@ -688,9 +1185,13 @@
#endif
android_log_format_free(p_format);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
TEST(liblog, is_loggable) {
+#ifdef __ANDROID__
static const char tag[] = "is_loggable";
static const char log_namespace[] = "persist.log.tag.";
static const size_t base_offset = 8; /* skip "persist." */
@@ -735,13 +1236,27 @@
continue;
}
fprintf(stderr, "i=%zu j=%zu\r", i, j);
+ bool android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), levels[j].level);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- levels[j].level));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), levels[j].level));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- levels[j].level));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), levels[j].level));
+ }
}
}
}
@@ -759,32 +1274,63 @@
snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key, buf);
+ usleep(20000);
property_set(key, buf);
+ bool android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_DEBUG)
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
}
+ usleep(20000);
property_set(key, "");
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key + base_offset, buf);
property_set(key + base_offset, buf);
+ android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_DEBUG)
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
}
+ usleep(20000);
property_set(key + base_offset, "");
strcpy(key, log_namespace);
@@ -792,31 +1338,61 @@
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key, buf);
property_set(key, buf);
+ android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_DEBUG)
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
}
+ usleep(20000);
property_set(key, "");
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key + base_offset, buf);
property_set(key + base_offset, buf);
+ android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_DEBUG)
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
}
+ usleep(20000);
property_set(key + base_offset, "");
}
}
@@ -824,6 +1400,7 @@
// All combinations of level and tag properties, but with global set to INFO
strcpy(key, log_namespace);
key[sizeof(log_namespace) - 2] = '\0';
+ usleep(20000);
property_set(key, "I");
snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
@@ -837,42 +1414,1534 @@
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key, buf);
+ usleep(20000);
property_set(key, buf);
+ bool android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
}
+ usleep(20000);
property_set(key, "");
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key + base_offset, buf);
property_set(key + base_offset, buf);
+ android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
}
+ usleep(20000);
property_set(key + base_offset, "");
}
}
// reset parms
snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+ usleep(20000);
property_set(key, hold[0]);
property_set(key + base_offset, hold[1]);
strcpy(key, log_namespace);
key[sizeof(log_namespace) - 2] = '\0';
property_set(key, hold[2]);
property_set(key + base_offset, hold[3]);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+// helper to liblog.enoent to count end-to-end matching logging messages.
+static int count_matching_ts(log_time ts) {
+ usleep(1000000);
+
+ pid_t pid = getpid();
+
+ struct logger_list* logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid);
+
+ int count = 0;
+ if (logger_list == NULL) return count;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+ if (log_msg.entry.len != sizeof(android_log_event_long_t)) continue;
+ if (log_msg.id() != LOG_ID_EVENTS) continue;
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+ if (!eventData) continue;
+ if (eventData->payload.type != EVENT_TYPE_LONG) continue;
+
+ log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+ if (ts != tx) continue;
+
+ // found event message with matching timestamp signature in payload
+ ++count;
+ }
+ android_logger_list_close(logger_list);
+
+ return count;
+}
+
+// meant to be handed to ASSERT_TRUE / EXPECT_TRUE only to expand the message
+static testing::AssertionResult IsOk(bool ok, std::string &message) {
+ return ok ?
+ testing::AssertionSuccess() :
+ (testing::AssertionFailure() << message);
+}
+#endif
+
+TEST(liblog, enoent) {
+#ifdef __ANDROID__
+ log_time ts(CLOCK_MONOTONIC);
+ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ EXPECT_EQ(1, count_matching_ts(ts));
+
+ // This call will fail if we are setuid(AID_SYSTEM), beware of any
+ // test prior to this one playing with setuid and causing interference.
+ // We need to run before these tests so that they do not interfere with
+ // this test.
+ //
+ // Stopping the logger can affect some other test's expectations as they
+ // count on the log buffers filled with existing content, and this
+ // effectively does a logcat -c emptying it. So we want this test to be
+ // as near as possible to the bottom of the file. For example
+ // liblog.android_logger_get_ is one of those tests that has no recourse
+ // and that would be adversely affected by emptying the log if it was run
+ // right after this test.
+ system("stop logd");
+ usleep(1000000);
+
+ // A clean stop like we are testing returns -ENOENT, but in the _real_
+ // world we could get -ENOTCONN or -ECONNREFUSED depending on timing.
+ // Alas we can not test these other return values; accept that they
+ // are treated equally within the open-retry logic in liblog.
+ ts = log_time(CLOCK_MONOTONIC);
+ int ret = __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts));
+ std::string content = android::base::StringPrintf(
+ "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
+ ret, strerror(-ret));
+ EXPECT_TRUE(IsOk((ret == -ENOENT) ||
+ (ret == -ENOTCONN) ||
+ (ret == -ECONNREFUSED), content));
+ ret = __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts));
+ content = android::base::StringPrintf(
+ "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
+ ret, strerror(-ret));
+ EXPECT_TRUE(IsOk((ret == -ENOENT) ||
+ (ret == -ENOTCONN) ||
+ (ret == -ECONNREFUSED), content));
+ EXPECT_EQ(0, count_matching_ts(ts));
+
+ system("start logd");
+ usleep(1000000);
+
+ EXPECT_EQ(0, count_matching_ts(ts));
+
+ ts = log_time(CLOCK_MONOTONIC);
+ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ EXPECT_EQ(1, count_matching_ts(ts));
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+// Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
+
+TEST(liblog, __security) {
+#ifdef __ANDROID__
+ static const char persist_key[] = "persist.logd.security";
+ static const char readonly_key[] = "ro.device_owner";
+ // A silly default value that can never be in readonly_key so
+ // that it can be determined the property is not set.
+ static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
+ char persist[PROP_VALUE_MAX];
+ char readonly[PROP_VALUE_MAX];
+
+ property_get(persist_key, persist, "");
+ property_get(readonly_key, readonly, nothing_val);
+
+ if (!strcmp(readonly, nothing_val)) {
+ EXPECT_FALSE(__android_log_security());
+ fprintf(stderr, "Warning, setting ro.device_owner to a domain\n");
+ property_set(readonly_key, "com.google.android.SecOps.DeviceOwner");
+ } else if (!strcasecmp(readonly, "false") || !readonly[0]) {
+ EXPECT_FALSE(__android_log_security());
+ return;
+ }
+
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, "TRUE");
+ EXPECT_TRUE(__android_log_security());
+ property_set(persist_key, "FALSE");
+ EXPECT_FALSE(__android_log_security());
+ property_set(persist_key, "true");
+ EXPECT_TRUE(__android_log_security());
+ property_set(persist_key, "false");
+ EXPECT_FALSE(__android_log_security());
+ property_set(persist_key, "");
+ EXPECT_FALSE(__android_log_security());
+ property_set(persist_key, persist);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __security_buffer) {
+#ifdef __ANDROID__
+ struct logger_list *logger_list;
+ android_event_long_t buffer;
+
+ static const char persist_key[] = "persist.logd.security";
+ char persist[PROP_VALUE_MAX];
+ bool set_persist = false;
+ bool allow_security = false;
+
+ if (__android_log_security()) {
+ allow_security = true;
+ } else {
+ property_get(persist_key, persist, "");
+ if (strcasecmp(persist, "true")) {
+ property_set(persist_key, "TRUE");
+ if (__android_log_security()) {
+ allow_security = true;
+ set_persist = true;
+ } else {
+ property_set(persist_key, persist);
+ }
+ }
+ }
+
+ if (!allow_security) {
+ fprintf(stderr, "WARNING: "
+ "security buffer disabled, bypassing end-to-end test\n");
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+ // expect failure!
+ ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+
+ return;
+ }
+
+ /* Matches clientHasLogCredentials() in logd */
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+ bool clientHasLogCredentials = true;
+ if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)
+ && (gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+ uid_t euid = geteuid();
+ if ((euid != AID_SYSTEM) && (euid != AID_ROOT) && (euid != AID_LOG)) {
+ gid_t egid = getegid();
+ if ((egid != AID_SYSTEM) && (egid != AID_ROOT) && (egid != AID_LOG)) {
+ int num_groups = getgroups(0, NULL);
+ if (num_groups > 0) {
+ gid_t groups[num_groups];
+ num_groups = getgroups(num_groups, groups);
+ while (num_groups > 0) {
+ if (groups[num_groups - 1] == AID_LOG) {
+ break;
+ }
+ --num_groups;
+ }
+ }
+ if (num_groups <= 0) {
+ clientHasLogCredentials = false;
+ }
+ }
+ }
+ }
+ if (!clientHasLogCredentials) {
+ fprintf(stderr, "WARNING: "
+ "not in system context, bypassing end-to-end test\n");
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+ // expect failure!
+ ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+
+ return;
+ }
+
+ EXPECT_EQ(0, setuid(AID_SYSTEM)); // only one that can read security buffer
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_SECURITY, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+ 1000, pid)));
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+ ASSERT_LT(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != sizeof(android_log_event_long_t))
+ || (log_msg.id() != LOG_ID_SECURITY)) {
+ continue;
+ }
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+
+ if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+ continue;
+ }
+
+ log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ if (set_persist) {
+ property_set(persist_key, persist);
+ }
+
+ android_logger_list_close(logger_list);
+
+ bool clientHasSecurityCredentials = (uid == AID_SYSTEM) || (gid == AID_SYSTEM);
+ if (!clientHasSecurityCredentials) {
+ fprintf(stderr, "WARNING: "
+ "not system, content submitted but can not check end-to-end\n");
+ }
+ EXPECT_EQ(clientHasSecurityCredentials ? 1 : 0, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+static void android_errorWriteWithInfoLog_helper(int TAG, const char* SUBTAG,
+ int UID, const char* payload,
+ int DATA_LEN, int& count) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ int retval_android_errorWriteWithinInfoLog = android_errorWriteWithInfoLog(
+ TAG, SUBTAG, UID, payload, DATA_LEN);
+ if (payload) {
+ ASSERT_LT(0, retval_android_errorWriteWithinInfoLog);
+ } else {
+ ASSERT_GT(0, retval_android_errorWriteWithinInfoLog);
+ }
+
+ sleep(2);
+
+ count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ char *eventData = log_msg.msg();
+ if (!eventData) {
+ continue;
+ }
+
+ char *original = eventData;
+
+ // Tag
+ int tag = get4LE(eventData);
+ eventData += 4;
+
+ if (tag != TAG) {
+ continue;
+ }
+
+ if (!payload) {
+ // This tag should not have been written because the data was null
+ ++count;
+ break;
+ }
+
+ // List type
+ ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+ eventData++;
+
+ // Number of elements in list
+ ASSERT_EQ(3, eventData[0]);
+ eventData++;
+
+ // Element #1: string type for subtag
+ ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+ eventData++;
+
+ int subtag_len = strlen(SUBTAG);
+ if (subtag_len > 32) subtag_len = 32;
+ ASSERT_EQ(subtag_len, get4LE(eventData));
+ eventData += 4;
+
+ if (memcmp(SUBTAG, eventData, subtag_len)) {
+ continue;
+ }
+ eventData += subtag_len;
+
+ // Element #2: int type for uid
+ ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
+ eventData++;
+
+ ASSERT_EQ(UID, get4LE(eventData));
+ eventData += 4;
+
+ // Element #3: string type for data
+ ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+ eventData++;
+
+ size_t dataLen = get4LE(eventData);
+ eventData += 4;
+ if (DATA_LEN < 512) ASSERT_EQ(DATA_LEN, (int)dataLen);
+
+ if (memcmp(payload, eventData, dataLen)) {
+ continue;
+ }
+
+ if (DATA_LEN >= 512) {
+ eventData += dataLen;
+ // 4 bytes for the tag, and max_payload_buf should be truncated.
+ ASSERT_LE(4 + 512, eventData - original); // worst expectations
+ ASSERT_GT(4 + DATA_LEN, eventData - original); // must be truncated
+ }
+
+ ++count;
+ }
+
+ android_logger_list_close(logger_list);
+}
+#endif
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
+#ifdef __ANDROID__
+ int count;
+ android_errorWriteWithInfoLog_helper(
+ 123456781,
+ "test-subtag",
+ -1,
+ max_payload_buf,
+ 200,
+ count);
+ EXPECT_EQ(1, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
+#ifdef __ANDROID__
+ int count;
+ android_errorWriteWithInfoLog_helper(
+ 123456782,
+ "test-subtag",
+ -1,
+ max_payload_buf,
+ sizeof(max_payload_buf),
+ count);
+ EXPECT_EQ(1, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
+#ifdef __ANDROID__
+ int count;
+ android_errorWriteWithInfoLog_helper(
+ 123456783,
+ "test-subtag",
+ -1,
+ NULL,
+ 200,
+ count);
+ EXPECT_EQ(0, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
+#ifdef __ANDROID__
+ int count;
+ android_errorWriteWithInfoLog_helper(
+ 123456784,
+ "abcdefghijklmnopqrstuvwxyz now i know my abc",
+ -1,
+ max_payload_buf,
+ 200,
+ count);
+ EXPECT_EQ(1, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_bswrite_and_print___max) {
+ bswrite_test(max_payload_buf);
+}
+
+TEST(liblog, __android_log_buf_write_and_print__max) {
+ buf_write_test(max_payload_buf);
+}
+
+#ifdef __ANDROID__
+static void android_errorWriteLog_helper(int TAG, const char *SUBTAG, int& count) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ int retval_android_errorWriteLog = android_errorWriteLog(TAG, SUBTAG);
+ if (SUBTAG) {
+ ASSERT_LT(0, retval_android_errorWriteLog);
+ } else {
+ ASSERT_GT(0, retval_android_errorWriteLog);
+ }
+
+ sleep(2);
+
+ count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ char *eventData = log_msg.msg();
+ if (!eventData) {
+ continue;
+ }
+
+ // Tag
+ int tag = get4LE(eventData);
+ eventData += 4;
+
+ if (tag != TAG) {
+ continue;
+ }
+
+ if (!SUBTAG) {
+ // This tag should not have been written because the data was null
+ ++count;
+ break;
+ }
+
+ // List type
+ ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+ eventData++;
+
+ // Number of elements in list
+ ASSERT_EQ(3, eventData[0]);
+ eventData++;
+
+ // Element #1: string type for subtag
+ ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+ eventData++;
+
+ ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
+ eventData +=4;
+
+ if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+ continue;
+ }
+ ++count;
+ }
+
+ android_logger_list_close(logger_list);
+}
+#endif
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
+#ifdef __ANDROID__
+ int count;
+ android_errorWriteLog_helper(123456785, "test-subtag", count);
+ EXPECT_EQ(1, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
+#ifdef __ANDROID__
+ int count;
+ android_errorWriteLog_helper(123456786, NULL, count);
+ EXPECT_EQ(0, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+static int is_real_element(int type) {
+ return ((type == EVENT_TYPE_INT) ||
+ (type == EVENT_TYPE_LONG) ||
+ (type == EVENT_TYPE_STRING) ||
+ (type == EVENT_TYPE_FLOAT));
+}
+
+int android_log_buffer_to_string(const char *msg, size_t len,
+ char *strOut, size_t strOutLen) {
+ android_log_context context = create_android_log_parser(msg, len);
+ android_log_list_element elem;
+ bool overflow = false;
+ /* Reserve 1 byte for null terminator. */
+ size_t origStrOutLen = strOutLen--;
+
+ if (!context) {
+ return -EBADF;
+ }
+
+ memset(&elem, 0, sizeof(elem));
+
+ size_t outCount;
+
+ do {
+ elem = android_log_read_next(context);
+ switch ((int)elem.type) {
+ case EVENT_TYPE_LIST:
+ if (strOutLen == 0) {
+ overflow = true;
+ } else {
+ *strOut++ = '[';
+ strOutLen--;
+ }
+ break;
+
+ case EVENT_TYPE_LIST_STOP:
+ if (strOutLen == 0) {
+ overflow = true;
+ } else {
+ *strOut++ = ']';
+ strOutLen--;
+ }
+ break;
+
+ case EVENT_TYPE_INT:
+ /*
+ * snprintf also requires room for the null terminator, which
+ * we don't care about but we have allocated enough room for
+ * that
+ */
+ outCount = snprintf(strOut, strOutLen + 1,
+ "%" PRId32, elem.data.int32);
+ if (outCount <= strOutLen) {
+ strOut += outCount;
+ strOutLen -= outCount;
+ } else {
+ overflow = true;
+ }
+ break;
+
+ case EVENT_TYPE_LONG:
+ /*
+ * snprintf also requires room for the null terminator, which
+ * we don't care about but we have allocated enough room for
+ * that
+ */
+ outCount = snprintf(strOut, strOutLen + 1,
+ "%" PRId64, elem.data.int64);
+ if (outCount <= strOutLen) {
+ strOut += outCount;
+ strOutLen -= outCount;
+ } else {
+ overflow = true;
+ }
+ break;
+
+ case EVENT_TYPE_FLOAT:
+ /*
+ * snprintf also requires room for the null terminator, which
+ * we don't care about but we have allocated enough room for
+ * that
+ */
+ outCount = snprintf(strOut, strOutLen + 1, "%f", elem.data.float32);
+ if (outCount <= strOutLen) {
+ strOut += outCount;
+ strOutLen -= outCount;
+ } else {
+ overflow = true;
+ }
+ break;
+
+ default:
+ elem.complete = true;
+ break;
+
+ case EVENT_TYPE_UNKNOWN:
+#if 0 // Ideal purity in the test, we want to complain about UNKNOWN showing up
+ if (elem.complete) {
+ break;
+ }
+#endif
+ elem.data.string = const_cast<char *>("<unknown>");
+ elem.len = strlen(elem.data.string);
+ /* FALLTHRU */
+ case EVENT_TYPE_STRING:
+ if (elem.len <= strOutLen) {
+ memcpy(strOut, elem.data.string, elem.len);
+ strOut += elem.len;
+ strOutLen -= elem.len;
+ } else if (strOutLen > 0) {
+ /* copy what we can */
+ memcpy(strOut, elem.data.string, strOutLen);
+ strOut += strOutLen;
+ strOutLen = 0;
+ overflow = true;
+ }
+ break;
+ }
+
+ if (elem.complete) {
+ break;
+ }
+ /* Determine whether to put a comma or not. */
+ if (!overflow && (is_real_element(elem.type) ||
+ (elem.type == EVENT_TYPE_LIST_STOP))) {
+ android_log_list_element next = android_log_peek_next(context);
+ if (!next.complete && (is_real_element(next.type) ||
+ (next.type == EVENT_TYPE_LIST))) {
+ if (strOutLen == 0) {
+ overflow = true;
+ } else {
+ *strOut++ = ',';
+ strOutLen--;
+ }
+ }
+ }
+ } while ((elem.type != EVENT_TYPE_UNKNOWN) && !overflow && !elem.complete);
+
+ android_log_destroy(&context);
+
+ if (overflow) {
+ if (strOutLen < origStrOutLen) {
+ /* leave an indicator */
+ *(strOut-1) = '!';
+ } else {
+ /* nothing was written at all */
+ *strOut++ = '!';
+ }
+ }
+ *strOut++ = '\0';
+
+ if ((elem.type == EVENT_TYPE_UNKNOWN) && !elem.complete) {
+ fprintf(stderr, "Binary log entry conversion failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *event_test_int32(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint32_t);
+
+ return "1076895760";
+}
+
+static const char *event_test_int64(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint64_t);
+
+ return "-9191740941672636400";
+}
+
+static const char *event_test_list_int64(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint64_t);
+
+ return "[-9191740941672636400]";
+}
+
+static const char *event_test_simple_automagic_list(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ // The convenience API where we allow a simple list to be
+ // created without explicit begin or end calls.
+ EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint64_t);
+
+ return "[1076895760,-9191740941672636400]";
+}
+
+static const char *event_test_list_empty(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t);
+
+ return "[]";
+}
+
+static const char *event_test_complex_nested_list(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+
+ EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+ EXPECT_LE(0, android_log_write_int32(ctx, 0x01020304));
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x0102030405060708));
+ EXPECT_LE(0, android_log_write_string8(ctx, "Hello World"));
+ EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+ EXPECT_LE(0, android_log_write_int32(ctx, 1));
+ EXPECT_LE(0, android_log_write_int32(ctx, 2));
+ EXPECT_LE(0, android_log_write_int32(ctx, 3));
+ EXPECT_LE(0, android_log_write_int32(ctx, 4));
+ EXPECT_LE(0, android_log_write_list_end(ctx)); // ]
+ EXPECT_LE(0, android_log_write_float32(ctx, 1.0102030405060708));
+ EXPECT_LE(0, android_log_write_list_end(ctx)); // ]
+
+ //
+ // This one checks for the automagic list creation because a list
+ // begin and end was missing for it! This is actually an <oops> corner
+ // case, and not the behavior we morally support. The automagic API is to
+ // allow for a simple case of a series of objects in a single list. e.g.
+ // int32,int32,int32,string -> [int32,int32,int32,string]
+ //
+ EXPECT_LE(0, android_log_write_string8(ctx, "dlroW olleH"));
+
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint64_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof("Hello World") - 1 +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ 4 * (sizeof(uint8_t) + sizeof(uint32_t)) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof("dlroW olleH") - 1;
+
+ return "[[16909060,72623859790382856,Hello World,[1,2,3,4],1.010203],dlroW olleH]";
+}
+
+static const char *event_test_7_level_prefix(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 1));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 2));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 3));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 4));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 5));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 6));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 7));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + 7 *
+ (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+
+ return "[[[[[[[1],2],3],4],5],6],7]";
+}
+
+static const char *event_test_7_level_suffix(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 1));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 2));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 3));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 4));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 5));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 6));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + 6 *
+ (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+
+ return "[1,[2,[3,[4,[5,[6]]]]]]";
+}
+
+static const char *event_test_android_log_error_write(uint32_t tag, size_t &expected_len) {
+ EXPECT_LE(0, __android_log_error_write(tag, "Hello World", 42, "dlroW olleH", 11));
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") - 1 +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("dlroW olleH") - 1;
+
+ return "[Hello World,42,dlroW olleH]";
+}
+
+static const char *event_test_android_log_error_write_null(uint32_t tag, size_t &expected_len) {
+ EXPECT_LE(0, __android_log_error_write(tag, "Hello World", 42, NULL, 0));
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") - 1 +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("") - 1;
+
+ return "[Hello World,42,]";
+}
+
+// make sure all user buffers are flushed
+static void print_barrier() {
+ std::cout.flush();
+ fflush(stdout);
+ std::cerr.flush();
+ fflush(stderr); // everything else is paranoia ...
+}
+
+static void create_android_logger(const char *(*fn)(uint32_t tag, size_t &expected_len)) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ log_time ts(android_log_clockid());
+
+ size_t expected_len;
+ const char *expected_string = (*fn)(1005, expected_len);
+
+ if (!expected_string) {
+ android_logger_list_close(logger_list);
+ return;
+ }
+
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || ((size_t)log_msg.entry.len != expected_len)
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ ++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ char msgBuf[1024];
+ int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
+ &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+ EXPECT_EQ(0, processBinaryLogBuffer);
+ if (processBinaryLogBuffer == 0) {
+ print_barrier();
+ int printLogLine = android_log_printLogLine(
+ logformat, fileno(stderr), &entry);
+ print_barrier();
+ EXPECT_EQ(20 + (int)strlen(expected_string), printLogLine);
+ }
+ android_log_format_free(logformat);
+
+ // test buffer reading API
+ int buffer_to_string = -1;
+ if (eventData) {
+ snprintf(msgBuf, sizeof(msgBuf), "I/[%d]", get4LE(eventData));
+ print_barrier();
+ fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
+ memset(msgBuf, 0, sizeof(msgBuf));
+ buffer_to_string = android_log_buffer_to_string(
+ eventData + sizeof(uint32_t),
+ log_msg.entry.len - sizeof(uint32_t),
+ msgBuf, sizeof(msgBuf));
+ fprintf(stderr, "%s\n", msgBuf);
+ print_barrier();
+ }
+ EXPECT_EQ(0, buffer_to_string);
+ EXPECT_EQ(strlen(expected_string), strlen(msgBuf));
+ EXPECT_EQ(0, strcmp(expected_string, msgBuf));
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+#endif
+
+TEST(liblog, create_android_logger_int32) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_int32);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_int64) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_int64);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_list_int64) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_list_int64);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_simple_automagic_list) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_simple_automagic_list);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_list_empty) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_list_empty);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_complex_nested_list) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_complex_nested_list);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_7_level_prefix) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_7_level_prefix);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_7_level_suffix) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_7_level_suffix);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_android_log_error_write) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_android_log_error_write);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_android_log_error_write_null) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_android_log_error_write_null);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_overflow) {
+#ifdef __ANDROID__
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(1005)));
+ if (ctx) {
+ for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ }
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ /* One more for good measure, must be permanently unhappy */
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+ }
+
+ ASSERT_TRUE(NULL != (ctx = create_android_logger(1005)));
+ for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, i));
+ }
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ /* One more for good measure, must be permanently unhappy */
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ ASSERT_TRUE(NULL == ctx);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_log_write_list_buffer) {
+#ifdef __ANDROID__
+ __android_log_event_list ctx(1005);
+ ctx << 1005 << "tag_def" << "(tag|1),(name|3),(format|3)";
+ std::string buffer(ctx);
+ ctx.close();
+
+ char msgBuf[1024];
+ memset(msgBuf, 0, sizeof(msgBuf));
+ EXPECT_EQ(android_log_buffer_to_string(buffer.data(), buffer.length(),
+ msgBuf, sizeof(msgBuf)), 0);
+ EXPECT_STREQ(msgBuf, "[1005,tag_def,(tag|1),(name|3),(format|3)]");
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+static const char __pmsg_file[] =
+ "/data/william-shakespeare/MuchAdoAboutNothing.txt";
+#endif
+
+TEST(liblog, __android_log_pmsg_file_write) {
+#ifdef __ANDROID__
+ __android_log_close();
+ if (getuid() == AID_ROOT) {
+ tested__android_log_close = true;
+ bool pmsgActiveAfter__android_log_close = isPmsgActive();
+ bool logdwActiveAfter__android_log_close = isLogdwActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+ EXPECT_FALSE(logdwActiveAfter__android_log_close);
+ } else if (!tested__android_log_close) {
+ fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+ }
+ int return__android_log_pmsg_file_write = __android_log_pmsg_file_write(
+ LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
+ __pmsg_file, max_payload_buf, sizeof(max_payload_buf));
+ EXPECT_LT(0, return__android_log_pmsg_file_write);
+ if (return__android_log_pmsg_file_write == -ENOMEM) {
+ fprintf(stderr,
+ "Kernel does not have space allocated to pmsg pstore driver configured\n"
+ );
+ } else if (!return__android_log_pmsg_file_write) {
+ fprintf(stderr, "Reboot, ensure file %s matches\n"
+ "with liblog.__android_log_msg_file_read test\n",
+ __pmsg_file);
+ }
+ bool pmsgActiveAfter__android_pmsg_file_write;
+ bool logdwActiveAfter__android_pmsg_file_write;
+ if (getuid() == AID_ROOT) {
+ pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
+ logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_pmsg_file_write);
+ EXPECT_FALSE(logdwActiveAfter__android_pmsg_file_write);
+ }
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_pmsg_file_write",
+ "main"));
+ if (getuid() == AID_ROOT) {
+ bool pmsgActiveAfter__android_log_buf_print = isPmsgActive();
+ bool logdwActiveAfter__android_log_buf_print = isLogdwActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_buf_print);
+ EXPECT_TRUE(logdwActiveAfter__android_log_buf_print);
+ }
+ EXPECT_LT(0, __android_log_pmsg_file_write(
+ LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
+ __pmsg_file, max_payload_buf, sizeof(max_payload_buf)));
+ if (getuid() == AID_ROOT) {
+ pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
+ logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_pmsg_file_write);
+ EXPECT_TRUE(logdwActiveAfter__android_pmsg_file_write);
+ }
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename,
+ const char *buf, size_t len, void *arg) {
+ EXPECT_TRUE(NULL == arg);
+ EXPECT_EQ(LOG_ID_CRASH, logId);
+ EXPECT_EQ(ANDROID_LOG_VERBOSE, prio);
+ EXPECT_FALSE(NULL == strstr(__pmsg_file, filename));
+ EXPECT_EQ(len, sizeof(max_payload_buf));
+ EXPECT_EQ(0, strcmp(max_payload_buf, buf));
+
+ ++signaled;
+ if ((len != sizeof(max_payload_buf)) ||
+ strcmp(max_payload_buf, buf)) {
+ fprintf(stderr, "comparison fails on content \"%s\"\n", buf);
+ }
+ return arg ||
+ (LOG_ID_CRASH != logId) ||
+ (ANDROID_LOG_VERBOSE != prio) ||
+ !strstr(__pmsg_file, filename) ||
+ (len != sizeof(max_payload_buf)) ||
+ !!strcmp(max_payload_buf, buf) ? -ENOEXEC : 1;
+}
+#endif
+
+TEST(liblog, __android_log_pmsg_file_read) {
+#ifdef __ANDROID__
+ signaled = 0;
+
+ __android_log_close();
+ if (getuid() == AID_ROOT) {
+ tested__android_log_close = true;
+ bool pmsgActiveAfter__android_log_close = isPmsgActive();
+ bool logdwActiveAfter__android_log_close = isLogdwActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+ EXPECT_FALSE(logdwActiveAfter__android_log_close);
+ } else if (!tested__android_log_close) {
+ fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+ }
+
+ ssize_t ret = __android_log_pmsg_file_read(
+ LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
+ __pmsg_file, __pmsg_fn, NULL);
+
+ if (getuid() == AID_ROOT) {
+ bool pmsgActiveAfter__android_log_pmsg_file_read = isPmsgActive();
+ bool logdwActiveAfter__android_log_pmsg_file_read = isLogdwActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_pmsg_file_read);
+ EXPECT_FALSE(logdwActiveAfter__android_log_pmsg_file_read);
+ }
+
+ if (ret == -ENOENT) {
+ fprintf(stderr,
+ "No pre-boot results of liblog.__android_log_mesg_file_write to "
+ "compare with,\n"
+ "false positive test result.\n");
+ return;
+ }
+
+ EXPECT_LT(0, ret);
+ EXPECT_EQ(1U, signaled);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+// must be: '<needle:> 0 kB'
+static bool isZero(const std::string &content, std::string::size_type pos,
+ const char* needle) {
+ std::string::size_type offset = content.find(needle, pos);
+ return (offset != std::string::npos) &&
+ ((offset = content.find_first_not_of(" \t", offset +
+ strlen(needle))) != std::string::npos) &&
+ (content.find_first_not_of("0", offset) != offset);
+}
+
+// must not be: '<needle:> 0 kB'
+static bool isNotZero(const std::string &content, std::string::size_type pos,
+ const char* needle) {
+ std::string::size_type offset = content.find(needle, pos);
+ return (offset != std::string::npos) &&
+ ((offset = content.find_first_not_of(" \t", offset +
+ strlen(needle))) != std::string::npos) &&
+ (content.find_first_not_of("123456789", offset) != offset);
+}
+
+static void event_log_tags_test_smap(pid_t pid) {
+ std::string filename = android::base::StringPrintf("/proc/%d/smaps", pid);
+
+ std::string content;
+ if (!android::base::ReadFileToString(filename, &content)) return;
+
+ bool shared_ok = false;
+ bool private_ok = false;
+ bool anonymous_ok = false;
+ bool pass_ok = false;
+
+ static const char event_log_tags[] = "event-log-tags";
+ std::string::size_type pos = 0;
+ while ((pos = content.find(event_log_tags, pos)) != std::string::npos) {
+ pos += strlen(event_log_tags);
+
+ // must not be: 'Shared_Clean: 0 kB'
+ bool ok = isNotZero(content, pos, "Shared_Clean:") ||
+ // If not /etc/event-log-tags, thus r/w, then half points
+ // back for not 'Shared_Dirty: 0 kB'
+ ((content.substr(pos - 5 - strlen(event_log_tags), 5) != "/etc/") &&
+ isNotZero(content, pos, "Shared_Dirty:"));
+ if (ok && !pass_ok) {
+ shared_ok = true;
+ } else if (!ok) {
+ shared_ok = false;
+ }
+
+ // must be: 'Private_Dirty: 0 kB' and 'Private_Clean: 0 kB'
+ ok = isZero(content, pos, "Private_Dirty:") ||
+ isZero(content, pos, "Private_Clean:");
+ if (ok && !pass_ok) {
+ private_ok = true;
+ } else if (!ok) {
+ private_ok = false;
+ }
+
+ // must be: 'Anonymous: 0 kB'
+ ok = isZero(content, pos, "Anonymous:");
+ if (ok && !pass_ok) {
+ anonymous_ok = true;
+ } else if (!ok) {
+ anonymous_ok = false;
+ }
+
+ pass_ok = true;
+ }
+ content = "";
+
+ if (!pass_ok) return;
+ if (shared_ok && anonymous_ok && private_ok) return;
+
+ filename = android::base::StringPrintf("/proc/%d/comm", pid);
+ android::base::ReadFileToString(filename, &content);
+ content = android::base::StringPrintf("%d:%s",
+ pid, content.substr(0, content.find("\n")).c_str());
+
+ EXPECT_TRUE(IsOk(shared_ok, content));
+ EXPECT_TRUE(IsOk(private_ok, content));
+ EXPECT_TRUE(IsOk(anonymous_ok, content));
+}
+#endif
+
+TEST(liblog, event_log_tags) {
+#ifdef __ANDROID__
+ std::unique_ptr<DIR, int(*)(DIR*)> proc_dir(opendir("/proc"), closedir);
+ ASSERT_FALSE(!proc_dir);
+
+ dirent* e;
+ while ((e = readdir(proc_dir.get()))) {
+ if (e->d_type != DT_DIR) continue;
+ if (!isdigit(e->d_name[0])) continue;
+ long long id = atoll(e->d_name);
+ if (id <= 0) continue;
+ pid_t pid = id;
+ if (id != pid) continue;
+ event_log_tags_test_smap(pid);
+ }
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_ratelimit) {
+ time_t state = 0;
+
+ errno = 42;
+ // Prime
+ __android_log_ratelimit(3, &state);
+ EXPECT_EQ(errno, 42);
+ // Check
+ EXPECT_FALSE(__android_log_ratelimit(3, &state));
+ sleep(1);
+ EXPECT_FALSE(__android_log_ratelimit(3, &state));
+ sleep(4);
+ EXPECT_TRUE(__android_log_ratelimit(3, &state));
+ sleep(5);
+ EXPECT_TRUE(__android_log_ratelimit(3, &state));
+
+ // API checks
+ IF_ALOG_RATELIMIT_LOCAL(3, &state) {
+ EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT_LOCAL(3, &state)");
+ }
+
+ IF_ALOG_RATELIMIT() {
+ ;
+ } else {
+ EXPECT_TRUE(0 == "IF_ALOG_RATELIMIT()");
+ }
+ IF_ALOG_RATELIMIT() {
+ EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT()");
+ }
+ // Do not test default seconds, to allow liblog to tune freely
+}
+
+TEST(liblog, android_lookupEventTagNum) {
+#ifdef __ANDROID__
+ EventTagMap* map = android_openEventTagMap(NULL);
+ EXPECT_TRUE(NULL != map);
+ std::string Name = android::base::StringPrintf("a%d", getpid());
+ int tag = android_lookupEventTagNum(map, Name.c_str(), "(new|1)", ANDROID_LOG_UNKNOWN);
+ android_closeEventTagMap(map);
+ if (tag == -1) system("tail -3 /dev/event-log-tags >&2");
+ EXPECT_NE(-1, tag);
+ EXPECT_NE(0, tag);
+ EXPECT_GT(UINT32_MAX, (unsigned)tag);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
diff --git a/liblog/tests/log_id_test.cpp b/liblog/tests/log_id_test.cpp
new file mode 100644
index 0000000..3241534
--- /dev/null
+++ b/liblog/tests/log_id_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_id.h>
+
+// We do not want to include <android/log.h> to acquire ANDROID_LOG_INFO for
+// include file API purity. We do however want to allow the _option_ that
+// log/log_id.h could include this file, or related content, in the future.
+#ifndef __android_LogPriority_defined
+# define ANDROID_LOG_INFO 4
+#endif
+
+TEST(liblog, log_id) {
+#ifdef __ANDROID__
+ int count = 0;
+
+ for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ log_id_t id = static_cast<log_id_t>(i);
+ const char *name = android_log_id_to_name(id);
+ if (id != android_name_to_log_id(name)) {
+ continue;
+ }
+ ++count;
+ fprintf(stderr, "log buffer %s\r", name);
+ }
+ ASSERT_EQ(LOG_ID_MAX, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_buf_print) {
+#ifdef __ANDROID__
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_print",
+ "radio"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_print",
+ "system"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_print",
+ "main"));
+ usleep(1000);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_buf_write) {
+#ifdef __ANDROID__
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_write",
+ "radio"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_write",
+ "system"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_write",
+ "main"));
+ usleep(1000);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+static void* ConcurrentPrintFn(void *arg) {
+ int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_print", "Concurrent %" PRIuPTR,
+ reinterpret_cast<uintptr_t>(arg));
+ return reinterpret_cast<void*>(ret);
+}
+#endif
+
+#define NUM_CONCURRENT 64
+#define _concurrent_name(a,n) a##__concurrent##n
+#define concurrent_name(a,n) _concurrent_name(a,n)
+
+TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) {
+#ifdef __ANDROID__
+ pthread_t t[NUM_CONCURRENT];
+ int i;
+ for (i=0; i < NUM_CONCURRENT; i++) {
+ ASSERT_EQ(0, pthread_create(&t[i], NULL,
+ ConcurrentPrintFn,
+ reinterpret_cast<void *>(i)));
+ }
+ int ret = 0;
+ for (i=0; i < NUM_CONCURRENT; i++) {
+ void* result;
+ ASSERT_EQ(0, pthread_join(t[i], &result));
+ int this_result = reinterpret_cast<uintptr_t>(result);
+ if ((0 == ret) && (0 != this_result)) {
+ ret = this_result;
+ }
+ }
+ ASSERT_LT(0, ret);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/liblog/tests/log_radio_test.cpp b/liblog/tests/log_radio_test.cpp
new file mode 100644
index 0000000..591748a
--- /dev/null
+++ b/liblog/tests/log_radio_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_radio.h>
+
+TEST(liblog, RLOG) {
+#ifdef __ANDROID__
+ static const char content[] = "log_radio.h";
+ static const char content_false[] = "log_radio.h false";
+
+ // ratelimit content to 10/s to keep away from spam filters
+ // do not send identical content together to keep away from spam filters
+
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGV"
+ RLOGV(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGD"
+ RLOGD(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGI"
+ RLOGI(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGW"
+ RLOGW(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGE"
+ RLOGE(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGV"
+ RLOGV_IF(true, content);
+ usleep(100000);
+ RLOGV_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGD"
+ RLOGD_IF(true, content);
+ usleep(100000);
+ RLOGD_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGI"
+ RLOGI_IF(true, content);
+ usleep(100000);
+ RLOGI_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGW"
+ RLOGW_IF(true, content);
+ usleep(100000);
+ RLOGW_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGE"
+ RLOGE_IF(true, content);
+ usleep(100000);
+ RLOGE_IF(false, content_false);
+
+ // give time for content to long-path through logger
+ sleep(1);
+
+ std::string buf = android::base::StringPrintf(
+ "logcat -b radio --pid=%u -d -s"
+ " TEST__RLOGV TEST__RLOGD TEST__RLOGI TEST__RLOGW TEST__RLOGE",
+ (unsigned)getpid());
+ FILE* fp = popen(buf.c_str(), "r");
+ int count = 0;
+ int count_false = 0;
+ if (fp) {
+ if (!android::base::ReadFdToString(fileno(fp), &buf)) buf = "";
+ pclose(fp);
+ for (size_t pos = 0; (pos = buf.find(content, pos)) != std::string::npos; ++pos) {
+ ++count;
+ }
+ for (size_t pos = 0; (pos = buf.find(content_false, pos)) != std::string::npos; ++pos) {
+ ++count_false;
+ }
+ }
+ EXPECT_EQ(0, count_false);
+#if LOG_NDEBUG
+ ASSERT_EQ(8, count);
+#else
+ ASSERT_EQ(10, count);
+#endif
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
new file mode 100644
index 0000000..2e02407
--- /dev/null
+++ b/liblog/tests/log_read_test.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2013-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android/log.h> // minimal logging API
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_read.h>
+// Do not use anything in log/log_time.h despite side effects of the above.
+
+TEST(liblog, __android_log_write__android_logger_list_read) {
+#ifdef __ANDROID__
+ pid_t pid = getpid();
+
+ struct logger_list *logger_list;
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ std::string buf = android::base::StringPrintf("pid=%u ts=%ld.%09ld",
+ pid, ts.tv_sec, ts.tv_nsec);
+ static const char tag[] = "liblog.__android_log_write__android_logger_list_read";
+ static const char prio = ANDROID_LOG_DEBUG;
+ ASSERT_LT(0, __android_log_write(prio, tag, buf.c_str()));
+ usleep(1000000);
+
+ buf = std::string(&prio, sizeof(prio)) +
+ tag +
+ std::string("", 1) +
+ buf +
+ std::string("", 1);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+ EXPECT_EQ(log_msg.entry.pid, pid);
+ // There may be a future where we leak "liblog" tagged LOG_ID_EVENT
+ // binary messages through so that logger losses can be correlated?
+ EXPECT_EQ(log_msg.id(), LOG_ID_MAIN);
+
+ if (log_msg.entry.len != buf.length()) continue;
+
+ if (buf != std::string(log_msg.msg(), log_msg.entry.len)) continue;
+
+ ++count;
+ }
+ android_logger_list_close(logger_list);
+
+ EXPECT_EQ(1, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_logger_get_) {
+#ifdef __ANDROID__
+ // This test assumes the log buffers are filled with noise from
+ // normal operations. It will fail if done immediately after a
+ // logcat -c.
+ struct logger_list * logger_list = android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0);
+
+ for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ log_id_t id = static_cast<log_id_t>(i);
+ const char *name = android_log_id_to_name(id);
+ if (id != android_name_to_log_id(name)) {
+ continue;
+ }
+ fprintf(stderr, "log buffer %s\r", name);
+ struct logger * logger;
+ EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
+ EXPECT_EQ(id, android_logger_get_id(logger));
+ ssize_t get_log_size = android_logger_get_log_size(logger);
+ /* security buffer is allowed to be denied */
+ if (strcmp("security", name)) {
+ EXPECT_LT(0, get_log_size);
+ /* crash buffer is allowed to be empty, that is actually healthy! */
+ EXPECT_LE((strcmp("crash", name)) != 0,
+ android_logger_get_log_readable_size(logger));
+ } else {
+ EXPECT_NE(0, get_log_size);
+ if (get_log_size < 0) {
+ EXPECT_GT(0, android_logger_get_log_readable_size(logger));
+ } else {
+ EXPECT_LE(0, android_logger_get_log_readable_size(logger));
+ }
+ }
+ EXPECT_LT(0, android_logger_get_log_version(logger));
+ }
+
+ android_logger_list_close(logger_list);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
diff --git a/liblog/tests/log_system_test.cpp b/liblog/tests/log_system_test.cpp
new file mode 100644
index 0000000..b62832e
--- /dev/null
+++ b/liblog/tests/log_system_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_system.h>
+
+TEST(liblog, SLOG) {
+#ifdef __ANDROID__
+ static const char content[] = "log_system.h";
+ static const char content_false[] = "log_system.h false";
+
+ // ratelimit content to 10/s to keep away from spam filters
+ // do not send identical content together to keep away from spam filters
+
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGV"
+ SLOGV(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGD"
+ SLOGD(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGI"
+ SLOGI(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGW"
+ SLOGW(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGE"
+ SLOGE(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGV"
+ SLOGV_IF(true, content);
+ usleep(100000);
+ SLOGV_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGD"
+ SLOGD_IF(true, content);
+ usleep(100000);
+ SLOGD_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGI"
+ SLOGI_IF(true, content);
+ usleep(100000);
+ SLOGI_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGW"
+ SLOGW_IF(true, content);
+ usleep(100000);
+ SLOGW_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGE"
+ SLOGE_IF(true, content);
+ usleep(100000);
+ SLOGE_IF(false, content_false);
+
+ // give time for content to long-path through logger
+ sleep(1);
+
+ std::string buf = android::base::StringPrintf(
+ "logcat -b system --pid=%u -d -s"
+ " TEST__SLOGV TEST__SLOGD TEST__SLOGI TEST__SLOGW TEST__SLOGE",
+ (unsigned)getpid());
+ FILE* fp = popen(buf.c_str(), "r");
+ int count = 0;
+ int count_false = 0;
+ if (fp) {
+ if (!android::base::ReadFdToString(fileno(fp), &buf)) buf = "";
+ pclose(fp);
+ for (size_t pos = 0; (pos = buf.find(content, pos)) != std::string::npos; ++pos) {
+ ++count;
+ }
+ for (size_t pos = 0; (pos = buf.find(content_false, pos)) != std::string::npos; ++pos) {
+ ++count_false;
+ }
+ }
+ EXPECT_EQ(0, count_false);
+#if LOG_NDEBUG
+ ASSERT_EQ(8, count);
+#else
+ ASSERT_EQ(10, count);
+#endif
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/liblog/tests/log_time_test.cpp b/liblog/tests/log_time_test.cpp
new file mode 100644
index 0000000..f2601b6
--- /dev/null
+++ b/liblog/tests/log_time_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <time.h>
+
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_time.h>
+
+TEST(liblog, log_time) {
+#ifdef __ANDROID__
+
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+ log_time(CLOCK_MONOTONIC);
+
+ EXPECT_EQ(log_time, log_time::EPOCH);
+#endif
+
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ log_time tl(ts);
+
+ EXPECT_EQ(tl, ts);
+ EXPECT_GE(tl, ts);
+ EXPECT_LE(tl, ts);
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/liblog/uio.c b/liblog/uio.c
index f77cc49..ac0558f 100644
--- a/liblog/uio.c
+++ b/liblog/uio.c
@@ -16,17 +16,20 @@
#if defined(_WIN32)
-#include <log/uio.h>
#include <unistd.h>
-int readv( int fd, struct iovec* vecs, int count )
+#include <log/uio.h>
+
+#include "log_portability.h"
+
+LIBLOG_ABI_PUBLIC int readv(int fd, struct iovec *vecs, int count)
{
int total = 0;
for ( ; count > 0; count--, vecs++ ) {
char* buf = vecs->iov_base;
int len = vecs->iov_len;
-
+
while (len > 0) {
int ret = read( fd, buf, len );
if (ret < 0) {
@@ -46,14 +49,14 @@
return total;
}
-int writev( int fd, const struct iovec* vecs, int count )
+LIBLOG_ABI_PUBLIC int writev(int fd, const struct iovec *vecs, int count)
{
int total = 0;
for ( ; count > 0; count--, vecs++ ) {
const char* buf = vecs->iov_base;
int len = vecs->iov_len;
-
+
while (len > 0) {
int ret = write( fd, buf, len );
if (ret < 0) {
@@ -69,7 +72,7 @@
len -= ret;
}
}
-Exit:
+Exit:
return total;
}
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
new file mode 100644
index 0000000..98413dd
--- /dev/null
+++ b/libmemtrack/Android.bp
@@ -0,0 +1,30 @@
+// Copyright 2013 The Android Open Source Project
+
+cc_library_shared {
+ name: "libmemtrack",
+ srcs: ["memtrack.c"],
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+ include_dirs: ["hardware/libhardware/include"],
+ shared_libs: [
+ "libhardware",
+ "liblog",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_binary {
+ name: "memtrack_test",
+ srcs: ["memtrack_test.c"],
+ shared_libs: [
+ "libmemtrack",
+ "libpagemap",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
deleted file mode 100644
index a8fb3eb..0000000
--- a/libmemtrack/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := memtrack.c
-LOCAL_MODULE := libmemtrack
-LOCAL_C_INCLUDES += hardware/libhardware/include
-LOCAL_SHARED_LIBRARIES := libhardware liblog
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := memtrack_test.c
-LOCAL_MODULE := memtrack_test
-LOCAL_C_INCLUDES := $(call include-path-for, libpagemap)
-LOCAL_SHARED_LIBRARIES := libmemtrack libpagemap
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_EXECUTABLE)
diff --git a/libmemtrack/include/memtrack/memtrack.h b/libmemtrack/include/memtrack/memtrack.h
new file mode 100644
index 0000000..8c0ab89
--- /dev/null
+++ b/libmemtrack/include/memtrack/memtrack.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBMEMTRACK_MEMTRACK_H_
+#define _LIBMEMTRACK_MEMTRACK_H_
+
+#include <sys/types.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * struct memtrack_proc
+ *
+ * an opaque handle to the memory stats on a process.
+ * Created with memtrack_proc_new, destroyed by
+ * memtrack_proc_destroy. Can be reused multiple times with
+ * memtrack_proc_get.
+ */
+struct memtrack_proc;
+
+/**
+ * memtrack_init
+ *
+ * Must be called once before calling any other functions. After this function
+ * is called, everything else is thread-safe.
+ *
+ * Returns 0 on success, -errno on error.
+ */
+int memtrack_init(void);
+
+/**
+ * memtrack_proc_new
+ *
+ * Return a new handle to hold process memory stats.
+ *
+ * Returns NULL on error.
+ */
+struct memtrack_proc *memtrack_proc_new(void);
+
+/**
+ * memtrack_proc_destroy
+ *
+ * Free all memory associated with a process memory stats handle.
+ */
+void memtrack_proc_destroy(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_get
+ *
+ * Fill a process memory stats handle with data about the given pid. Can be
+ * called on a handle that was just allocated with memtrack_proc_new,
+ * or on a handle that has been previously passed to memtrack_proc_get
+ * to replace the data with new data on the same or another process. It is
+ * expected that the second call on the same handle should not require
+ * allocating any new memory.
+ *
+ * Returns 0 on success, -errno on error.
+ */
+int memtrack_proc_get(struct memtrack_proc *p, pid_t pid);
+
+/**
+ * memtrack_proc_graphics_total
+ *
+ * Return total amount of memory that has been allocated for use as window
+ * buffers. Does not differentiate between memory that has already been
+ * accounted for by reading /proc/pid/smaps and memory that has not been
+ * accounted for.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_graphics_pss
+ *
+ * Return total amount of memory that has been allocated for use as window
+ * buffers, but has not already been accounted for by reading /proc/pid/smaps.
+ * Memory that is shared across processes may already be divided by the
+ * number of processes that share it (preferred), or may be charged in full to
+ * every process that shares it, depending on the capabilities of the driver.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_gl_total
+ *
+ * Same as memtrack_proc_graphics_total, but counts GL memory (which
+ * should not overlap with graphics memory) instead of graphics memory.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_gl_total(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_gl_pss
+ *
+ * Same as memtrack_proc_graphics_total, but counts GL memory (which
+ * should not overlap with graphics memory) instead of graphics memory.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_other_total
+ *
+ * Same as memtrack_proc_graphics_total, but counts miscellaneous memory
+ * not tracked by gl or graphics calls above.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_other_total(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_other_pss
+ *
+ * Same as memtrack_proc_graphics_total, but counts miscellaneous memory
+ * not tracked by gl or graphics calls above.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_other_pss(struct memtrack_proc *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c
index 21d9ebd..9ed9451 100644
--- a/libmemtrack/memtrack.c
+++ b/libmemtrack/memtrack.c
@@ -14,19 +14,18 @@
* limitations under the License.
*/
-#include <memtrack/memtrack.h>
-
#define LOG_TAG "memtrack"
-#include <log/log.h>
+#include <memtrack/memtrack.h>
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include <hardware/memtrack.h>
+#include <log/log.h>
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
static const memtrack_module_t *module;
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
new file mode 100644
index 0000000..6fe67a4
--- /dev/null
+++ b/libmemunreachable/Allocator.cpp
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Header page:
+//
+// For minimum allocation size (8 bytes), bitmap can store used allocations for
+// up to 4032*8*8=258048, which is 256KiB minus the header page
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+#include "anon_vma_naming.h"
+#include "Allocator.h"
+#include "LinkedList.h"
+
+// runtime interfaces used:
+// abort
+// assert - fprintf + mmap
+// mmap
+// munmap
+// prctl
+
+constexpr size_t const_log2(size_t n, size_t p = 0) {
+ return (n <= 1) ? p : const_log2(n / 2, p + 1);
+}
+
+constexpr unsigned int div_round_up(unsigned int x, unsigned int y) {
+ return (x + y - 1) / y;
+}
+
+static constexpr size_t kPageSize = 4096;
+static constexpr size_t kChunkSize = 256 * 1024;
+static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize;
+static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4;
+static constexpr size_t kMinBucketAllocationSize = 8;
+static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize)
+ - const_log2(kMinBucketAllocationSize) + 1;
+static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize
+ / kPageSize;
+
+std::atomic<int> heap_count;
+
+class Chunk;
+
+class HeapImpl {
+ public:
+ HeapImpl();
+ ~HeapImpl();
+ void* operator new(std::size_t count) noexcept;
+ void operator delete(void* ptr);
+
+ void* Alloc(size_t size);
+ void Free(void* ptr);
+ bool Empty();
+
+ void MoveToFullList(Chunk* chunk, int bucket_);
+ void MoveToFreeList(Chunk* chunk, int bucket_);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HeapImpl);
+
+ LinkedList<Chunk*> free_chunks_[kNumBuckets];
+ LinkedList<Chunk*> full_chunks_[kNumBuckets];
+
+ void MoveToList(Chunk* chunk, LinkedList<Chunk*>* head);
+ void* MapAlloc(size_t size);
+ void MapFree(void* ptr);
+ void* AllocLocked(size_t size);
+ void FreeLocked(void* ptr);
+
+ struct MapAllocation {
+ void *ptr;
+ size_t size;
+ MapAllocation* next;
+ };
+ MapAllocation* map_allocation_list_;
+ std::mutex m_;
+};
+
+// Integer log 2, rounds down
+static inline unsigned int log2(size_t n) {
+ return 8 * sizeof(unsigned long long) - __builtin_clzll(n) - 1;
+}
+
+static inline unsigned int size_to_bucket(size_t size) {
+ if (size < kMinBucketAllocationSize)
+ return kMinBucketAllocationSize;
+ return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize);
+}
+
+static inline size_t bucket_to_size(unsigned int bucket) {
+ return kMinBucketAllocationSize << bucket;
+}
+
+static void* MapAligned(size_t size, size_t align) {
+ const int prot = PROT_READ | PROT_WRITE;
+ const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+
+ size = (size + kPageSize - 1) & ~(kPageSize - 1);
+
+ // Over-allocate enough to align
+ size_t map_size = size + align - kPageSize;
+ if (map_size < size) {
+ return nullptr;
+ }
+
+ void* ptr = mmap(NULL, map_size, prot, flags, -1, 0);
+ if (ptr == MAP_FAILED) {
+ return nullptr;
+ }
+
+ size_t aligned_size = map_size;
+ void* aligned_ptr = ptr;
+
+ std::align(align, size, aligned_ptr, aligned_size);
+
+ // Trim beginning
+ if (aligned_ptr != ptr) {
+ ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr)
+ - reinterpret_cast<uintptr_t>(ptr);
+ munmap(ptr, extra);
+ map_size -= extra;
+ ptr = aligned_ptr;
+ }
+
+ // Trim end
+ if (map_size != size) {
+ assert(map_size > size);
+ assert(ptr != NULL);
+ munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size),
+ map_size - size);
+ }
+
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
+ reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc");
+
+ return ptr;
+}
+
+class Chunk {
+ public:
+ static void* operator new(std::size_t count) noexcept;
+ static void operator delete(void* ptr);
+ Chunk(HeapImpl* heap, int bucket);
+ ~Chunk() {}
+
+ void *Alloc();
+ void Free(void* ptr);
+ void Purge();
+ bool Empty();
+
+ static Chunk* ptr_to_chunk(void* ptr) {
+ return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr)
+ & ~(kChunkSize - 1));
+ }
+ static bool is_chunk(void* ptr) {
+ return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0;
+ }
+
+ unsigned int free_count() {
+ return free_count_;
+ }
+ HeapImpl* heap() {
+ return heap_;
+ }
+ LinkedList<Chunk*> node_; // linked list sorted by minimum free count
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Chunk);
+ HeapImpl* heap_;
+ unsigned int bucket_;
+ unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
+ unsigned int max_allocations_; // maximum number of allocations in the chunk
+ unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
+ unsigned int free_count_; // number of available allocations
+ unsigned int frees_since_purge_; // number of calls to Free since last Purge
+
+ // bitmap of pages that have been dirtied
+ uint32_t dirty_pages_[div_round_up(kUsablePagesPerChunk, 32)];
+
+ // bitmap of free allocations.
+ uint32_t free_bitmap_[kUsableChunkSize / kMinBucketAllocationSize / 32];
+
+ char data_[0];
+
+ unsigned int ptr_to_n(void* ptr) {
+ ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr)
+ - reinterpret_cast<uintptr_t>(data_);
+ return offset / allocation_size_;
+ }
+ void* n_to_ptr(unsigned int n) {
+ return data_ + n * allocation_size_;
+ }
+};
+static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page");
+
+// Override new operator on chunk to use mmap to allocate kChunkSize
+void* Chunk::operator new(std::size_t count __attribute__((unused))) noexcept {
+ assert(count == sizeof(Chunk));
+ void* mem = MapAligned(kChunkSize, kChunkSize);
+ if (!mem) {
+ abort(); //throw std::bad_alloc;
+ }
+
+ return mem;
+}
+
+// Override new operator on chunk to use mmap to allocate kChunkSize
+void Chunk::operator delete(void *ptr) {
+ assert(reinterpret_cast<Chunk*>(ptr) == ptr_to_chunk(ptr));
+ munmap(ptr, kChunkSize);
+}
+
+Chunk::Chunk(HeapImpl* heap, int bucket) :
+ node_(this), heap_(heap), bucket_(bucket), allocation_size_(
+ bucket_to_size(bucket)), max_allocations_(
+ kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(
+ max_allocations_), frees_since_purge_(0) {
+ memset(dirty_pages_, 0, sizeof(dirty_pages_));
+ memset(free_bitmap_, 0xff, sizeof(free_bitmap_));
+}
+
+bool Chunk::Empty() {
+ return free_count_ == max_allocations_;
+}
+
+void* Chunk::Alloc() {
+ assert(free_count_ > 0);
+
+ unsigned int i = first_free_bitmap_;
+ while (free_bitmap_[i] == 0)
+ i++;
+ assert(i < arraysize(free_bitmap_));
+ unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1;
+ assert(free_bitmap_[i] & (1U << bit));
+ free_bitmap_[i] &= ~(1U << bit);
+ unsigned int n = i * 32 + bit;
+ assert(n < max_allocations_);
+
+ unsigned int page = n * allocation_size_ / kPageSize;
+ assert(page / 32 < arraysize(dirty_pages_));
+ dirty_pages_[page / 32] |= 1U << (page % 32);
+
+ free_count_--;
+ if (free_count_ == 0) {
+ heap_->MoveToFullList(this, bucket_);
+ }
+
+ return n_to_ptr(n);
+}
+
+void Chunk::Free(void* ptr) {
+ assert(is_chunk(ptr));
+ assert(ptr_to_chunk(ptr) == this);
+
+ unsigned int n = ptr_to_n(ptr);
+ unsigned int i = n / 32;
+ unsigned int bit = n % 32;
+
+ assert(i < arraysize(free_bitmap_));
+ assert(!(free_bitmap_[i] & (1U << bit)));
+ free_bitmap_[i] |= 1U << bit;
+ free_count_++;
+
+ if (i < first_free_bitmap_) {
+ first_free_bitmap_ = i;
+ }
+
+ if (free_count_ == 1) {
+ heap_->MoveToFreeList(this, bucket_);
+ } else {
+ // TODO(ccross): move down free list if necessary
+ }
+
+ if (frees_since_purge_++ * allocation_size_ > 16 * kPageSize) {
+ Purge();
+ }
+}
+
+void Chunk::Purge() {
+ frees_since_purge_ = 0;
+
+ //unsigned int allocsPerPage = kPageSize / allocation_size_;
+}
+
+// Override new operator on HeapImpl to use mmap to allocate a page
+void* HeapImpl::operator new(std::size_t count __attribute__((unused)))
+ noexcept {
+ assert(count == sizeof(HeapImpl));
+ void* mem = MapAligned(kPageSize, kPageSize);
+ if (!mem) {
+ abort(); //throw std::bad_alloc;
+ }
+
+ heap_count++;
+ return mem;
+}
+
+void HeapImpl::operator delete(void *ptr) {
+ munmap(ptr, kPageSize);
+}
+
+HeapImpl::HeapImpl() :
+ free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {
+}
+
+bool HeapImpl::Empty() {
+ for (unsigned int i = 0; i < kNumBuckets; i++) {
+ for (LinkedList<Chunk*> *it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ if (!it->data()->Empty()) {
+ return false;
+ }
+ }
+ for (LinkedList<Chunk*> *it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ if (!it->data()->Empty()) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+HeapImpl::~HeapImpl() {
+ for (unsigned int i = 0; i < kNumBuckets; i++) {
+ while (!free_chunks_[i].empty()) {
+ Chunk *chunk = free_chunks_[i].next()->data();
+ chunk->node_.remove();
+ delete chunk;
+ }
+ while (!full_chunks_[i].empty()) {
+ Chunk *chunk = full_chunks_[i].next()->data();
+ chunk->node_.remove();
+ delete chunk;
+ }
+ }
+}
+
+void* HeapImpl::Alloc(size_t size) {
+ std::lock_guard<std::mutex> lk(m_);
+ return AllocLocked(size);
+}
+
+void* HeapImpl::AllocLocked(size_t size) {
+ if (size > kMaxBucketAllocationSize) {
+ return MapAlloc(size);
+ }
+ int bucket = size_to_bucket(size);
+ if (free_chunks_[bucket].empty()) {
+ Chunk *chunk = new Chunk(this, bucket);
+ free_chunks_[bucket].insert(chunk->node_);
+ }
+ return free_chunks_[bucket].next()->data()->Alloc();
+}
+
+void HeapImpl::Free(void *ptr) {
+ std::lock_guard<std::mutex> lk(m_);
+ FreeLocked(ptr);
+}
+
+void HeapImpl::FreeLocked(void *ptr) {
+ if (!Chunk::is_chunk(ptr)) {
+ HeapImpl::MapFree(ptr);
+ } else {
+ Chunk* chunk = Chunk::ptr_to_chunk(ptr);
+ assert(chunk->heap() == this);
+ chunk->Free(ptr);
+ }
+}
+
+void* HeapImpl::MapAlloc(size_t size) {
+ size = (size + kPageSize - 1) & ~(kPageSize - 1);
+
+ MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(
+ sizeof(MapAllocation)));
+ void* ptr = MapAligned(size, kChunkSize);
+ if (!ptr) {
+ FreeLocked(allocation);
+ abort(); //throw std::bad_alloc;
+ }
+ allocation->ptr = ptr;
+ allocation->size = size;
+ allocation->next = map_allocation_list_;
+ map_allocation_list_ = allocation;
+
+ return ptr;
+}
+
+void HeapImpl::MapFree(void *ptr) {
+ MapAllocation **allocation = &map_allocation_list_;
+ while (*allocation && (*allocation)->ptr != ptr)
+ allocation = &(*allocation)->next;
+
+ assert(*allocation != nullptr);
+
+ munmap((*allocation)->ptr, (*allocation)->size);
+ FreeLocked(*allocation);
+
+ *allocation = (*allocation)->next;
+}
+
+void HeapImpl::MoveToFreeList(Chunk *chunk, int bucket) {
+ MoveToList(chunk, &free_chunks_[bucket]);
+}
+
+void HeapImpl::MoveToFullList(Chunk *chunk, int bucket) {
+ MoveToList(chunk, &full_chunks_[bucket]);
+}
+
+void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) {
+ // Remove from old list
+ chunk->node_.remove();
+
+ LinkedList<Chunk*> *node = head;
+ // Insert into new list, sorted by lowest free count
+ while (node->next() != head && node->data() != nullptr
+ && node->data()->free_count() < chunk->free_count())
+ node = node->next();
+
+ node->insert(chunk->node_);
+}
+
+Heap::Heap() {
+ // HeapImpl overloads the operator new in order to mmap itself instead of
+ // allocating with new.
+ // Can't use a shared_ptr to store the result because shared_ptr needs to
+ // allocate, and Allocator<T> is still being constructed.
+ impl_ = new HeapImpl();
+ owns_impl_ = true;
+}
+
+Heap::~Heap() {
+ if (owns_impl_) {
+ delete impl_;
+ }
+}
+
+void* Heap::allocate(size_t size) {
+ return impl_->Alloc(size);
+}
+
+void Heap::deallocate(void* ptr) {
+ impl_->Free(ptr);
+}
+
+void Heap::deallocate(HeapImpl*impl, void* ptr) {
+ impl->Free(ptr);
+}
+
+bool Heap::empty() {
+ return impl_->Empty();
+}
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
new file mode 100644
index 0000000..5390739
--- /dev/null
+++ b/libmemunreachable/Allocator.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_ALLOCATOR_H_
+#define LIBMEMUNREACHABLE_ALLOCATOR_H_
+
+#include <atomic>
+#include <cstddef>
+#include <functional>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+extern std::atomic<int> heap_count;
+
+class HeapImpl;
+
+template<typename T>
+class Allocator;
+
+
+// Non-templated class that implements wraps HeapImpl to keep
+// implementation out of the header file
+class Heap {
+public:
+ Heap();
+ ~Heap();
+
+ // Copy constructor that does not take ownership of impl_
+ Heap(const Heap& other) : impl_(other.impl_), owns_impl_(false) {}
+
+ // Assignment disabled
+ Heap& operator=(const Heap&) = delete;
+
+ // Allocate size bytes
+ void* allocate(size_t size);
+
+ // Deallocate allocation returned by allocate
+ void deallocate(void*);
+
+ bool empty();
+
+ static void deallocate(HeapImpl* impl, void* ptr);
+
+ // Allocate a class of type T
+ template<class T>
+ T* allocate() {
+ return reinterpret_cast<T*>(allocate(sizeof(T)));
+ }
+
+ // Comparators, copied objects will be equal
+ bool operator ==(const Heap& other) const {
+ return impl_ == other.impl_;
+ }
+ bool operator !=(const Heap& other) const {
+ return !(*this == other);
+ }
+
+ // std::unique_ptr wrapper that allocates using allocate and deletes using
+ // deallocate
+ template<class T>
+ using unique_ptr = std::unique_ptr<T, std::function<void(void*)>>;
+
+ template<class T, class... Args>
+ unique_ptr<T> make_unique(Args&&... args) {
+ HeapImpl* impl = impl_;
+ return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...),
+ [impl](void* ptr) {
+ reinterpret_cast<T*>(ptr)->~T();
+ deallocate(impl, ptr);
+ });
+ }
+
+ // std::unique_ptr wrapper that allocates using allocate and deletes using
+ // deallocate
+ template<class T>
+ using shared_ptr = std::shared_ptr<T>;
+
+ template<class T, class... Args>
+ shared_ptr<T> make_shared(Args&&... args);
+
+protected:
+ HeapImpl* impl_;
+ bool owns_impl_;
+};
+
+// STLAllocator implements the std allocator interface on top of a Heap
+template<typename T>
+class STLAllocator {
+public:
+ using value_type = T;
+ ~STLAllocator() {
+ }
+
+ // Construct an STLAllocator on top of a Heap
+ STLAllocator(const Heap& heap) : // NOLINT, implicit
+ heap_(heap) {
+ }
+
+ // Rebind an STLAllocator from an another STLAllocator
+ template<typename U>
+ STLAllocator(const STLAllocator<U>& other) : // NOLINT, implicit
+ heap_(other.heap_) {
+ }
+
+ STLAllocator(const STLAllocator&) = default;
+ STLAllocator<T>& operator=(const STLAllocator<T>&) = default;
+
+ T* allocate(std::size_t n) {
+ return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T)));
+ }
+
+ void deallocate(T* ptr, std::size_t) {
+ heap_.deallocate(ptr);
+ }
+
+ template<typename U>
+ bool operator ==(const STLAllocator<U>& other) const {
+ return heap_ == other.heap_;
+ }
+ template<typename U>
+ inline bool operator !=(const STLAllocator<U>& other) const {
+ return !(this == other);
+ }
+
+ template<typename U>
+ friend class STLAllocator;
+
+protected:
+ Heap heap_;
+};
+
+
+// Allocator extends STLAllocator with some convenience methods for allocating
+// a single object and for constructing unique_ptr and shared_ptr objects with
+// appropriate deleters.
+template<class T>
+class Allocator : public STLAllocator<T> {
+ public:
+ ~Allocator() {}
+
+ Allocator(const Heap& other) : // NOLINT, implicit
+ STLAllocator<T>(other) {
+ }
+
+ template<typename U>
+ Allocator(const STLAllocator<U>& other) : // NOLINT, implicit
+ STLAllocator<T>(other) {
+ }
+
+ Allocator(const Allocator&) = default;
+ Allocator<T>& operator=(const Allocator<T>&) = default;
+
+ using STLAllocator<T>::allocate;
+ using STLAllocator<T>::deallocate;
+ using STLAllocator<T>::heap_;
+
+ T* allocate() {
+ return STLAllocator<T>::allocate(1);
+ }
+ void deallocate(void* ptr) {
+ heap_.deallocate(ptr);
+ }
+
+ using shared_ptr = Heap::shared_ptr<T>;
+
+ template<class... Args>
+ shared_ptr make_shared(Args&& ...args) {
+ return heap_.template make_shared<T>(std::forward<Args>(args)...);
+ }
+
+ using unique_ptr = Heap::unique_ptr<T>;
+
+ template<class... Args>
+ unique_ptr make_unique(Args&& ...args) {
+ return heap_.template make_unique<T>(std::forward<Args>(args)...);
+ }
+};
+
+// std::unique_ptr wrapper that allocates using allocate and deletes using
+// deallocate. Implemented outside class definition in order to pass
+// Allocator<T> to shared_ptr.
+template<class T, class... Args>
+inline Heap::shared_ptr<T> Heap::make_shared(Args&&... args) {
+ return std::allocate_shared<T, Allocator<T>, Args...>(Allocator<T>(*this),
+ std::forward<Args>(args)...);
+}
+
+namespace allocator {
+
+template<class T>
+using vector = std::vector<T, Allocator<T>>;
+
+template<class T>
+using list = std::list<T, Allocator<T>>;
+
+template<class Key, class T, class Compare = std::less<Key>>
+using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>;
+
+template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
+
+template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>;
+
+template<class Key, class Compare = std::less<Key>>
+using set = std::set<Key, Compare, Allocator<Key>>;
+
+using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+}
+
+#endif
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
new file mode 100644
index 0000000..4662368
--- /dev/null
+++ b/libmemunreachable/Android.bp
@@ -0,0 +1,79 @@
+cc_defaults {
+ name: "libmemunreachable_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ clang: true,
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+}
+
+cc_library_shared {
+ name: "libmemunreachable",
+ defaults: ["libmemunreachable_defaults"],
+ srcs: [
+ "Allocator.cpp",
+ "HeapWalker.cpp",
+ "LeakFolding.cpp",
+ "LeakPipe.cpp",
+ "LineBuffer.cpp",
+ "MemUnreachable.cpp",
+ "ProcessMappings.cpp",
+ "PtracerThread.cpp",
+ "ThreadCapture.cpp",
+ ],
+
+ static_libs: [
+ "libc_malloc_debug_backtrace",
+ "libc_logging",
+ ],
+ // Only need this for arm since libc++ uses its own unwind code that
+ // doesn't mix with the other default unwind code.
+ arch: {
+ arm: {
+ static_libs: ["libunwind_llvm"],
+ },
+ },
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "memunreachable_test",
+ defaults: ["libmemunreachable_defaults"],
+ host_supported: true,
+ srcs: [
+ "tests/Allocator_test.cpp",
+ "tests/HeapWalker_test.cpp",
+ "tests/LeakFolding_test.cpp",
+ ],
+
+ target: {
+ android: {
+ srcs: [
+ "tests/DisableMalloc_test.cpp",
+ "tests/MemUnreachable_test.cpp",
+ "tests/ThreadCapture_test.cpp",
+ ],
+ shared_libs: [
+ "libmemunreachable",
+ ],
+ },
+ host: {
+ srcs: [
+ "Allocator.cpp",
+ "HeapWalker.cpp",
+ "LeakFolding.cpp",
+ "tests/HostMallocStub.cpp",
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
new file mode 100644
index 0000000..62366f2
--- /dev/null
+++ b/libmemunreachable/HeapWalker.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <map>
+#include <utility>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "LeakFolding.h"
+#include "ScopedSignalHandler.h"
+#include "log.h"
+
+bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
+ if (end == begin) {
+ end = begin + 1;
+ }
+ Range range{begin, end};
+ auto inserted = allocations_.insert(std::pair<Range, AllocationInfo>(range, AllocationInfo{}));
+ if (inserted.second) {
+ valid_allocations_range_.begin = std::min(valid_allocations_range_.begin, begin);
+ valid_allocations_range_.end = std::max(valid_allocations_range_.end, end);
+ allocation_bytes_ += range.size();
+ return true;
+ } else {
+ Range overlap = inserted.first->first;
+ if (overlap != range) {
+ ALOGE("range %p-%p overlaps with existing range %p-%p",
+ reinterpret_cast<void*>(begin),
+ reinterpret_cast<void*>(end),
+ reinterpret_cast<void*>(overlap.begin),
+ reinterpret_cast<void*>(overlap.end));
+ }
+ return false;
+ }
+}
+
+bool HeapWalker::WordContainsAllocationPtr(uintptr_t word_ptr, Range* range, AllocationInfo** info) {
+ walking_ptr_ = word_ptr;
+ // This access may segfault if the process under test has done something strange,
+ // for example mprotect(PROT_NONE) on a native heap page. If so, it will be
+ // caught and handled by mmaping a zero page over the faulting page.
+ uintptr_t value = *reinterpret_cast<uintptr_t*>(word_ptr);
+ walking_ptr_ = 0;
+ if (value >= valid_allocations_range_.begin && value < valid_allocations_range_.end) {
+ AllocationMap::iterator it = allocations_.find(Range{value, value + 1});
+ if (it != allocations_.end()) {
+ *range = it->first;
+ *info = &it->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+void HeapWalker::RecurseRoot(const Range& root) {
+ allocator::vector<Range> to_do(1, root, allocator_);
+ while (!to_do.empty()) {
+ Range range = to_do.back();
+ to_do.pop_back();
+
+ ForEachPtrInRange(range, [&](Range& ref_range, AllocationInfo* ref_info) {
+ if (!ref_info->referenced_from_root) {
+ ref_info->referenced_from_root = true;
+ to_do.push_back(ref_range);
+ }
+ });
+ }
+}
+
+void HeapWalker::Root(uintptr_t begin, uintptr_t end) {
+ roots_.push_back(Range{begin, end});
+}
+
+void HeapWalker::Root(const allocator::vector<uintptr_t>& vals) {
+ root_vals_.insert(root_vals_.end(), vals.begin(), vals.end());
+}
+
+size_t HeapWalker::Allocations() {
+ return allocations_.size();
+}
+
+size_t HeapWalker::AllocationBytes() {
+ return allocation_bytes_;
+}
+
+bool HeapWalker::DetectLeaks() {
+ // Recursively walk pointers from roots to mark referenced allocations
+ for (auto it = roots_.begin(); it != roots_.end(); it++) {
+ RecurseRoot(*it);
+ }
+
+ Range vals;
+ vals.begin = reinterpret_cast<uintptr_t>(root_vals_.data());
+ vals.end = vals.begin + root_vals_.size() * sizeof(uintptr_t);
+
+ RecurseRoot(vals);
+
+ return true;
+}
+
+bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,
+ size_t* num_leaks_out, size_t* leak_bytes_out) {
+ leaked.clear();
+
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
+ if (!it->second.referenced_from_root) {
+ num_leaks++;
+ leak_bytes += it->first.end - it->first.begin;
+ }
+ }
+
+ size_t n = 0;
+ for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
+ if (!it->second.referenced_from_root) {
+ if (n++ < limit) {
+ leaked.push_back(it->first);
+ }
+ }
+ }
+
+ if (num_leaks_out) {
+ *num_leaks_out = num_leaks;
+ }
+ if (leak_bytes_out) {
+ *leak_bytes_out = leak_bytes;
+ }
+
+ return true;
+}
+
+static bool MapOverPage(void* addr) {
+ const size_t page_size = sysconf(_SC_PAGE_SIZE);
+ void *page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size-1));
+
+ void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
+ if (ret == MAP_FAILED) {
+ ALOGE("failed to map page at %p: %s", page, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) {
+ uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr);
+ if (addr != walking_ptr_) {
+ handler.reset();
+ return;
+ }
+ ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+ if (!MapOverPage(si->si_addr)) {
+ handler.reset();
+ }
+}
+
+ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
new file mode 100644
index 0000000..b25696f
--- /dev/null
+++ b/libmemunreachable/HeapWalker.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_HEAP_WALKER_H_
+#define LIBMEMUNREACHABLE_HEAP_WALKER_H_
+
+#include <signal.h>
+
+#include "android-base/macros.h"
+
+#include "Allocator.h"
+#include "ScopedSignalHandler.h"
+#include "Tarjan.h"
+
+// A range [begin, end)
+struct Range {
+ uintptr_t begin;
+ uintptr_t end;
+
+ size_t size() const { return end - begin; };
+ bool operator==(const Range& other) const {
+ return this->begin == other.begin && this->end == other.end;
+ }
+ bool operator!=(const Range& other) const {
+ return !(*this == other);
+ }
+};
+
+// Comparator for Ranges that returns equivalence for overlapping ranges
+struct compare_range {
+ bool operator()(const Range& a, const Range& b) const {
+ return a.end <= b.begin;
+ }
+};
+
+class HeapWalker {
+ public:
+ explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
+ allocations_(allocator), allocation_bytes_(0),
+ roots_(allocator), root_vals_(allocator),
+ segv_handler_(allocator), walking_ptr_(0) {
+ valid_allocations_range_.end = 0;
+ valid_allocations_range_.begin = ~valid_allocations_range_.end;
+
+ segv_handler_.install(SIGSEGV,
+ [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
+ this->HandleSegFault(handler, signal, siginfo, uctx);
+ });
+ }
+
+ ~HeapWalker() {}
+ bool Allocation(uintptr_t begin, uintptr_t end);
+ void Root(uintptr_t begin, uintptr_t end);
+ void Root(const allocator::vector<uintptr_t>& vals);
+
+ bool DetectLeaks();
+
+ bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks,
+ size_t* leak_bytes);
+ size_t Allocations();
+ size_t AllocationBytes();
+
+ template<class F>
+ void ForEachPtrInRange(const Range& range, F&& f);
+
+ template<class F>
+ void ForEachAllocation(F&& f);
+
+ struct AllocationInfo {
+ bool referenced_from_root;
+ };
+
+ private:
+
+ void RecurseRoot(const Range& root);
+ bool WordContainsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
+ void HandleSegFault(ScopedSignalHandler&, int, siginfo_t*, void*);
+
+ DISALLOW_COPY_AND_ASSIGN(HeapWalker);
+ Allocator<HeapWalker> allocator_;
+ using AllocationMap = allocator::map<Range, AllocationInfo, compare_range>;
+ AllocationMap allocations_;
+ size_t allocation_bytes_;
+ Range valid_allocations_range_;
+
+ allocator::vector<Range> roots_;
+ allocator::vector<uintptr_t> root_vals_;
+
+ ScopedSignalHandler segv_handler_;
+ uintptr_t walking_ptr_;
+};
+
+template<class F>
+inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) {
+ uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
+ // TODO(ccross): we might need to consider a pointer to the end of a buffer
+ // to be inside the buffer, which means the common case of a pointer to the
+ // beginning of a buffer may keep two ranges live.
+ for (uintptr_t i = begin; i < range.end; i += sizeof(uintptr_t)) {
+ Range ref_range;
+ AllocationInfo* ref_info;
+ if (WordContainsAllocationPtr(i, &ref_range, &ref_info)) {
+ f(ref_range, ref_info);
+ }
+ }
+}
+
+template<class F>
+inline void HeapWalker::ForEachAllocation(F&& f) {
+ for (auto& it : allocations_) {
+ const Range& range = it.first;
+ HeapWalker::AllocationInfo& allocation = it.second;
+ f(range, allocation);
+ }
+}
+
+#endif
diff --git a/libmemunreachable/Leak.h b/libmemunreachable/Leak.h
new file mode 100644
index 0000000..eaeeea7
--- /dev/null
+++ b/libmemunreachable/Leak.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LEAK_H_
+#define LIBMEMUNREACHABLE_LEAK_H_
+
+#include <functional>
+#include <vector>
+
+#include "memunreachable/memunreachable.h"
+
+// Custom std::hash specialization so that Leak::Backtrace can be used
+// as a key in std::unordered_map.
+namespace std {
+
+template<>
+struct hash<Leak::Backtrace> {
+ std::size_t operator()(const Leak::Backtrace& key) const {
+ std::size_t seed = 0;
+
+ hash_combine(seed, key.num_frames);
+ for (size_t i = 0; i < key.num_frames; i++) {
+ hash_combine(seed, key.frames[i]);
+ }
+
+ return seed;
+ }
+
+ private:
+ template<typename T>
+ inline void hash_combine(std::size_t& seed, const T& v) const {
+ std::hash<T> hasher;
+ seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ }
+};
+
+} // namespace std
+
+static bool operator==(const Leak::Backtrace& lhs, const Leak::Backtrace& rhs) {
+ return (lhs.num_frames == rhs.num_frames) &&
+ memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+}
+
+#endif
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
new file mode 100644
index 0000000..be4d20c
--- /dev/null
+++ b/libmemunreachable/LeakFolding.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "LeakFolding.h"
+#include "Tarjan.h"
+#include "log.h"
+
+// Converts possibly cyclic graph of leaks to a DAG by combining
+// strongly-connected components into a object, stored in the scc pointer
+// of each node in the component.
+void LeakFolding::ComputeDAG() {
+ SCCList<LeakInfo> scc_list{allocator_};
+ Tarjan(leak_graph_, scc_list);
+
+ Allocator<SCCInfo> scc_allocator = allocator_;
+
+ for (auto& scc_nodes: scc_list) {
+ Allocator<SCCInfo>::unique_ptr leak_scc;
+ leak_scc = scc_allocator.make_unique(scc_allocator);
+
+ for (auto& node: scc_nodes) {
+ node->ptr->scc = leak_scc.get();
+ leak_scc->count++;
+ leak_scc->size += node->ptr->range.size();
+ }
+
+ leak_scc_.emplace_back(std::move(leak_scc));
+ }
+
+ for (auto& it : leak_map_) {
+ LeakInfo& leak = it.second;
+ for (auto& ref: leak.node.references_out) {
+ if (leak.scc != ref->ptr->scc) {
+ leak.scc->node.Edge(&ref->ptr->scc->node);
+ }
+ }
+ }
+}
+
+void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
+ std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_,
+ [&](SCCInfo* scc) {
+ if (scc->accumulator != dominator) {
+ scc->accumulator = dominator;
+ dominator->cuumulative_size += scc->size;
+ dominator->cuumulative_count += scc->count;
+ scc->node.Foreach([&](SCCInfo* ref) {
+ walk(ref);
+ });
+ }
+ });
+ walk(dominator);
+}
+
+bool LeakFolding::FoldLeaks() {
+ Allocator<LeakInfo> leak_allocator = allocator_;
+
+ // Find all leaked allocations insert them into leak_map_ and leak_graph_
+ heap_walker_.ForEachAllocation(
+ [&](const Range& range, HeapWalker::AllocationInfo& allocation) {
+ if (!allocation.referenced_from_root) {
+ auto it = leak_map_.emplace(std::piecewise_construct,
+ std::forward_as_tuple(range),
+ std::forward_as_tuple(range, allocator_));
+ LeakInfo& leak = it.first->second;
+ leak_graph_.push_back(&leak.node);
+ }
+ });
+
+ // Find references between leaked allocations and connect them in leak_graph_
+ for (auto& it : leak_map_) {
+ LeakInfo& leak = it.second;
+ heap_walker_.ForEachPtrInRange(leak.range,
+ [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
+ if (!ptr_info->referenced_from_root) {
+ LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
+ leak.node.Edge(&ptr_leak->node);
+ }
+ });
+ }
+
+ // Convert the cyclic graph to a DAG by grouping strongly connected components
+ ComputeDAG();
+
+ // Compute dominators and cuumulative sizes
+ for (auto& scc : leak_scc_) {
+ if (scc->node.references_in.size() == 0) {
+ scc->dominator = true;
+ AccumulateLeaks(scc.get());
+ }
+ }
+
+ return true;
+}
+
+bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked,
+ size_t* num_leaks_out, size_t* leak_bytes_out) {
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ for (auto& it : leak_map_) {
+ const LeakInfo& leak = it.second;
+ num_leaks++;
+ leak_bytes += leak.range.size();
+ }
+
+ for (auto& it : leak_map_) {
+ const LeakInfo& leak = it.second;
+ if (leak.scc->dominator) {
+ leaked.emplace_back(Leak{leak.range,
+ leak.scc->cuumulative_count - 1,
+ leak.scc->cuumulative_size - leak.range.size()});
+ }
+ }
+
+ if (num_leaks_out) {
+ *num_leaks_out = num_leaks;
+ }
+ if (leak_bytes_out) {
+ *leak_bytes_out = leak_bytes;
+ }
+
+ return true;
+}
diff --git a/libmemunreachable/LeakFolding.h b/libmemunreachable/LeakFolding.h
new file mode 100644
index 0000000..9c6a525
--- /dev/null
+++ b/libmemunreachable/LeakFolding.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+#define LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+
+#include "HeapWalker.h"
+
+class LeakFolding {
+ public:
+ LeakFolding(Allocator<void> allocator, HeapWalker& heap_walker)
+ : allocator_(allocator), heap_walker_(heap_walker),
+ leak_map_(allocator), leak_graph_(allocator), leak_scc_(allocator) {}
+
+ bool FoldLeaks();
+
+ struct Leak {
+ const Range range;
+ size_t referenced_count;
+ size_t referenced_size;
+ };
+
+ bool Leaked(allocator::vector<Leak>& leaked,
+ size_t* num_leaks_out, size_t* leak_bytes_out);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakFolding);
+ Allocator<void> allocator_;
+ HeapWalker& heap_walker_;
+
+ struct SCCInfo {
+ public:
+ Node<SCCInfo> node;
+
+ size_t count;
+ size_t size;
+
+ size_t cuumulative_count;
+ size_t cuumulative_size;
+
+ bool dominator;
+ SCCInfo* accumulator;
+
+ explicit SCCInfo(Allocator<SCCInfo> allocator) : node(this, allocator),
+ count(0), size(0), cuumulative_count(0), cuumulative_size(0),
+ dominator(false), accumulator(nullptr) {}
+ private:
+ SCCInfo(SCCInfo&&) = delete;
+ DISALLOW_COPY_AND_ASSIGN(SCCInfo);
+ };
+
+ struct LeakInfo {
+ public:
+ Node<LeakInfo> node;
+
+ const Range range;
+
+ SCCInfo* scc;
+
+ LeakInfo(const Range& range, Allocator<LeakInfo> allocator)
+ : node(this, allocator), range(range),
+ scc(nullptr) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakInfo);
+ };
+
+ void ComputeDAG();
+ void AccumulateLeaks(SCCInfo* dominator);
+
+ allocator::map<Range, LeakInfo, compare_range> leak_map_;
+ Graph<LeakInfo> leak_graph_;
+ allocator::vector<Allocator<SCCInfo>::unique_ptr> leak_scc_;
+};
+
+#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
new file mode 100644
index 0000000..080f8a7
--- /dev/null
+++ b/libmemunreachable/LeakPipe.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "LeakPipe.h"
+
+#include "log.h"
+
+bool LeakPipe::SendFd(int sock, int fd) {
+ struct msghdr hdr{};
+ struct iovec iov{};
+ unsigned int data = 0xfdfdfdfd;
+ alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ iov.iov_base = &data;
+ iov.iov_len = sizeof(data);
+
+ hdr.msg_control = cmsgbuf;
+ hdr.msg_controllen = CMSG_LEN(sizeof(int));
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ *(int*)CMSG_DATA(cmsg) = fd;
+
+ int ret = sendmsg(sock, &hdr, 0);
+ if (ret < 0) {
+ ALOGE("failed to send fd: %s", strerror(errno));
+ return false;
+ }
+ if (ret == 0) {
+ ALOGE("eof when sending fd");
+ return false;
+ }
+
+ return true;
+}
+
+int LeakPipe::ReceiveFd(int sock) {
+ struct msghdr hdr{};
+ struct iovec iov{};
+ unsigned int data;
+ alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ iov.iov_base = &data;
+ iov.iov_len = sizeof(data);
+
+ hdr.msg_control = cmsgbuf;
+ hdr.msg_controllen = CMSG_LEN(sizeof(int));
+
+ int ret = recvmsg(sock, &hdr, 0);
+ if (ret < 0) {
+ ALOGE("failed to receive fd: %s", strerror(errno));
+ return -1;
+ }
+ if (ret == 0) {
+ ALOGE("eof when receiving fd");
+ return -1;
+ }
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+ if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ ALOGE("missing fd while receiving fd");
+ return -1;
+ }
+
+ return *(int*)CMSG_DATA(cmsg);
+}
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
new file mode 100644
index 0000000..3f4e0b7
--- /dev/null
+++ b/libmemunreachable/LeakPipe.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LEAK_PIPE_H_
+#define LIBMEMUNREACHABLE_LEAK_PIPE_H_
+
+#include <sys/socket.h>
+
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "ScopedPipe.h"
+#include "log.h"
+
+// LeakPipe implements a pipe that can transfer vectors of simple objects
+// between processes. The pipe is created in the sending process and
+// transferred over a socketpair that was created before forking. This ensures
+// that only the sending process can have the send side of the pipe open, so if
+// the sending process dies the pipe will close.
+class LeakPipe {
+ public:
+ LeakPipe() {
+ int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
+ if (ret < 0) {
+ LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+ }
+ }
+
+ ~LeakPipe() {
+ Close();
+ }
+
+ void Close() {
+ close(sv_[0]);
+ close(sv_[1]);
+ sv_[0] = -1;
+ sv_[1] = -1;
+ }
+
+ bool OpenReceiver() {
+ int fd = ReceiveFd(sv_[0]);
+ if (fd < 0) {
+ return false;
+ }
+
+ receiver_.SetFd(fd);
+ return true;
+ }
+
+ bool OpenSender() {
+ ScopedPipe pipe;
+
+ if (!SendFd(sv_[1], pipe.Receiver())) {
+ return false;
+ }
+ pipe.ReleaseReceiver();
+
+ sender_.SetFd(pipe.ReleaseSender());
+ return true;
+ }
+
+ class LeakPipeBase {
+ public:
+ LeakPipeBase() : fd_(-1) {}
+
+ ~LeakPipeBase() {
+ Close();
+ }
+
+ void SetFd(int fd) {
+ fd_ = fd;
+ }
+
+ void Close() {
+ close(fd_);
+ fd_ = -1;
+ }
+
+ protected:
+ int fd_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakPipeBase);
+ };
+
+ class LeakPipeSender : public LeakPipeBase {
+ public:
+ using LeakPipeBase::LeakPipeBase;
+
+ template<typename T>
+ bool Send(const T& value) {
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
+ if (ret < 0) {
+ ALOGE("failed to send value: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(ret) != sizeof(T)) {
+ ALOGE("eof while writing value");
+ return false;
+ }
+
+ return true;
+ }
+
+ template<class T, class Alloc = std::allocator<T>>
+ bool SendVector(const std::vector<T, Alloc>& vector) {
+ size_t size = vector.size() * sizeof(T);
+ if (!Send(size)) {
+ return false;
+ }
+
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
+ if (ret < 0) {
+ ALOGE("failed to send vector: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(ret) != size) {
+ ALOGE("eof while writing vector");
+ return false;
+ }
+
+ return true;
+ }
+ };
+
+ class LeakPipeReceiver : public LeakPipeBase {
+ public:
+ using LeakPipeBase::LeakPipeBase;
+
+ template<typename T>
+ bool Receive(T* value) {
+ ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
+ if (ret < 0) {
+ ALOGE("failed to receive value: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(ret) != sizeof(T)) {
+ ALOGE("eof while receiving value");
+ return false;
+ }
+
+ return true;
+ }
+
+ template<class T, class Alloc = std::allocator<T>>
+ bool ReceiveVector(std::vector<T, Alloc>& vector) {
+ size_t size = 0;
+ if (!Receive(&size)) {
+ return false;
+ }
+
+ vector.resize(size / sizeof(T));
+
+ char* ptr = reinterpret_cast<char*>(vector.data());
+ while (size > 0) {
+ ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
+ if (ret < 0) {
+ ALOGE("failed to send vector: %s", strerror(errno));
+ return false;
+ } else if (ret == 0) {
+ ALOGE("eof while reading vector");
+ return false;
+ }
+ size -= ret;
+ ptr += ret;
+ }
+
+ return true;
+ }
+
+ };
+
+ LeakPipeReceiver& Receiver() {
+ return receiver_;
+ }
+
+ LeakPipeSender& Sender() {
+ return sender_;
+ }
+
+ private:
+ LeakPipeReceiver receiver_;
+ LeakPipeSender sender_;
+ bool SendFd(int sock, int fd);
+ int ReceiveFd(int sock);
+ DISALLOW_COPY_AND_ASSIGN(LeakPipe);
+ int sv_[2];
+};
+
+#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
new file mode 100644
index 0000000..d3580c0
--- /dev/null
+++ b/libmemunreachable/LineBuffer.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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"
+
+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;
+ }
+}
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h
new file mode 100644
index 0000000..a015c46
--- /dev/null
+++ b/libmemunreachable/LineBuffer.h
@@ -0,0 +1,36 @@
+/*
+ * 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>
+
+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;
+};
+
+#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/LinkedList.h b/libmemunreachable/LinkedList.h
new file mode 100644
index 0000000..132842d
--- /dev/null
+++ b/libmemunreachable/LinkedList.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LINKED_LIST_H_
+#define LIBMEMUNREACHABLE_LINKED_LIST_H_
+
+template<class T>
+class LinkedList {
+public:
+ LinkedList() : next_(this), prev_(this), data_() {}
+ explicit LinkedList(T data) : LinkedList() {
+ data_ = data;
+ }
+ ~LinkedList() {}
+ void insert(LinkedList<T>& node) {
+ assert(node.empty());
+ node.next_ = this->next_;
+ node.next_->prev_ = &node;
+ this->next_ = &node;
+ node.prev_ = this;
+ }
+ void remove() {
+ this->next_->prev_ = this->prev_;
+ this->prev_->next_ = this->next_;
+ this->next_ = this;
+ this->prev_ = this;
+ }
+ T data() { return data_; }
+ bool empty() { return next_ == this && prev_ == this; }
+ LinkedList<T> *next() { return next_; }
+private:
+ LinkedList<T> *next_;
+ LinkedList<T> *prev_;
+ T data_;
+};
+
+template<class T>
+class LinkedListHead {
+public:
+ LinkedListHead() : node_() {}
+ ~LinkedListHead() {}
+
+private:
+ LinkedList<T> node_;
+};
+
+#endif
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
new file mode 100644
index 0000000..ac19a66
--- /dev/null
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#include <functional>
+#include <iomanip>
+#include <mutex>
+#include <string>
+#include <sstream>
+#include <unordered_map>
+
+#include <backtrace.h>
+#include <android-base/macros.h>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "Leak.h"
+#include "LeakFolding.h"
+#include "LeakPipe.h"
+#include "ProcessMappings.h"
+#include "PtracerThread.h"
+#include "ScopedDisableMalloc.h"
+#include "Semaphore.h"
+#include "ThreadCapture.h"
+
+#include "memunreachable/memunreachable.h"
+#include "bionic.h"
+#include "log.h"
+
+const size_t Leak::contents_length;
+
+using namespace std::chrono_literals;
+
+class MemUnreachable {
+ public:
+ MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator),
+ heap_walker_(allocator_) {}
+ bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
+ const allocator::vector<Mapping>& mappings);
+ bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
+ size_t* num_leaks, size_t* leak_bytes);
+ size_t Allocations() { return heap_walker_.Allocations(); }
+ size_t AllocationBytes() { return heap_walker_.AllocationBytes(); }
+ private:
+ bool ClassifyMappings(const allocator::vector<Mapping>& mappings,
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings);
+ DISALLOW_COPY_AND_ASSIGN(MemUnreachable);
+ pid_t pid_;
+ Allocator<void> allocator_;
+ HeapWalker heap_walker_;
+};
+
+static void HeapIterate(const Mapping& heap_mapping,
+ const std::function<void(uintptr_t, size_t)>& func) {
+ malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin,
+ [](uintptr_t base, size_t size, void* arg) {
+ auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
+ (*f)(base, size);
+ }, const_cast<void*>(reinterpret_cast<const void*>(&func)));
+}
+
+bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
+ const allocator::vector<Mapping>& mappings) {
+ ALOGI("searching process %d for allocations", pid_);
+ allocator::vector<Mapping> heap_mappings{mappings};
+ allocator::vector<Mapping> anon_mappings{mappings};
+ allocator::vector<Mapping> globals_mappings{mappings};
+ allocator::vector<Mapping> stack_mappings{mappings};
+ if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
+ globals_mappings, stack_mappings)) {
+ return false;
+ }
+
+ for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
+ ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ HeapIterate(*it, [&](uintptr_t base, size_t size) {
+ heap_walker_.Allocation(base, base + size);
+ });
+ }
+
+ for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
+ ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ heap_walker_.Allocation(it->begin, it->end);
+ }
+
+ for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
+ ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ heap_walker_.Root(it->begin, it->end);
+ }
+
+ for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
+ for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
+ if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
+ ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+ heap_walker_.Root(thread_it->stack.first, it->end);
+ }
+ }
+ heap_walker_.Root(thread_it->regs);
+ }
+
+ ALOGI("searching done");
+
+ return true;
+}
+
+bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
+ size_t limit, size_t* num_leaks, size_t* leak_bytes) {
+ ALOGI("sweeping process %d for unreachable memory", pid_);
+ leaks.clear();
+
+ if (!heap_walker_.DetectLeaks()) {
+ return false;
+ }
+
+
+ allocator::vector<Range> leaked1{allocator_};
+ heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
+
+ ALOGI("sweeping done");
+
+ ALOGI("folding related leaks");
+
+ LeakFolding folding(allocator_, heap_walker_);
+ if (!folding.FoldLeaks()) {
+ return false;
+ }
+
+ allocator::vector<LeakFolding::Leak> leaked{allocator_};
+
+ if (!folding.Leaked(leaked, num_leaks, leak_bytes)) {
+ return false;
+ }
+
+ allocator::unordered_map<Leak::Backtrace, Leak*> backtrace_map{allocator_};
+
+ // Prevent reallocations of backing memory so we can store pointers into it
+ // in backtrace_map.
+ leaks.reserve(leaked.size());
+
+ for (auto& it: leaked) {
+ leaks.emplace_back();
+ Leak* leak = &leaks.back();
+
+ ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it.range.begin),
+ leak->backtrace.frames, leak->backtrace.max_frames);
+ if (num_backtrace_frames > 0) {
+ leak->backtrace.num_frames = num_backtrace_frames;
+
+ auto inserted = backtrace_map.emplace(leak->backtrace, leak);
+ if (!inserted.second) {
+ // Leak with same backtrace already exists, drop this one and
+ // increment similar counts on the existing one.
+ leaks.pop_back();
+ Leak* similar_leak = inserted.first->second;
+ similar_leak->similar_count++;
+ similar_leak->similar_size += it.range.size();
+ similar_leak->similar_referenced_count += it.referenced_count;
+ similar_leak->similar_referenced_size += it.referenced_size;
+ similar_leak->total_size += it.range.size();
+ similar_leak->total_size += it.referenced_size;
+ continue;
+ }
+ }
+
+ leak->begin = it.range.begin;
+ leak->size = it.range.size();
+ leak->referenced_count = it.referenced_count;
+ leak->referenced_size = it.referenced_size;
+ leak->total_size = leak->size + leak->referenced_size;
+ memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin),
+ std::min(leak->size, Leak::contents_length));
+ }
+
+ ALOGI("folding done");
+
+ std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
+ return a.total_size > b.total_size;
+ });
+
+ if (leaks.size() > limit) {
+ leaks.resize(limit);
+ }
+
+ return true;
+}
+
+static bool has_prefix(const allocator::string& s, const char* prefix) {
+ int ret = s.compare(0, strlen(prefix), prefix);
+ return ret == 0;
+}
+
+bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings)
+{
+ heap_mappings.clear();
+ anon_mappings.clear();
+ globals_mappings.clear();
+ stack_mappings.clear();
+
+ allocator::string current_lib{allocator_};
+
+ for (auto it = mappings.begin(); it != mappings.end(); it++) {
+ if (it->execute) {
+ current_lib = it->name;
+ continue;
+ }
+
+ if (!it->read) {
+ continue;
+ }
+
+ const allocator::string mapping_name{it->name, allocator_};
+ if (mapping_name == "[anon:.bss]") {
+ // named .bss section
+ globals_mappings.emplace_back(*it);
+ } else if (mapping_name == current_lib) {
+ // .rodata or .data section
+ globals_mappings.emplace_back(*it);
+ } else if (mapping_name == "[anon:libc_malloc]") {
+ // named malloc mapping
+ heap_mappings.emplace_back(*it);
+ } else if (has_prefix(mapping_name, "/dev/ashmem/dalvik")) {
+ // named dalvik heap mapping
+ globals_mappings.emplace_back(*it);
+ } else if (has_prefix(mapping_name, "[stack")) {
+ // named stack mapping
+ stack_mappings.emplace_back(*it);
+ } else if (mapping_name.size() == 0) {
+ globals_mappings.emplace_back(*it);
+ } else if (has_prefix(mapping_name, "[anon:") && mapping_name != "[anon:leak_detector_malloc]") {
+ // TODO(ccross): it would be nice to treat named anonymous mappings as
+ // possible leaks, but naming something in a .bss or .data section makes
+ // it impossible to distinguish them from mmaped and then named mappings.
+ globals_mappings.emplace_back(*it);
+ }
+ }
+
+ return true;
+}
+
+template<typename T>
+static inline const char* plural(T val) {
+ return (val == 1) ? "" : "s";
+}
+
+bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) {
+ int parent_pid = getpid();
+ int parent_tid = gettid();
+
+ Heap heap;
+
+ Semaphore continue_parent_sem;
+ LeakPipe pipe;
+
+ PtracerThread thread{[&]() -> int {
+ /////////////////////////////////////////////
+ // Collection thread
+ /////////////////////////////////////////////
+ ALOGI("collecting thread info for process %d...", parent_pid);
+
+ ThreadCapture thread_capture(parent_pid, heap);
+ allocator::vector<ThreadInfo> thread_info(heap);
+ allocator::vector<Mapping> mappings(heap);
+
+ // ptrace all the threads
+ if (!thread_capture.CaptureThreads()) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
+ // collect register contents and stacks
+ if (!thread_capture.CapturedThreadInfo(thread_info)) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
+ // snapshot /proc/pid/maps
+ if (!ProcessMappings(parent_pid, mappings)) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
+ // malloc must be enabled to call fork, at_fork handlers take the same
+ // locks as ScopedDisableMalloc. All threads are paused in ptrace, so
+ // memory state is still consistent. Unfreeze the original thread so it
+ // can drop the malloc locks, it will block until the collection thread
+ // exits.
+ thread_capture.ReleaseThread(parent_tid);
+ continue_parent_sem.Post();
+
+ // fork a process to do the heap walking
+ int ret = fork();
+ if (ret < 0) {
+ return 1;
+ } else if (ret == 0) {
+ /////////////////////////////////////////////
+ // Heap walker process
+ /////////////////////////////////////////////
+ // Examine memory state in the child using the data collected above and
+ // the CoW snapshot of the process memory contents.
+
+ if (!pipe.OpenSender()) {
+ _exit(1);
+ }
+
+ MemUnreachable unreachable{parent_pid, heap};
+
+ if (!unreachable.CollectAllocations(thread_info, mappings)) {
+ _exit(2);
+ }
+ size_t num_allocations = unreachable.Allocations();
+ size_t allocation_bytes = unreachable.AllocationBytes();
+
+ allocator::vector<Leak> leaks{heap};
+
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ bool ok = unreachable.GetUnreachableMemory(leaks, limit, &num_leaks, &leak_bytes);
+
+ ok = ok && pipe.Sender().Send(num_allocations);
+ ok = ok && pipe.Sender().Send(allocation_bytes);
+ ok = ok && pipe.Sender().Send(num_leaks);
+ ok = ok && pipe.Sender().Send(leak_bytes);
+ ok = ok && pipe.Sender().SendVector(leaks);
+
+ if (!ok) {
+ _exit(3);
+ }
+
+ _exit(0);
+ } else {
+ // Nothing left to do in the collection thread, return immediately,
+ // releasing all the captured threads.
+ ALOGI("collection thread done");
+ return 0;
+ }
+ }};
+
+ /////////////////////////////////////////////
+ // Original thread
+ /////////////////////////////////////////////
+
+ {
+ // Disable malloc to get a consistent view of memory
+ ScopedDisableMalloc disable_malloc;
+
+ // Start the collection thread
+ thread.Start();
+
+ // Wait for the collection thread to signal that it is ready to fork the
+ // heap walker process.
+ continue_parent_sem.Wait(30s);
+
+ // Re-enable malloc so the collection thread can fork.
+ }
+
+ // Wait for the collection thread to exit
+ int ret = thread.Join();
+ if (ret != 0) {
+ return false;
+ }
+
+ // Get a pipe from the heap walker process. Transferring a new pipe fd
+ // ensures no other forked processes can have it open, so when the heap
+ // walker process dies the remote side of the pipe will close.
+ if (!pipe.OpenReceiver()) {
+ return false;
+ }
+
+ bool ok = true;
+ ok = ok && pipe.Receiver().Receive(&info.num_allocations);
+ ok = ok && pipe.Receiver().Receive(&info.allocation_bytes);
+ ok = ok && pipe.Receiver().Receive(&info.num_leaks);
+ ok = ok && pipe.Receiver().Receive(&info.leak_bytes);
+ ok = ok && pipe.Receiver().ReceiveVector(info.leaks);
+ if (!ok) {
+ return false;
+ }
+
+ ALOGI("unreachable memory detection done");
+ ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
+ info.leak_bytes, info.num_leaks, plural(info.num_leaks),
+ info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
+ return true;
+}
+
+std::string Leak::ToString(bool log_contents) const {
+
+ std::ostringstream oss;
+
+ oss << " " << std::dec << size;
+ oss << " bytes unreachable at ";
+ oss << std::hex << begin;
+ oss << std::endl;
+ if (referenced_count > 0) {
+ oss << std::dec;
+ oss << " referencing " << referenced_size << " unreachable bytes";
+ oss << " in " << referenced_count << " allocation" << plural(referenced_count);
+ oss << std::endl;
+ }
+ if (similar_count > 0) {
+ oss << std::dec;
+ oss << " and " << similar_size << " similar unreachable bytes";
+ oss << " in " << similar_count << " allocation" << plural(similar_count);
+ oss << std::endl;
+ if (similar_referenced_count > 0) {
+ oss << " referencing " << similar_referenced_size << " unreachable bytes";
+ oss << " in " << similar_referenced_count << " allocation" << plural(similar_referenced_count);
+ oss << std::endl;
+ }
+ }
+
+ if (log_contents) {
+ const int bytes_per_line = 16;
+ const size_t bytes = std::min(size, contents_length);
+
+ if (bytes == size) {
+ oss << " contents:" << std::endl;
+ } else {
+ oss << " first " << bytes << " bytes of contents:" << std::endl;
+ }
+
+ for (size_t i = 0; i < bytes; i += bytes_per_line) {
+ oss << " " << std::hex << begin + i << ": ";
+ size_t j;
+ oss << std::setfill('0');
+ for (j = i; j < bytes && j < i + bytes_per_line; j++) {
+ oss << std::setw(2) << static_cast<int>(contents[j]) << " ";
+ }
+ oss << std::setfill(' ');
+ for (; j < i + bytes_per_line; j++) {
+ oss << " ";
+ }
+ for (j = i; j < bytes && j < i + bytes_per_line; j++) {
+ char c = contents[j];
+ if (c < ' ' || c >= 0x7f) {
+ c = '.';
+ }
+ oss << c;
+ }
+ oss << std::endl;
+ }
+ }
+ if (backtrace.num_frames > 0) {
+ oss << backtrace_string(backtrace.frames, backtrace.num_frames);
+ }
+
+ return oss.str();
+}
+
+// Figure out the abi based on defined macros.
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#else
+#error "Unsupported ABI"
+#endif
+
+std::string UnreachableMemoryInfo::ToString(bool log_contents) const {
+ std::ostringstream oss;
+ oss << " " << leak_bytes << " bytes in ";
+ oss << num_leaks << " unreachable allocation" << plural(num_leaks);
+ oss << std::endl;
+ oss << " ABI: '" ABI_STRING "'" << std::endl;
+ oss << std::endl;
+
+ for (auto it = leaks.begin(); it != leaks.end(); it++) {
+ oss << it->ToString(log_contents);
+ oss << std::endl;
+ }
+
+ return oss.str();
+}
+
+std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
+ UnreachableMemoryInfo info;
+ if (!GetUnreachableMemory(info, limit)) {
+ return "Failed to get unreachable memory\n";
+ }
+
+ return info.ToString(log_contents);
+}
+
+bool LogUnreachableMemory(bool log_contents, size_t limit) {
+ UnreachableMemoryInfo info;
+ if (!GetUnreachableMemory(info, limit)) {
+ return false;
+ }
+
+ for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
+ ALOGE("%s", it->ToString(log_contents).c_str());
+ }
+ return true;
+}
+
+
+bool NoLeaks() {
+ UnreachableMemoryInfo info;
+ if (!GetUnreachableMemory(info, 0)) {
+ return false;
+ }
+
+ return info.num_leaks == 0;
+}
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
new file mode 100644
index 0000000..57b2321
--- /dev/null
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+#include "LineBuffer.h"
+#include "ProcessMappings.h"
+#include "log.h"
+
+// This function is not re-entrant since it uses a static buffer for
+// the line data.
+bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
+ char map_buffer[1024];
+ snprintf(map_buffer, sizeof(map_buffer), "/proc/%d/maps", pid);
+ android::base::unique_fd fd(open(map_buffer, O_RDONLY));
+ 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);
+ }
+ }
+ return true;
+}
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
new file mode 100644
index 0000000..d3b7496
--- /dev/null
+++ b/libmemunreachable/ProcessMappings.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+#define LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+
+#include "Allocator.h"
+
+struct Mapping {
+ uintptr_t begin;
+ uintptr_t end;
+ bool read;
+ bool write;
+ bool execute;
+ bool priv;
+ char name[96];
+};
+
+// This function is not re-entrant since it uses a static buffer for
+// the line data.
+bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings);
+
+#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
new file mode 100644
index 0000000..4e3c41e
--- /dev/null
+++ b/libmemunreachable/PtracerThread.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "android-base/macros.h"
+
+#include "anon_vma_naming.h"
+#include "log.h"
+#include "PtracerThread.h"
+
+class Stack {
+ public:
+ explicit Stack(size_t size) : size_(size) {
+ int prot = PROT_READ | PROT_WRITE;
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ page_size_ = sysconf(_SC_PAGE_SIZE);
+ size_ += page_size_*2; // guard pages
+ base_ = mmap(NULL, size_, prot, flags, -1, 0);
+ if (base_ == MAP_FAILED) {
+ base_ = NULL;
+ size_ = 0;
+ return;
+ }
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base_, size_, "libmemunreachable stack");
+ mprotect(base_, page_size_, PROT_NONE);
+ mprotect(top(), page_size_, PROT_NONE);
+ };
+ ~Stack() {
+ munmap(base_, size_);
+ };
+ void* top() {
+ return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_) + size_ - page_size_);
+ };
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Stack);
+
+ void *base_;
+ size_t size_;
+ size_t page_size_;
+};
+
+PtracerThread::PtracerThread(const std::function<int()>& func) :
+ child_pid_(0) {
+ stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
+ if (stack_->top() == nullptr) {
+ LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+ }
+
+ func_ = std::function<int()>{[&, func]() -> int {
+ // In the child thread, lock and unlock the mutex to wait for the parent
+ // to finish setting up for the child thread
+ std::unique_lock<std::mutex> lk(m_);
+ lk.unlock();
+ _exit(func());
+ }};
+}
+
+PtracerThread::~PtracerThread() {
+ Kill();
+ Join();
+ ClearTracer();
+ stack_ = nullptr;
+}
+
+bool PtracerThread::Start() {
+ std::unique_lock<std::mutex> lk(m_);
+
+ // Convert from void(*)(void*) to lambda with captures
+ auto proxy = [](void *arg) -> int {
+ prctl(PR_SET_NAME, "libmemunreachable ptrace thread");
+ return (*reinterpret_cast<std::function<int()>*>(arg))();
+ };
+
+ child_pid_ = clone(proxy, stack_->top(),
+ CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
+ reinterpret_cast<void*>(&func_));
+ if (child_pid_ < 0) {
+ ALOGE("failed to clone child: %s", strerror(errno));
+ return false;
+ }
+
+ SetTracer(child_pid_);
+
+ lk.unlock();
+
+ return true;
+}
+
+int PtracerThread::Join() {
+ if (child_pid_ == -1) {
+ return -1;
+ }
+ int status;
+ int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
+ if (ret < 0) {
+ ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+ return -1;
+ }
+
+ child_pid_ = -1;
+
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ return -WTERMSIG(status);
+ } else {
+ ALOGE("unexpected status %x", status);
+ return -1;
+ }
+}
+
+void PtracerThread::Kill() {
+ if (child_pid_ == -1) {
+ return;
+ }
+
+ syscall(SYS_tkill, child_pid_, SIGKILL);
+}
+
+void PtracerThread::SetTracer(pid_t tracer_pid) {
+ prctl(PR_SET_PTRACER, tracer_pid);
+}
+
+void PtracerThread::ClearTracer() {
+ prctl(PR_SET_PTRACER, 0);
+}
diff --git a/libmemunreachable/PtracerThread.h b/libmemunreachable/PtracerThread.h
new file mode 100644
index 0000000..f88b599
--- /dev/null
+++ b/libmemunreachable/PtracerThread.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+#define LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+
+#include <functional>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+#include "Allocator.h"
+
+class Stack;
+
+// PtracerThread is similar to std::thread, except that it creates a "thread"
+// that can ptrace the other threads. The thread is actually a separate
+// process, with its own thread group, but shares address space and fds with
+// the parent.
+class PtracerThread {
+ public:
+ explicit PtracerThread(const std::function<int()>& func);
+ ~PtracerThread();
+ bool Start();
+ int Join();
+ private:
+ void SetTracer(pid_t);
+ void ClearTracer();
+ void Kill();
+ DISALLOW_COPY_AND_ASSIGN(PtracerThread);
+ std::unique_ptr<Stack> stack_;
+ std::function<int()> func_;
+ std::mutex m_;
+ pid_t child_pid_;
+};
+
+#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
new file mode 100644
index 0000000..61a47de
--- /dev/null
+++ b/libmemunreachable/README.md
@@ -0,0 +1,71 @@
+libmemunreachable
+================
+
+Introduction
+--------------
+libmemunreachable is a zero-overhead native memory leak detector. It uses an imprecise mark-and-sweep garbage collector pass over all native memory, reporting any unreachable blocks as leaks. It is similar to the [Heap Checker from tcmalloc](http://htmlpreview.github.io/?https://github.com/gperftools/gperftools/blob/master/doc/heap_checker.html), but with a few key differences to remove the overhead. Instead of instrumenting every call to malloc and free, it queries the allocator (jemalloc) for active allocations when leak detection is requested. In addition, it performs a very short stop-the-world data collection on the main process, and then forks a copy of the process to perform the mark-and-sweep, minimizing disruption to the original process.
+
+In the default (zero-overhead) mode, the returned data on leaks is limited to the address, approximate (upper bound) size, and the the first 32 bytes of the contents of the leaked allocation. If malloc_debug backtraces are enabled they will be included in the leak information, but backtracing allocations requires significant overhead.
+
+----------
+
+Usage
+-------
+
+### C interface ###
+
+#### `bool LogUnreachableMemory(bool log_contents, size_t limit)` ####
+Writes a description of leaked memory to the log. A summary is always written, followed by details of up to `limit` leaks. If `log_contents` is `true`, details include up to 32 bytes of the contents of each leaked allocation.
+Returns true if leak detection succeeded.
+
+#### `bool NoLeaks()` ####
+Returns `true` if no unreachable memory was found.
+
+### C++ interface ###
+
+####`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)` ####
+Returns a description of leaked memory. A summary is always written, followed by details of up to `limit` leaks. If `log_contents` is `true`, details include up to 32 bytes of the contents of each leaked allocation.
+Returns true if leak detection succeeded.
+
+Implementation
+-------------------
+The sequence of steps required to perform a leak detection pass is divided into three processes - the original process, the collection process, and the sweeper process.
+
+ 1. *Original process*: Leak detection is requested by calling `GetUnreachableMemory()`
+ 2. Allocations are disabled using `malloc_disable()`
+ 3. The collection process is spawned. The collection process is similar to a normal `fork()` child process, except that it shares the address space of the parent - any writes by the original process are visible to the collection process, and vice-versa.
+ 4. *Collection process*: All threads in the original process are paused with `ptrace()`.
+ 5. Registers contents, active stack areas, and memory mapping information are collected.
+ 6. *Original process*: Allocations are re-enabled using `malloc_enable()`, but all threads are still paused with `ptrace()`.
+ 7. *Collection process*: The sweeper process is spawned using a normal `fork()`. The sweeper process has a copy of all memory from the original process, including all the data collected by the collection process.
+ 8. Collection process releases all threads from `ptrace` and exits
+ 9. *Original process*: All threads continue, the thread that called `GetUnreachableMemory()` blocks waiting for leak data over a pipe.
+ 10. *Sweeper process*: A list of all active allocations is produced by examining the memory mappings and calling `malloc_iterate()` on any heap mappings.
+ 11. A list of all roots is produced from globals (.data and .bss sections of binaries), and registers and stacks from each thread.
+ 12. The mark-and-sweep pass is performed starting from roots.
+ 13. Unmarked allocations are sent over the pipe back to the original process.
+
+----------
+
+
+Components
+---------------
+- `MemUnreachable.cpp`: Entry points, implements the sequencing described above.
+- `PtracerThread.cpp`: Used to clone the collection process with shared address space.
+- `ThreadCapture.cpp`: Pauses threads in the main process and collects register contents.
+- `ProcessMappings.cpp`: Collects snapshots of `/proc/pid/maps`.
+- `HeapWalker.cpp`: Performs the mark-and-sweep pass over active allocations.
+- `LeakPipe.cpp`: transfers data describing leaks from the sweeper process to the original process.
+
+
+Heap allocator requirements
+----------------------------------
+libmemunreachable requires a small interface to the allocator in order to collect information about active allocations.
+
+ - `malloc_disable()`: prevent any thread from mutating internal allocator state.
+ - `malloc enable()`: re-enable allocations in all threads.
+ - `malloc_iterate()`: call a callback on each active allocation in a given heap region.
+ - `malloc_backtrace()`: return the backtrace from when the allocation at the given address was allocated, if it was collected.
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
new file mode 100644
index 0000000..287f479
--- /dev/null
+++ b/libmemunreachable/ScopedAlarm.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_ALARM_H_
+#define LIBMEMUNREACHABLE_SCOPED_ALARM_H_
+
+#include <signal.h>
+#include <sys/time.h>
+
+#include <chrono>
+#include <functional>
+
+class ScopedAlarm {
+ public:
+ ScopedAlarm(std::chrono::microseconds us, std::function<void()> func) {
+ func_ = func;
+ struct sigaction oldact{};
+ struct sigaction act{};
+ act.sa_handler = [](int) {
+ ScopedAlarm::func_();
+ };
+ sigaction(SIGALRM, &act, &oldact);
+
+ std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
+ itimerval t = itimerval{};
+ t.it_value.tv_sec = s.count();
+ t.it_value.tv_usec = (us - s).count();
+ setitimer(ITIMER_REAL, &t, NULL);
+ }
+ ~ScopedAlarm() {
+ itimerval t = itimerval{};
+ setitimer(ITIMER_REAL, &t, NULL);
+ struct sigaction act{};
+ act.sa_handler = SIG_DFL;
+ sigaction(SIGALRM, &act, NULL);
+ }
+ private:
+ static std::function<void()> func_;
+};
+#endif
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
new file mode 100644
index 0000000..758d317
--- /dev/null
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+#define LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+
+#include <memory>
+
+#include "android-base/macros.h"
+
+#include "bionic.h"
+#include "log.h"
+#include "ScopedAlarm.h"
+
+class DisableMallocGuard{
+ public:
+ DisableMallocGuard() : disabled_(false){}
+ ~DisableMallocGuard() {
+ Enable();
+ }
+
+ void Disable() {
+ if (!disabled_) {
+ malloc_disable();
+ disabled_ = true;
+ }
+ }
+
+ void Enable() {
+ if (disabled_) {
+ malloc_enable();
+ disabled_ = false;
+ }
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DisableMallocGuard);
+ bool disabled_;
+};
+
+// Any calls to malloc or free from this thread will deadlock as long as this
+// object is in scope. Calls to malloc from other threads may succeed (for
+// example if the allocation is satisfied out of the thread's tcache), or may
+// block until the object is destroyed.
+//
+// Don't call fork() while malloc is disabled, it needs the same locks held
+// here.
+class ScopedDisableMalloc {
+ public:
+ ScopedDisableMalloc() {
+ disable_malloc_.Disable();
+ }
+
+ ~ScopedDisableMalloc() {
+ disable_malloc_.Enable();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableMalloc);
+ DisableMallocGuard disable_malloc_;
+};
+
+class ScopedDisableMallocTimeout {
+ public:
+ explicit ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) :
+ timeout_(timeout), timed_out_(false), disable_malloc_() {
+ Disable();
+ }
+
+ ~ScopedDisableMallocTimeout() {
+ Enable();
+ }
+
+ bool timed_out() {
+ return timed_out_;
+ }
+
+ void Enable() {
+ disable_malloc_.Enable();
+ alarm_ = nullptr;
+ }
+
+ void Disable() {
+ // set up the alarm before disabling malloc so unique_ptr can be used
+ alarm_ = std::make_unique<ScopedAlarm>(timeout_, [&]() {
+ disable_malloc_.Enable();
+ timed_out_ = true;
+ });
+
+ disable_malloc_.Disable();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableMallocTimeout);
+ std::chrono::milliseconds timeout_;
+ bool timed_out_;
+ std::unique_ptr<ScopedAlarm> alarm_;
+ DisableMallocGuard disable_malloc_;
+};
+
+#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
new file mode 100644
index 0000000..9beef9a
--- /dev/null
+++ b/libmemunreachable/ScopedPipe.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_PIPE_H_
+#define LIBMEMUNREACHABLE_SCOPED_PIPE_H_
+
+#include <unistd.h>
+
+#include "log.h"
+
+class ScopedPipe {
+ public:
+ ScopedPipe() : pipefd_{-1, -1} {
+ int ret = pipe2(pipefd_, O_CLOEXEC);
+ if (ret < 0) {
+ LOG_ALWAYS_FATAL("failed to open pipe");
+ }
+ }
+ ~ScopedPipe() {
+ Close();
+ }
+
+ ScopedPipe(ScopedPipe&& other) {
+ SetReceiver(other.ReleaseReceiver());
+ SetSender(other.ReleaseSender());
+ }
+
+ ScopedPipe& operator = (ScopedPipe&& other) {
+ SetReceiver(other.ReleaseReceiver());
+ SetSender(other.ReleaseSender());
+ return *this;
+ }
+
+ void CloseReceiver() {
+ close(ReleaseReceiver());
+ }
+
+ void CloseSender() {
+ close(ReleaseSender());
+ }
+
+ void Close() {
+ CloseReceiver();
+ CloseSender();
+ }
+
+ int Receiver() { return pipefd_[0]; }
+ int Sender() { return pipefd_[1]; }
+
+ int ReleaseReceiver() {
+ int ret = Receiver();
+ SetReceiver(-1);
+ return ret;
+ }
+
+ int ReleaseSender() {
+ int ret = Sender();
+ SetSender(-1);
+ return ret;
+ }
+
+ private:
+ void SetReceiver(int fd) { pipefd_[0] = fd; };
+ void SetSender(int fd) { pipefd_[1] = fd; };
+
+ int pipefd_[2];
+};
+#endif
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
new file mode 100644
index 0000000..1fd9d4d
--- /dev/null
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
+#define LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
+
+#include <errno.h>
+#include <signal.h>
+
+#include <functional>
+
+#include "android-base/macros.h"
+
+#include "log.h"
+
+class ScopedSignalHandler {
+ public:
+ using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
+
+ explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
+ ~ScopedSignalHandler() {
+ reset();
+ }
+
+ template <class F>
+ void install(int signal, F&& f) {
+ LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+
+ handler_ = SignalFn(std::allocator_arg, allocator_,
+ [=](int signal, siginfo_t* si, void* uctx) {
+ f(*this, signal, si, uctx);
+ });
+
+ struct sigaction act{};
+ act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
+ handler_(signal, si, uctx);
+ };
+ act.sa_flags = SA_SIGINFO;
+
+ int ret = sigaction(signal, &act, &old_act_);
+ if (ret < 0) {
+ LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
+ }
+
+ signal_ = signal;
+ }
+
+ void reset() {
+ if (signal_ != -1) {
+ int ret = sigaction(signal_, &old_act_, NULL);
+ if (ret < 0) {
+ ALOGE("failed to uninstall segfault handler");
+ }
+ handler_ = SignalFn{};
+ signal_ = -1;
+ }
+ }
+
+
+ private:
+ using SignalFn = std::function<void(int, siginfo_t*, void*)>;
+ DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
+ Allocator<Fn> allocator_;
+ int signal_;
+ struct sigaction old_act_;
+ // TODO(ccross): to support multiple ScopedSignalHandlers handler_ would need
+ // to be a static map of signals to handlers, but allocated with Allocator.
+ static SignalFn handler_;
+};
+
+#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
diff --git a/libmemunreachable/Semaphore.h b/libmemunreachable/Semaphore.h
new file mode 100644
index 0000000..6bcf4ea
--- /dev/null
+++ b/libmemunreachable/Semaphore.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SEMAPHORE_H_
+#define LIBMEMUNREACHABLE_SEMAPHORE_H_
+
+#include <chrono>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+class Semaphore {
+ public:
+ explicit Semaphore(int count = 0) : count_(count) {}
+ ~Semaphore() = default;
+
+ void Wait(std::chrono::milliseconds ms) {
+ std::unique_lock<std::mutex> lk(m_);
+ cv_.wait_for(lk, ms, [&]{
+ if (count_ > 0) {
+ count_--;
+ return true;
+ }
+ return false;
+ });
+ }
+ void Post() {
+ {
+ std::lock_guard<std::mutex> lk(m_);
+ count_++;
+ }
+ cv_.notify_one();
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Semaphore);
+
+ int count_;
+ std::mutex m_;
+ std::condition_variable cv_;
+};
+
+
+#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
new file mode 100644
index 0000000..2546341
--- /dev/null
+++ b/libmemunreachable/Tarjan.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Based on system/update_engine/payload_generator/tarjan.cc
+
+#ifndef LIBMEMUNREACHABLE_TARJAN_H_
+#define LIBMEMUNREACHABLE_TARJAN_H_
+
+#include <assert.h>
+#include <algorithm>
+
+#include "Allocator.h"
+
+template<class T>
+class Node {
+ public:
+ allocator::set<Node<T>*> references_in;
+ allocator::set<Node<T>*> references_out;
+ size_t index;
+ size_t lowlink;
+
+ T* ptr;
+
+ Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator),
+ ptr(ptr) {};
+ Node(Node&& rhs) = default;
+ void Edge(Node<T>* ref) {
+ references_out.emplace(ref);
+ ref->references_in.emplace(this);
+ }
+ template<class F>
+ void Foreach(F&& f) {
+ for (auto& node: references_out) {
+ f(node->ptr);
+ }
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Node<T>);
+};
+
+template<class T>
+using Graph = allocator::vector<Node<T>*>;
+
+template<class T>
+using SCC = allocator::vector<Node<T>*>;
+
+template<class T>
+using SCCList = allocator::vector<SCC<T>>;
+
+template<class T>
+class TarjanAlgorithm {
+ public:
+ explicit TarjanAlgorithm(Allocator<void> allocator) : index_(0),
+ stack_(allocator), components_(allocator) {}
+
+ void Execute(Graph<T>& graph, SCCList<T>& out);
+ private:
+ static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1);
+ void Tarjan(Node<T>* vertex, Graph<T>& graph);
+
+ size_t index_;
+ allocator::vector<Node<T>*> stack_;
+ SCCList<T> components_;
+};
+
+template<class T>
+void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) {
+ stack_.clear();
+ components_.clear();
+ index_ = 0;
+ for (auto& it: graph) {
+ it->index = UNDEFINED_INDEX;
+ it->lowlink = UNDEFINED_INDEX;
+ }
+
+ for (auto& it: graph) {
+ if (it->index == UNDEFINED_INDEX) {
+ Tarjan(it, graph);
+ }
+ }
+ out.swap(components_);
+}
+
+template<class T>
+void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) {
+ assert(vertex->index == UNDEFINED_INDEX);
+ vertex->index = index_;
+ vertex->lowlink = index_;
+ index_++;
+ stack_.push_back(vertex);
+ for (auto& it: vertex->references_out) {
+ Node<T>* vertex_next = it;
+ if (vertex_next->index == UNDEFINED_INDEX) {
+ Tarjan(vertex_next, graph);
+ vertex->lowlink = std::min(vertex->lowlink, vertex_next->lowlink);
+ } else if (std::find(stack_.begin(), stack_.end(), vertex_next) != stack_.end()) {
+ vertex->lowlink = std::min(vertex->lowlink, vertex_next->index);
+ }
+ }
+ if (vertex->lowlink == vertex->index) {
+ SCC<T> component{components_.get_allocator()};
+ Node<T>* other_vertex;
+ do {
+ other_vertex = stack_.back();
+ stack_.pop_back();
+ component.push_back(other_vertex);
+ } while (other_vertex != vertex && !stack_.empty());
+
+ components_.emplace_back(component);
+ }
+}
+
+template<class T>
+void Tarjan(Graph<T>& graph, SCCList<T>& out) {
+ TarjanAlgorithm<T> tarjan{graph.get_allocator()};
+ tarjan.Execute(graph, out);
+}
+
+#endif // LIBMEMUNREACHABLE_TARJAN_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
new file mode 100644
index 0000000..9155c29
--- /dev/null
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ThreadCapture.h"
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "Allocator.h"
+#include "log.h"
+
+// bionic interfaces used:
+// atoi
+// strlcat
+// writev
+
+// bionic interfaces reimplemented to avoid allocation:
+// getdents64
+
+// Convert a pid > 0 to a string. sprintf might allocate, so we can't use it.
+// Returns a pointer somewhere in buf to a null terminated string, or NULL
+// on error.
+static char *pid_to_str(char *buf, size_t len, pid_t pid) {
+ if (pid <= 0) {
+ return nullptr;
+ }
+
+ char *ptr = buf + len - 1;
+ *ptr = 0;
+ while (pid > 0) {
+ ptr--;
+ if (ptr < buf) {
+ return nullptr;
+ }
+ *ptr = '0' + (pid % 10);
+ pid /= 10;
+ }
+
+ return ptr;
+}
+
+class ThreadCaptureImpl {
+ public:
+ ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator);
+ ~ThreadCaptureImpl() {}
+ bool ListThreads(TidList& tids);
+ bool CaptureThreads();
+ bool ReleaseThreads();
+ bool ReleaseThread(pid_t tid);
+ bool CapturedThreadInfo(ThreadInfoList& threads);
+ void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
+ private:
+ int CaptureThread(pid_t tid);
+ bool ReleaseThread(pid_t tid, unsigned int signal);
+ int PtraceAttach(pid_t tid);
+ void PtraceDetach(pid_t tid, unsigned int signal);
+ bool PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info);
+
+ allocator::map<pid_t, unsigned int> captured_threads_;
+ Allocator<ThreadCaptureImpl> allocator_;
+ pid_t pid_;
+ std::function<void(pid_t)> inject_test_func_;
+};
+
+ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) :
+ captured_threads_(allocator), allocator_(allocator), pid_(pid) {
+}
+
+bool ThreadCaptureImpl::ListThreads(TidList& tids) {
+ tids.clear();
+
+ char pid_buf[11];
+ char path[256] = "/proc/";
+ char* pid_str = pid_to_str(pid_buf, sizeof(pid_buf), pid_);
+ if (!pid_str) {
+ return false;
+ }
+ strlcat(path, pid_str, sizeof(path));
+ strlcat(path, "/task", sizeof(path));
+
+ android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
+ if (fd == -1) {
+ ALOGE("failed to open %s: %s", path, strerror(errno));
+ return false;
+ }
+
+ struct linux_dirent64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ uint16_t d_reclen;
+ char d_type;
+ char d_name[];
+ } __attribute((packed));
+ char dirent_buf[4096];
+ ssize_t nread;
+ do {
+ nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
+ if (nread < 0) {
+ ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+ return false;
+ } else if (nread > 0) {
+ ssize_t off = 0;
+ while (off < nread) {
+ linux_dirent64* dirent = reinterpret_cast<linux_dirent64*>(dirent_buf + off);
+ off += dirent->d_reclen;
+ pid_t tid = atoi(dirent->d_name);
+ if (tid <= 0) {
+ continue;
+ }
+ tids.push_back(tid);
+ }
+ }
+
+ } while (nread != 0);
+
+ return true;
+}
+
+bool ThreadCaptureImpl::CaptureThreads() {
+ TidList tids{allocator_};
+
+ bool found_new_thread;
+ do {
+ if (!ListThreads(tids)) {
+ ReleaseThreads();
+ return false;
+ }
+
+ found_new_thread = false;
+
+ for (auto it = tids.begin(); it != tids.end(); it++) {
+ auto captured = captured_threads_.find(*it);
+ if (captured == captured_threads_.end()) {
+ if (CaptureThread(*it) < 0) {
+ ReleaseThreads();
+ return false;
+ }
+ found_new_thread = true;
+ }
+ }
+ } while (found_new_thread);
+
+ return true;
+}
+
+// Detatches from a thread, delivering signal if nonzero, logs on error
+void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
+ void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
+ if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
+ ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ }
+}
+
+// Attaches to and pauses thread.
+// Returns 1 on attach, 0 on tid not found, -1 and logs on error
+int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
+ int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
+ if (ret < 0) {
+ ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ return -1;
+ }
+
+ if (inject_test_func_) {
+ inject_test_func_(tid);
+ }
+
+ if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) < 0) {
+ if (errno == ESRCH) {
+ return 0;
+ } else {
+ ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ PtraceDetach(tid, 0);
+ return -1;
+ }
+ }
+ return 1;
+}
+
+bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
+ thread_info.tid = tid;
+
+ const unsigned int max_num_regs = 128; // larger than number of registers on any device
+ uintptr_t regs[max_num_regs];
+ struct iovec iovec;
+ iovec.iov_base = ®s;
+ iovec.iov_len = sizeof(regs);
+
+ if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
+ ALOGE("ptrace getregset for thread %d of process %d failed: %s",
+ tid, pid_, strerror(errno));
+ return false;
+ }
+
+ unsigned int num_regs = iovec.iov_len / sizeof(uintptr_t);
+ thread_info.regs.assign(®s[0], ®s[num_regs]);
+
+ const int sp =
+#if defined(__x86_64__)
+ offsetof(struct pt_regs, rsp) / sizeof(uintptr_t)
+#elif defined(__i386__)
+ offsetof(struct pt_regs, esp) / sizeof(uintptr_t)
+#elif defined(__arm__)
+ offsetof(struct pt_regs, ARM_sp) / sizeof(uintptr_t)
+#elif defined(__aarch64__)
+ offsetof(struct user_pt_regs, sp) / sizeof(uintptr_t)
+#elif defined(__mips__) || defined(__mips64__)
+ offsetof(struct pt_regs, regs[29]) / sizeof(uintptr_t)
+#else
+#error Unrecognized architecture
+#endif
+ ;
+
+ // TODO(ccross): use /proc/tid/status or /proc/pid/maps to get start_stack
+
+ thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
+
+ return true;
+}
+
+int ThreadCaptureImpl::CaptureThread(pid_t tid) {
+ int ret = PtraceAttach(tid);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ int status = 0;
+ if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
+ ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ PtraceDetach(tid, 0);
+ return -1;
+ }
+
+ if (!WIFSTOPPED(status)) {
+ ALOGE("thread %d of process %d was not paused after waitpid, killed?",
+ tid, pid_);
+ return 0;
+ }
+
+ unsigned int resume_signal = 0;
+
+ unsigned int signal = WSTOPSIG(status);
+ if ((status >> 16) == PTRACE_EVENT_STOP) {
+ switch (signal) {
+ case SIGSTOP:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ // group-stop signals
+ break;
+ case SIGTRAP:
+ // normal ptrace interrupt stop
+ break;
+ default:
+ ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
+ signal, tid, pid_);
+ return -1;
+ }
+ } else {
+ // signal-delivery-stop
+ resume_signal = signal;
+ }
+
+ captured_threads_[tid] = resume_signal;
+ return 1;
+}
+
+bool ThreadCaptureImpl::ReleaseThread(pid_t tid) {
+ auto it = captured_threads_.find(tid);
+ if (it == captured_threads_.end()) {
+ return false;
+ }
+ return ReleaseThread(it->first, it->second);
+}
+
+bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) {
+ PtraceDetach(tid, signal);
+ return true;
+}
+
+bool ThreadCaptureImpl::ReleaseThreads() {
+ bool ret = true;
+ for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) {
+ if (ReleaseThread(it->first, it->second)) {
+ it = captured_threads_.erase(it);
+ } else {
+ it++;
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+bool ThreadCaptureImpl::CapturedThreadInfo(ThreadInfoList& threads) {
+ threads.clear();
+
+ for (auto it = captured_threads_.begin(); it != captured_threads_.end(); it++) {
+ ThreadInfo t{0, allocator::vector<uintptr_t>(allocator_), std::pair<uintptr_t, uintptr_t>(0, 0)};
+ if (!PtraceThreadInfo(it->first, t)) {
+ return false;
+ }
+ threads.push_back(t);
+ }
+ return true;
+}
+
+ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) {
+ Allocator<ThreadCaptureImpl> impl_allocator = allocator;
+ impl_ = impl_allocator.make_unique(pid, impl_allocator);
+}
+
+ThreadCapture::~ThreadCapture() {}
+
+bool ThreadCapture::ListThreads(TidList& tids) {
+ return impl_->ListThreads(tids);
+}
+
+bool ThreadCapture::CaptureThreads() {
+ return impl_->CaptureThreads();
+}
+
+bool ThreadCapture::ReleaseThreads() {
+ return impl_->ReleaseThreads();
+}
+
+bool ThreadCapture::ReleaseThread(pid_t tid) {
+ return impl_->ReleaseThread(tid);
+}
+
+bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) {
+ return impl_->CapturedThreadInfo(threads);
+}
+
+void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
+ impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
+}
diff --git a/libmemunreachable/ThreadCapture.h b/libmemunreachable/ThreadCapture.h
new file mode 100644
index 0000000..1022cad
--- /dev/null
+++ b/libmemunreachable/ThreadCapture.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_THREAD_CAPTURE_H_
+#define LIBMEMUNREACHABLE_THREAD_CAPTURE_H_
+
+#include <utility>
+
+#include "Allocator.h"
+
+struct ThreadInfo {
+ pid_t tid;
+ allocator::vector<uintptr_t> regs;
+ std::pair<uintptr_t, uintptr_t> stack;
+};
+
+using TidList = allocator::vector<pid_t>;
+using ThreadInfoList = allocator::vector<ThreadInfo>;
+
+class ThreadCaptureImpl;
+
+class ThreadCapture {
+public:
+ ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator);
+ ~ThreadCapture();
+
+ bool ListThreads(TidList& tids);
+ bool CaptureThreads();
+ bool ReleaseThreads();
+ bool ReleaseThread(pid_t tid);
+ bool CapturedThreadInfo(ThreadInfoList& threads);
+ void InjectTestFunc(std::function<void(pid_t)>&& f);
+
+private:
+ ThreadCapture(const ThreadCapture&) = delete;
+ void operator=(const ThreadCapture&) = delete;
+
+ Allocator<ThreadCaptureImpl>::unique_ptr impl_;
+};
+
+#endif
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h
new file mode 100644
index 0000000..1e4ade1
--- /dev/null
+++ b/libmemunreachable/anon_vma_naming.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+#define LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+
+#include <sys/prctl.h>
+
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+
+#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
new file mode 100644
index 0000000..83d07a8
--- /dev/null
+++ b/libmemunreachable/bionic.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_BIONIC_H_
+#define LIBMEMUNREACHABLE_BIONIC_H_
+
+#include <sys/cdefs.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+__BEGIN_DECLS
+
+/* Exported from bionic */
+extern void malloc_disable();
+extern void malloc_enable();
+extern int malloc_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+extern ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
+
+__END_DECLS
+
+#endif // LIBMEMUNREACHABLE_BIONIC_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
new file mode 100644
index 0000000..9b227fd
--- /dev/null
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+#define LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+
+#include <sys/cdefs.h>
+
+#ifdef __cplusplus
+
+#include <vector>
+#include <string>
+
+struct Leak {
+ uintptr_t begin;
+ size_t size;
+
+ size_t referenced_count;
+ size_t referenced_size;
+
+ size_t similar_count;
+ size_t similar_size;
+ size_t similar_referenced_count;
+ size_t similar_referenced_size;
+
+ size_t total_size;
+
+ static const size_t contents_length = 32;
+ char contents[contents_length];
+
+ struct Backtrace {
+ size_t num_frames;
+
+ static const size_t max_frames = 16;
+ uintptr_t frames[max_frames];
+ } backtrace;
+
+ std::string ToString(bool log_contents) const;
+};
+
+struct UnreachableMemoryInfo {
+ std::vector<Leak> leaks;
+ size_t num_leaks;
+ size_t leak_bytes;
+ size_t num_allocations;
+ size_t allocation_bytes;
+
+ 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::string ToString(bool log_contents) const;
+};
+
+bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100);
+
+std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100);
+
+#endif
+
+__BEGIN_DECLS
+
+bool LogUnreachableMemory(bool log_contents, size_t limit);
+
+bool NoLeaks();
+
+__END_DECLS
+
+#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
new file mode 100644
index 0000000..cdfbfd9
--- /dev/null
+++ b/libmemunreachable/log.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LOG_H_
+#define LIBMEMUNREACHABLE_LOG_H_
+
+#define LOG_TAG "libmemunreachable"
+
+#include <log/log.h>
+
+#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
new file mode 100644
index 0000000..21c8218
--- /dev/null
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <Allocator.h>
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+
+
+std::function<void()> ScopedAlarm::func_;
+
+class AllocatorTest : public testing::Test {
+ protected:
+ AllocatorTest() : heap(), disable_malloc_() {}
+ virtual void SetUp() {
+ heap_count = 0;
+ }
+ virtual void TearDown() {
+ ASSERT_EQ(heap_count, 0);
+ ASSERT_TRUE(heap.empty());
+ ASSERT_FALSE(disable_malloc_.timed_out());
+ }
+ Heap heap;
+ private:
+ ScopedDisableMallocTimeout disable_malloc_;
+};
+
+TEST_F(AllocatorTest, simple) {
+ Allocator<char[100]> allocator(heap);
+ void *ptr = allocator.allocate();
+ ASSERT_TRUE(ptr != NULL);
+ allocator.deallocate(ptr);
+}
+
+TEST_F(AllocatorTest, multiple) {
+ Allocator<char[100]> allocator(heap);
+ void *ptr1 = allocator.allocate();
+ ASSERT_TRUE(ptr1 != NULL);
+ void *ptr2 = allocator.allocate();
+ ASSERT_TRUE(ptr2 != NULL);
+ ASSERT_NE(ptr1, ptr2);
+ allocator.deallocate(ptr1);
+ void *ptr3 = allocator.allocate();
+ ASSERT_EQ(ptr1, ptr3);
+ allocator.deallocate(ptr3);
+ allocator.deallocate(ptr2);
+}
+
+TEST_F(AllocatorTest, many) {
+ const int num = 4096;
+ const int size = 128;
+ Allocator<char[size]> allocator(heap);
+ void *ptr[num];
+ for (int i = 0; i < num; i++) {
+ ptr[i] = allocator.allocate();
+ memset(ptr[i], 0xaa, size);
+ *(reinterpret_cast<unsigned char*>(ptr[i])) = i;
+ }
+
+ for (int i = 0; i < num; i++) {
+ for (int j = 0; j < num; j++) {
+ if (i != j) {
+ ASSERT_NE(ptr[i], ptr[j]);
+ }
+ }
+ }
+
+ for (int i = 0; i < num; i++) {
+ ASSERT_EQ(*(reinterpret_cast<unsigned char*>(ptr[i])), i & 0xFF);
+ allocator.deallocate(ptr[i]);
+ }
+}
+
+TEST_F(AllocatorTest, large) {
+ const size_t size = 1024 * 1024;
+ Allocator<char[size]> allocator(heap);
+ void *ptr = allocator.allocate();
+ memset(ptr, 0xaa, size);
+ allocator.deallocate(ptr);
+}
+
+TEST_F(AllocatorTest, many_large) {
+ const int num = 128;
+ const int size = 1024 * 1024;
+ Allocator<char[size]> allocator(heap);
+ void *ptr[num];
+ for (int i = 0; i < num; i++) {
+ ptr[i] = allocator.allocate();
+ memset(ptr[i], 0xaa, size);
+ *(reinterpret_cast<unsigned char*>(ptr[i])) = i;
+ }
+
+ for (int i = 0; i < num; i++) {
+ ASSERT_EQ(*(reinterpret_cast<unsigned char*>(ptr[i])), i & 0xFF);
+ allocator.deallocate(ptr[i]);
+ }
+}
+
+TEST_F(AllocatorTest, copy) {
+ Allocator<char[100]> a(heap);
+ Allocator<char[200]> b = a;
+ Allocator<char[300]> c(b);
+ Allocator<char[100]> d(a);
+ Allocator<char[100]> e(heap);
+
+ ASSERT_EQ(a, b);
+ ASSERT_EQ(a, c);
+ ASSERT_EQ(a, d);
+ ASSERT_EQ(a, e);
+
+ void* ptr1 = a.allocate();
+ void* ptr2 = b.allocate();
+ void* ptr3 = c.allocate();
+ void* ptr4 = d.allocate();
+
+ b.deallocate(ptr1);
+ d.deallocate(ptr2);
+ a.deallocate(ptr3);
+ c.deallocate(ptr4);
+}
+
+TEST_F(AllocatorTest, stl_vector) {
+ auto v = allocator::vector<int>(Allocator<int>(heap));
+ for (int i = 0; i < 1024; i++) {
+ v.push_back(i);
+ }
+ for (int i = 0; i < 1024; i++) {
+ ASSERT_EQ(v[i], i);
+ }
+ v.clear();
+}
+
+TEST_F(AllocatorTest, stl_list) {
+ auto v = allocator::list<int>(Allocator<int>(heap));
+ for (int i = 0; i < 1024; i++) {
+ v.push_back(i);
+ }
+ int i = 0;
+ for (auto iter = v.begin(); iter != v.end(); iter++, i++) {
+ ASSERT_EQ(*iter, i);
+ }
+ v.clear();
+}
+
+TEST_F(AllocatorTest, shared) {
+ Allocator<int> allocator(heap);
+
+ Allocator<int>::shared_ptr ptr = allocator.make_shared(0);
+ {
+ auto ptr2 = ptr; // NOLINT, test copy of ptr
+ }
+ ASSERT_NE(ptr, nullptr);
+}
+
+TEST_F(AllocatorTest, unique) {
+ Allocator<int> allocator(heap);
+
+ Allocator<int>::unique_ptr ptr = allocator.make_unique(0);
+
+ ASSERT_NE(ptr, nullptr);
+}
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
new file mode 100644
index 0000000..ea5c22c
--- /dev/null
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/time.h>
+
+#include <chrono>
+#include <functional>
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+
+using namespace std::chrono_literals;
+
+class DisableMallocTest : public ::testing::Test {
+ protected:
+ void alarm(std::chrono::microseconds us) {
+ std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
+ itimerval t = itimerval();
+ t.it_value.tv_sec = s.count();
+ t.it_value.tv_usec = (us - s).count();
+ setitimer(ITIMER_REAL, &t, NULL);
+ }
+};
+
+TEST_F(DisableMallocTest, reenable) {
+ ASSERT_EXIT({
+ alarm(100ms);
+ void *ptr1 = malloc(128);
+ ASSERT_NE(ptr1, nullptr);
+ free(ptr1);
+ {
+ ScopedDisableMalloc disable_malloc;
+ }
+ void *ptr2 = malloc(128);
+ ASSERT_NE(ptr2, nullptr);
+ free(ptr2);
+ _exit(1);
+ }, ::testing::ExitedWithCode(1), "");
+}
+
+TEST_F(DisableMallocTest, deadlock_allocate) {
+ ASSERT_DEATH({
+ void *ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_new) {
+ ASSERT_DEATH({
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ delete(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ delete(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_delete) {
+ ASSERT_DEATH({
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(250ms);
+ ScopedDisableMalloc disable_malloc;
+ delete(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_free) {
+ ASSERT_DEATH({
+ void *ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ free(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_fork) {
+ ASSERT_DEATH({
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ fork();
+ }
+ }, "");
+}
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
new file mode 100644
index 0000000..98e4aa1
--- /dev/null
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "HeapWalker.h"
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+#include "Allocator.h"
+
+class HeapWalkerTest : public ::testing::Test {
+ public:
+ HeapWalkerTest() : disable_malloc_(), heap_() {}
+
+ void TearDown() {
+ ASSERT_TRUE(heap_.empty());
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc_.timed_out());
+ }
+ }
+
+ protected:
+ ScopedDisableMallocTimeout disable_malloc_;
+ Heap heap_;
+};
+
+TEST_F(HeapWalkerTest, allocation) {
+ HeapWalker heap_walker(heap_);
+ ASSERT_TRUE(heap_walker.Allocation(3, 4));
+ ASSERT_TRUE(heap_walker.Allocation(2, 3));
+ ASSERT_TRUE(heap_walker.Allocation(4, 5));
+ ASSERT_TRUE(heap_walker.Allocation(6, 7));
+ ASSERT_TRUE(heap_walker.Allocation(0, 1));
+}
+
+TEST_F(HeapWalkerTest, overlap) {
+ HeapWalker heap_walker(heap_);
+ ASSERT_TRUE(heap_walker.Allocation(2, 3));
+ ASSERT_TRUE(heap_walker.Allocation(3, 4));
+ ASSERT_FALSE(heap_walker.Allocation(2, 3));
+ ASSERT_FALSE(heap_walker.Allocation(1, 3));
+ ASSERT_FALSE(heap_walker.Allocation(1, 4));
+ ASSERT_FALSE(heap_walker.Allocation(1, 5));
+ ASSERT_FALSE(heap_walker.Allocation(3, 4));
+ ASSERT_FALSE(heap_walker.Allocation(3, 5));
+ ASSERT_TRUE(heap_walker.Allocation(4, 5));
+ ASSERT_TRUE(heap_walker.Allocation(1, 2));
+}
+
+TEST_F(HeapWalkerTest, zero) {
+ HeapWalker heap_walker(heap_);
+ ASSERT_TRUE(heap_walker.Allocation(2, 2));
+ ASSERT_FALSE(heap_walker.Allocation(2, 2));
+ ASSERT_TRUE(heap_walker.Allocation(3, 3));
+ ASSERT_TRUE(heap_walker.Allocation(1, 1));
+ ASSERT_FALSE(heap_walker.Allocation(2, 3));
+}
+
+#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(buffer)
+#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(buffer) + sizeof(buffer))
+
+TEST_F(HeapWalkerTest, leak) {
+ void* buffer1[16]{};
+ char buffer2[16]{};
+ buffer1[0] = &buffer2[0] - sizeof(void*);
+ buffer1[1] = &buffer2[15] + sizeof(void*);
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(1U, num_leaks);
+ EXPECT_EQ(16U, leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(buffer_begin(buffer2), leaked[0].begin);
+ EXPECT_EQ(buffer_end(buffer2), leaked[0].end);
+}
+
+TEST_F(HeapWalkerTest, live) {
+ const int from_buffer_entries = 4;
+ const int to_buffer_bytes = 16;
+
+ for (int i = 0; i < from_buffer_entries; i++) {
+ for (int j = 0; j < to_buffer_bytes; j++) {
+ void* buffer1[from_buffer_entries]{};
+ char buffer2[to_buffer_bytes]{};
+ buffer1[i] = &buffer2[j];
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+ heap_walker.Root(buffer_begin(buffer1), buffer_end(buffer1));
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = SIZE_MAX;
+ size_t leaked_bytes = SIZE_MAX;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(0U, num_leaks);
+ EXPECT_EQ(0U, leaked_bytes);
+ EXPECT_EQ(0U, leaked.size());
+ }
+ }
+}
+
+TEST_F(HeapWalkerTest, unaligned) {
+ const int from_buffer_entries = 4;
+ const int to_buffer_bytes = 16;
+ void* buffer1[from_buffer_entries]{};
+ char buffer2[to_buffer_bytes]{};
+
+ buffer1[1] = &buffer2;
+
+ for (unsigned int i = 0; i < sizeof(uintptr_t); i++) {
+ for (unsigned int j = 0; j < sizeof(uintptr_t); j++) {
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+ heap_walker.Root(buffer_begin(buffer1) + i, buffer_end(buffer1) - j);
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = SIZE_MAX;
+ size_t leaked_bytes = SIZE_MAX;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(0U, num_leaks);
+ EXPECT_EQ(0U, leaked_bytes);
+ EXPECT_EQ(0U, leaked.size());
+ }
+ }
+}
+
+TEST_F(HeapWalkerTest, cycle) {
+ void* buffer1;
+ void* buffer2;
+
+ buffer1 = &buffer2;
+ buffer2 = &buffer1;
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer1), buffer_end(buffer1));
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(2U, num_leaks);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+}
+
+TEST_F(HeapWalkerTest, segv) {
+ const size_t page_size = sysconf(_SC_PAGE_SIZE);
+ void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ ASSERT_NE(buffer1, nullptr);
+ void* buffer2;
+
+ buffer2 = &buffer1;
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1)+page_size);
+ heap_walker.Root(buffer_begin(buffer2), buffer_end(buffer2));
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(0U, num_leaks);
+ EXPECT_EQ(0U, leaked_bytes);
+ ASSERT_EQ(0U, leaked.size());
+}
diff --git a/libmemunreachable/tests/HostMallocStub.cpp b/libmemunreachable/tests/HostMallocStub.cpp
new file mode 100644
index 0000000..a7e3f07
--- /dev/null
+++ b/libmemunreachable/tests/HostMallocStub.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bionic.h"
+
+void malloc_disable() {
+}
+
+void malloc_enable() {
+}
diff --git a/libmemunreachable/tests/LeakFolding_test.cpp b/libmemunreachable/tests/LeakFolding_test.cpp
new file mode 100644
index 0000000..e85df5f
--- /dev/null
+++ b/libmemunreachable/tests/LeakFolding_test.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HeapWalker.h"
+#include "LeakFolding.h"
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+#include "Allocator.h"
+
+class LeakFoldingTest : public ::testing::Test {
+ public:
+ LeakFoldingTest() : disable_malloc_(), heap_() {}
+
+ void TearDown() {
+ ASSERT_TRUE(heap_.empty());
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc_.timed_out());
+ }
+ }
+
+ protected:
+ ScopedDisableMallocTimeout disable_malloc_;
+ Heap heap_;
+};
+
+#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(&(buffer)[0])
+#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(&(buffer)[0]) + sizeof(buffer))
+#define ALLOCATION(heap_walker, buffer) \
+ ASSERT_EQ(true, (heap_walker).Allocation(buffer_begin(buffer), buffer_end(buffer)))
+
+TEST_F(LeakFoldingTest, one) {
+ void* buffer1[1] = {nullptr};
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(1U, num_leaks);
+ EXPECT_EQ(sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(0U, leaked[0].referenced_count);
+ EXPECT_EQ(0U, leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two) {
+ void* buffer1[1] = {nullptr};
+ void* buffer2[1] = {nullptr};
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(2U, num_leaks);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+ EXPECT_EQ(0U, leaked[0].referenced_count);
+ EXPECT_EQ(0U, leaked[0].referenced_size);
+ EXPECT_EQ(0U, leaked[1].referenced_count);
+ EXPECT_EQ(0U, leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, dominator) {
+ void* buffer1[1];
+ void* buffer2[1] = {nullptr};
+
+ buffer1[0] = buffer2;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(2U, num_leaks);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(1U, leaked[0].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, cycle) {
+ void* buffer1[1];
+ void* buffer2[1];
+ void* buffer3[1];
+
+ buffer1[0] = buffer2;
+ buffer2[0] = buffer3;
+ buffer3[0] = buffer2;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(3U, num_leaks);
+ EXPECT_EQ(3*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(2U, leaked[0].referenced_count);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, dominator_cycle) {
+ void* buffer1[2] = {nullptr, nullptr};
+ void* buffer2[2];
+ void* buffer3[1] = {nullptr};
+
+ buffer1[0] = &buffer2;
+ buffer2[0] = &buffer1;
+ buffer2[1] = &buffer3;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(3U, num_leaks);
+ EXPECT_EQ(5*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+
+ EXPECT_EQ(2U, leaked[0].referenced_count);
+ EXPECT_EQ(3*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2U, leaked[1].referenced_count);
+ EXPECT_EQ(3*sizeof(uintptr_t), leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two_cycles) {
+ void* buffer1[1];
+ void* buffer2[1];
+ void* buffer3[1];
+ void* buffer4[1];
+ void* buffer5[1];
+ void* buffer6[1];
+
+ buffer1[0] = buffer3;
+ buffer2[0] = buffer5;
+ buffer3[0] = buffer4;
+ buffer4[0] = buffer3;
+ buffer5[0] = buffer6;
+ buffer6[0] = buffer5;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+ ALLOCATION(heap_walker, buffer5);
+ ALLOCATION(heap_walker, buffer6);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(6U, num_leaks);
+ EXPECT_EQ(6*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+ EXPECT_EQ(2U, leaked[0].referenced_count);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2U, leaked[1].referenced_count);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two_dominator_cycles) {
+ void* buffer1[1];
+ void* buffer2[1];
+ void* buffer3[1];
+ void* buffer4[1];
+
+ buffer1[0] = buffer2;
+ buffer2[0] = buffer1;
+ buffer3[0] = buffer4;
+ buffer4[0] = buffer3;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(4U, num_leaks);
+ EXPECT_EQ(4*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(4U, leaked.size());
+ EXPECT_EQ(1U, leaked[0].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(1U, leaked[1].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(1U, leaked[2].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[2].referenced_size);
+ EXPECT_EQ(1U, leaked[3].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[3].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, giant_dominator_cycle) {
+ const size_t n = 1000;
+ void* buffer[n];
+
+ HeapWalker heap_walker(heap_);
+
+ for (size_t i = 0; i < n; i ++) {
+ ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
+ reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ }
+
+ for (size_t i = 0; i < n - 1; i++) {
+ buffer[i] = &buffer[i+1];
+ }
+ buffer[n - 1] = &buffer[0];
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(n, num_leaks);
+ EXPECT_EQ(n * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1000U, leaked.size());
+ EXPECT_EQ(n - 1, leaked[0].referenced_count);
+ EXPECT_EQ((n - 1) * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, giant_cycle) {
+ const size_t n = 1000;
+ void* buffer[n];
+ void* buffer1[1];
+
+ HeapWalker heap_walker(heap_);
+
+ for (size_t i = 0; i < n - 1; i++) {
+ buffer[i] = &buffer[i+1];
+ }
+ buffer[n - 1] = &buffer[0];
+
+ buffer1[0] = &buffer[0];
+
+ for (size_t i = 0; i < n; i ++) {
+ ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
+ reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ }
+
+ ALLOCATION(heap_walker, buffer1);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(n + 1, num_leaks);
+ EXPECT_EQ((n + 1) * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(n, leaked[0].referenced_count);
+ EXPECT_EQ(n * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, multipath) {
+ void* buffer1[2];
+ void* buffer2[1];
+ void* buffer3[1];
+ void* buffer4[1] = {nullptr};
+
+ // 1
+ // / \
+ // v v
+ // 2 3
+ // \ /
+ // v
+ // 4
+
+ buffer1[0] = &buffer2;
+ buffer1[1] = &buffer3;
+ buffer2[0] = &buffer4;
+ buffer3[0] = &buffer4;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(4U, num_leaks);
+ EXPECT_EQ(5 * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(3U, leaked[0].referenced_count);
+ EXPECT_EQ(3 * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, multicycle) {
+ void* buffer1[2]{};
+ void* buffer2[2]{};
+ void* buffer3[2]{};
+ void* buffer4[2]{};
+
+ // 1
+ // / ^
+ // v \
+ // 2 -> 3
+ // \ ^
+ // v /
+ // 4
+
+ buffer1[0] = &buffer2;
+ buffer2[0] = &buffer3;
+ buffer2[1] = &buffer4;
+ buffer3[0] = &buffer1;
+ buffer4[0] = &buffer3;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(4U, num_leaks);
+ EXPECT_EQ(8 * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(4U, leaked.size());
+ EXPECT_EQ(3U, leaked[0].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(3U, leaked[1].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(3U, leaked[2].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[2].referenced_size);
+ EXPECT_EQ(3U, leaked[3].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[3].referenced_size);
+}
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
new file mode 100644
index 0000000..2ae3db8
--- /dev/null
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#include <gtest/gtest.h>
+
+#include <memunreachable/memunreachable.h>
+
+void* ptr;
+
+class HiddenPointer {
+ public:
+ explicit HiddenPointer(size_t size = 256) {
+ Set(malloc(size));
+ }
+ ~HiddenPointer() {
+ Free();
+ }
+ void* Get() {
+ return reinterpret_cast<void*>(~ptr_);
+ }
+ void Free() {
+ free(Get());
+ Set(nullptr);
+ }
+ private:
+ void Set(void* ptr) {
+ ptr_ = ~reinterpret_cast<uintptr_t>(ptr);
+ }
+ volatile uintptr_t ptr_;
+};
+
+static void Ref(void* ptr) {
+ write(0, ptr, 0);
+}
+
+TEST(MemunreachableTest, clean) {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+}
+
+TEST(MemunreachableTest, stack) {
+ HiddenPointer hidden_ptr;
+
+ {
+ void* ptr = hidden_ptr.Get();
+ Ref(ptr);
+
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+
+ Ref(ptr);
+ }
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, global) {
+ HiddenPointer hidden_ptr;
+
+ ptr = hidden_ptr.Get();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+
+ ptr = NULL;
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, tls) {
+ HiddenPointer hidden_ptr;
+ pthread_key_t key;
+ pthread_key_create(&key, NULL);
+
+ pthread_setspecific(key, hidden_ptr.Get());
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+
+ pthread_setspecific(key, nullptr);
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+
+ pthread_key_delete(key);
+}
+
+TEST(MemunreachableTest, twice) {
+ HiddenPointer hidden_ptr;
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, log) {
+ HiddenPointer hidden_ptr;
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, notdumpable) {
+ ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0));
+
+ HiddenPointer hidden_ptr;
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+ ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1));
+}
+
+TEST(MemunreachableTest, leak_lots) {
+ std::vector<HiddenPointer> hidden_ptrs;
+ hidden_ptrs.resize(1024);
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+}
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
new file mode 100644
index 0000000..41ed84e
--- /dev/null
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ThreadCapture.h"
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include "Allocator.h"
+#include "ScopedDisableMalloc.h"
+#include "ScopedPipe.h"
+
+using namespace std::chrono_literals;
+
+class ThreadListTest : public ::testing::TestWithParam<int> {
+ public:
+ ThreadListTest() : stop_(false) {}
+
+ ~ThreadListTest() {
+ // pthread_join may return before the entry in /proc/pid/task/ is gone,
+ // loop until ListThreads only finds the main thread so the next test
+ // doesn't fail.
+ WaitForThreads();
+ }
+
+ virtual void TearDown() {
+ ASSERT_TRUE(heap.empty());
+ }
+
+ protected:
+ template<class Function>
+ void StartThreads(unsigned int threads, Function&& func) {
+ threads_.reserve(threads);
+ tids_.reserve(threads);
+ for (unsigned int i = 0; i < threads; i++) {
+ threads_.emplace_back([&, i, threads, this]() {
+ {
+ std::lock_guard<std::mutex> lk(m_);
+ tids_.push_back(gettid());
+ if (tids_.size() == threads) {
+ cv_start_.notify_one();
+ }
+ }
+
+ func();
+
+ {
+ std::unique_lock<std::mutex> lk(m_);
+ cv_stop_.wait(lk, [&] {return stop_;});
+ }
+ });
+ }
+
+ {
+ std::unique_lock<std::mutex> lk(m_);
+ cv_start_.wait(lk, [&]{ return tids_.size() == threads; });
+ }
+ }
+
+ void StopThreads() {
+ {
+ std::lock_guard<std::mutex> lk(m_);
+ stop_ = true;
+ }
+ cv_stop_.notify_all();
+
+ for (auto i = threads_.begin(); i != threads_.end(); i++) {
+ i->join();
+ }
+ threads_.clear();
+ tids_.clear();
+ }
+
+ std::vector<pid_t>& tids() {
+ return tids_;
+ }
+
+ Heap heap;
+
+ private:
+ void WaitForThreads() {
+ auto tids = TidList{heap};
+ ThreadCapture thread_capture{getpid(), heap};
+
+ for (unsigned int i = 0; i < 100; i++) {
+ EXPECT_TRUE(thread_capture.ListThreads(tids));
+ if (tids.size() == 1) {
+ break;
+ }
+ std::this_thread::sleep_for(10ms);
+ }
+ EXPECT_EQ(1U, tids.size());
+ }
+
+ std::mutex m_;
+ std::condition_variable cv_start_;
+ std::condition_variable cv_stop_;
+ bool stop_;
+ std::vector<pid_t> tids_;
+
+ std::vector<std::thread> threads_;
+};
+
+TEST_F(ThreadListTest, list_one) {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(getpid(), heap);
+
+ auto expected_tids = allocator::vector<pid_t>(1, getpid(), heap);
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+
+ ASSERT_EQ(expected_tids, list_tids);
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+}
+
+TEST_P(ThreadListTest, list_some) {
+ const unsigned int threads = GetParam() - 1;
+
+ StartThreads(threads, [](){});
+ std::vector<pid_t> expected_tids = tids();
+ expected_tids.push_back(getpid());
+
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(getpid(), heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+
+ StopThreads();
+
+ std::sort(list_tids.begin(), list_tids.end());
+ std::sort(expected_tids.begin(), expected_tids.end());
+
+ ASSERT_EQ(expected_tids.size(), list_tids.size());
+ EXPECT_TRUE(std::equal(expected_tids.begin(), expected_tids.end(), list_tids.begin()));
+}
+
+INSTANTIATE_TEST_CASE_P(ThreadListTest, ThreadListTest, ::testing::Values(1, 2, 10, 1024));
+
+class ThreadCaptureTest : public ThreadListTest {
+ public:
+ ThreadCaptureTest() {}
+ ~ThreadCaptureTest() {}
+ void Fork(std::function<void()>&& child_init,
+ std::function<void()>&& child_cleanup,
+ std::function<void(pid_t)>&& parent) {
+
+ ScopedPipe start_pipe;
+ ScopedPipe stop_pipe;
+
+ int pid = fork();
+
+ if (pid == 0) {
+ // child
+ child_init();
+ EXPECT_EQ(1, TEMP_FAILURE_RETRY(write(start_pipe.Sender(), "+", 1))) << strerror(errno);
+ char buf;
+ EXPECT_EQ(1, TEMP_FAILURE_RETRY(read(stop_pipe.Receiver(), &buf, 1))) << strerror(errno);
+ child_cleanup();
+ _exit(0);
+ } else {
+ // parent
+ ASSERT_GT(pid, 0);
+ char buf;
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(start_pipe.Receiver(), &buf, 1))) << strerror(errno);
+
+ parent(pid);
+
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(stop_pipe.Sender(), "+", 1))) << strerror(errno);
+ siginfo_t info{};
+ ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) << strerror(errno);
+ }
+ }
+};
+
+TEST_P(ThreadCaptureTest, capture_some) {
+ const unsigned int threads = GetParam();
+
+ Fork([&](){
+ // child init
+ StartThreads(threads - 1, [](){});
+ },
+ [&](){
+ // child cleanup
+ StopThreads();
+ },
+ [&](pid_t child){
+ // parent
+ ASSERT_GT(child, 0);
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(child, heap);
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(threads, list_tids.size());
+
+ ASSERT_TRUE(thread_capture.CaptureThreads());
+
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(threads, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+}
+ });
+}
+
+INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024));
+
+TEST_F(ThreadCaptureTest, capture_kill) {
+ int ret = fork();
+
+ if (ret == 0) {
+ // child
+ sleep(10);
+ } else {
+ // parent
+ ASSERT_GT(ret, 0);
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(ret, heap);
+ thread_capture.InjectTestFunc([&](pid_t tid){
+ syscall(SYS_tgkill, ret, tid, SIGKILL);
+ usleep(10000);
+ });
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(1U, list_tids.size());
+
+ ASSERT_FALSE(thread_capture.CaptureThreads());
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+ }
+}
+
+TEST_F(ThreadCaptureTest, capture_signal) {
+ const int sig = SIGUSR1;
+
+ ScopedPipe pipe;
+
+ // For signal handler
+ static ScopedPipe* g_pipe;
+
+ Fork([&](){
+ // child init
+ pipe.CloseReceiver();
+
+ g_pipe = &pipe;
+
+ struct sigaction act{};
+ act.sa_handler = [](int){
+ char buf = '+';
+ write(g_pipe->Sender(), &buf, 1);
+ g_pipe->CloseSender();
+ };
+ sigaction(sig, &act, NULL);
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+ },
+ [&](){
+ // child cleanup
+ g_pipe = nullptr;
+ pipe.Close();
+ },
+ [&](pid_t child){
+ // parent
+ ASSERT_GT(child, 0);
+ pipe.CloseSender();
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(child, heap);
+ thread_capture.InjectTestFunc([&](pid_t tid){
+ syscall(SYS_tgkill, child, tid, sig);
+ usleep(10000);
+ });
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(1U, list_tids.size());
+
+ ASSERT_TRUE(thread_capture.CaptureThreads());
+
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(1U, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+ usleep(100000);
+ char buf;
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
+ ASSERT_EQ(buf, '+');
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+ });
+}
diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk
deleted file mode 100644
index 7906986..0000000
--- a/libmincrypt/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2008 The Android Open Source Project
-#
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libmincrypt
-LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libmincrypt
-LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(LOCAL_PATH)/tools/Android.mk \
- $(LOCAL_PATH)/test/Android.mk
diff --git a/libmincrypt/NOTICE b/libmincrypt/NOTICE
deleted file mode 100644
index 430d3d6..0000000
--- a/libmincrypt/NOTICE
+++ /dev/null
@@ -1,23 +0,0 @@
- Copyright 2008, The Android Open Source Project
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of Google Inc. nor the names of its contributors may
- be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/libmincrypt/dsa_sig.c b/libmincrypt/dsa_sig.c
deleted file mode 100644
index 101314b..0000000
--- a/libmincrypt/dsa_sig.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. 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 <string.h>
-
-#include "mincrypt/dsa_sig.h"
-#include "mincrypt/p256.h"
-
-/**
- * Trims off the leading zero bytes and copy it to a buffer aligning it to the end.
- */
-static inline int trim_to_p256_bytes(unsigned char dst[P256_NBYTES], unsigned char *src,
- int src_len) {
- int dst_offset;
- while (*src == '\0' && src_len > 0) {
- src++;
- src_len--;
- }
- if (src_len > P256_NBYTES || src_len < 1) {
- return 0;
- }
- dst_offset = P256_NBYTES - src_len;
- memset(dst, 0, dst_offset);
- memcpy(dst + dst_offset, src, src_len);
- return 1;
-}
-
-/**
- * Unpacks the ASN.1 DSA signature sequence.
- */
-int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int) {
- /*
- * Structure is:
- * 0x30 0xNN SEQUENCE + s_length
- * 0x02 0xNN INTEGER + r_length
- * 0xAA 0xBB .. r_length bytes of "r" (offset 4)
- * 0x02 0xNN INTEGER + s_length
- * 0xMM 0xNN .. s_length bytes of "s" (offset 6 + r_len)
- */
- int seq_len;
- unsigned char r_bytes[P256_NBYTES];
- unsigned char s_bytes[P256_NBYTES];
- int r_len;
- int s_len;
-
- memset(r_bytes, 0, sizeof(r_bytes));
- memset(s_bytes, 0, sizeof(s_bytes));
-
- /*
- * Must have at least:
- * 2 bytes sequence header and length
- * 2 bytes R integer header and length
- * 1 byte of R
- * 2 bytes S integer header and length
- * 1 byte of S
- *
- * 8 bytes total
- */
- if (sig_len < 8 || sig[0] != 0x30 || sig[2] != 0x02) {
- return 0;
- }
-
- seq_len = sig[1];
- if ((seq_len <= 0) || (seq_len + 2 != sig_len)) {
- return 0;
- }
-
- r_len = sig[3];
- /*
- * Must have at least:
- * 2 bytes for R header and length
- * 2 bytes S integer header and length
- * 1 byte of S
- */
- if ((r_len < 1) || (r_len > seq_len - 5) || (sig[4 + r_len] != 0x02)) {
- return 0;
- }
- s_len = sig[5 + r_len];
-
- /**
- * Must have:
- * 2 bytes for R header and length
- * r_len bytes for R
- * 2 bytes S integer header and length
- */
- if ((s_len < 1) || (s_len != seq_len - 4 - r_len)) {
- return 0;
- }
-
- /*
- * ASN.1 encoded integers are zero-padded for positive integers. Make sure we have
- * a correctly-sized buffer and that the resulting integer isn't too large.
- */
- if (!trim_to_p256_bytes(r_bytes, &sig[4], r_len)
- || !trim_to_p256_bytes(s_bytes, &sig[6 + r_len], s_len)) {
- return 0;
- }
-
- p256_from_bin(r_bytes, r_int);
- p256_from_bin(s_bytes, s_int);
-
- return 1;
-}
diff --git a/libmincrypt/p256.c b/libmincrypt/p256.c
deleted file mode 100644
index 555a07a..0000000
--- a/libmincrypt/p256.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. 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.
- */
-
-// This is an implementation of the P256 elliptic curve group. It's written to
-// be portable 32-bit, although it's still constant-time.
-//
-// WARNING: Implementing these functions in a constant-time manner is far from
-// obvious. Be careful when touching this code.
-//
-// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
-
-#include <assert.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "mincrypt/p256.h"
-
-const p256_int SECP256r1_n = // curve order
- {{0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, -1, -1, 0, -1}};
-
-const p256_int SECP256r1_p = // curve field size
- {{-1, -1, -1, 0, 0, 0, 1, -1 }};
-
-const p256_int SECP256r1_b = // curve b
- {{0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0,
- 0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8}};
-
-void p256_init(p256_int* a) {
- memset(a, 0, sizeof(*a));
-}
-
-void p256_clear(p256_int* a) { p256_init(a); }
-
-int p256_get_bit(const p256_int* scalar, int bit) {
- return (P256_DIGIT(scalar, bit / P256_BITSPERDIGIT)
- >> (bit & (P256_BITSPERDIGIT - 1))) & 1;
-}
-
-int p256_is_zero(const p256_int* a) {
- int i, result = 0;
- for (i = 0; i < P256_NDIGITS; ++i) result |= P256_DIGIT(a, i);
- return !result;
-}
-
-// top, c[] += a[] * b
-// Returns new top
-static p256_digit mulAdd(const p256_int* a,
- p256_digit b,
- p256_digit top,
- p256_digit* c) {
- int i;
- p256_ddigit carry = 0;
-
- for (i = 0; i < P256_NDIGITS; ++i) {
- carry += *c;
- carry += (p256_ddigit)P256_DIGIT(a, i) * b;
- *c++ = (p256_digit)carry;
- carry >>= P256_BITSPERDIGIT;
- }
- return top + (p256_digit)carry;
-}
-
-// top, c[] -= top_a, a[]
-static p256_digit subTop(p256_digit top_a,
- const p256_digit* a,
- p256_digit top_c,
- p256_digit* c) {
- int i;
- p256_sddigit borrow = 0;
-
- for (i = 0; i < P256_NDIGITS; ++i) {
- borrow += *c;
- borrow -= *a++;
- *c++ = (p256_digit)borrow;
- borrow >>= P256_BITSPERDIGIT;
- }
- borrow += top_c;
- borrow -= top_a;
- top_c = (p256_digit)borrow;
- assert((borrow >> P256_BITSPERDIGIT) == 0);
- return top_c;
-}
-
-// top, c[] -= MOD[] & mask (0 or -1)
-// returns new top.
-static p256_digit subM(const p256_int* MOD,
- p256_digit top,
- p256_digit* c,
- p256_digit mask) {
- int i;
- p256_sddigit borrow = 0;
- for (i = 0; i < P256_NDIGITS; ++i) {
- borrow += *c;
- borrow -= P256_DIGIT(MOD, i) & mask;
- *c++ = (p256_digit)borrow;
- borrow >>= P256_BITSPERDIGIT;
- }
- return top + (p256_digit)borrow;
-}
-
-// top, c[] += MOD[] & mask (0 or -1)
-// returns new top.
-static p256_digit addM(const p256_int* MOD,
- p256_digit top,
- p256_digit* c,
- p256_digit mask) {
- int i;
- p256_ddigit carry = 0;
- for (i = 0; i < P256_NDIGITS; ++i) {
- carry += *c;
- carry += P256_DIGIT(MOD, i) & mask;
- *c++ = (p256_digit)carry;
- carry >>= P256_BITSPERDIGIT;
- }
- return top + (p256_digit)carry;
-}
-
-// c = a * b mod MOD. c can be a and/or b.
-void p256_modmul(const p256_int* MOD,
- const p256_int* a,
- const p256_digit top_b,
- const p256_int* b,
- p256_int* c) {
- p256_digit tmp[P256_NDIGITS * 2 + 1] = { 0 };
- p256_digit top = 0;
- int i;
-
- // Multiply/add into tmp.
- for (i = 0; i < P256_NDIGITS; ++i) {
- if (i) tmp[i + P256_NDIGITS - 1] = top;
- top = mulAdd(a, P256_DIGIT(b, i), 0, tmp + i);
- }
-
- // Multiply/add top digit
- tmp[i + P256_NDIGITS - 1] = top;
- top = mulAdd(a, top_b, 0, tmp + i);
-
- // Reduce tmp, digit by digit.
- for (; i >= 0; --i) {
- p256_digit reducer[P256_NDIGITS] = { 0 };
- p256_digit top_reducer;
-
- // top can be any value at this point.
- // Guestimate reducer as top * MOD, since msw of MOD is -1.
- top_reducer = mulAdd(MOD, top, 0, reducer);
-
- // Subtract reducer from top | tmp.
- top = subTop(top_reducer, reducer, top, tmp + i);
-
- // top is now either 0 or 1. Make it 0, fixed-timing.
- assert(top <= 1);
-
- top = subM(MOD, top, tmp + i, ~(top - 1));
-
- assert(top == 0);
-
- // We have now reduced the top digit off tmp. Fetch new top digit.
- top = tmp[i + P256_NDIGITS - 1];
- }
-
- // tmp might still be larger than MOD, yet same bit length.
- // Make sure it is less, fixed-timing.
- addM(MOD, 0, tmp, subM(MOD, 0, tmp, -1));
-
- memcpy(c, tmp, P256_NBYTES);
-}
-int p256_is_odd(const p256_int* a) { return P256_DIGIT(a, 0) & 1; }
-int p256_is_even(const p256_int* a) { return !(P256_DIGIT(a, 0) & 1); }
-
-p256_digit p256_shl(const p256_int* a, int n, p256_int* b) {
- int i;
- p256_digit top = P256_DIGIT(a, P256_NDIGITS - 1);
-
- n %= P256_BITSPERDIGIT;
- for (i = P256_NDIGITS - 1; i > 0; --i) {
- p256_digit accu = (P256_DIGIT(a, i) << n);
- accu |= (P256_DIGIT(a, i - 1) >> (P256_BITSPERDIGIT - n));
- P256_DIGIT(b, i) = accu;
- }
- P256_DIGIT(b, i) = (P256_DIGIT(a, i) << n);
-
- top = (p256_digit)((((p256_ddigit)top) << n) >> P256_BITSPERDIGIT);
-
- return top;
-}
-
-void p256_shr(const p256_int* a, int n, p256_int* b) {
- int i;
-
- n %= P256_BITSPERDIGIT;
- for (i = 0; i < P256_NDIGITS - 1; ++i) {
- p256_digit accu = (P256_DIGIT(a, i) >> n);
- accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - n));
- P256_DIGIT(b, i) = accu;
- }
- P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> n);
-}
-
-static void p256_shr1(const p256_int* a, int highbit, p256_int* b) {
- int i;
-
- for (i = 0; i < P256_NDIGITS - 1; ++i) {
- p256_digit accu = (P256_DIGIT(a, i) >> 1);
- accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - 1));
- P256_DIGIT(b, i) = accu;
- }
- P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> 1) |
- (highbit << (P256_BITSPERDIGIT - 1));
-}
-
-// Return -1, 0, 1 for a < b, a == b or a > b respectively.
-int p256_cmp(const p256_int* a, const p256_int* b) {
- int i;
- p256_sddigit borrow = 0;
- p256_digit notzero = 0;
-
- for (i = 0; i < P256_NDIGITS; ++i) {
- borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i);
- // Track whether any result digit is ever not zero.
- // Relies on !!(non-zero) evaluating to 1, e.g., !!(-1) evaluating to 1.
- notzero |= !!((p256_digit)borrow);
- borrow >>= P256_BITSPERDIGIT;
- }
- return (int)borrow | notzero;
-}
-
-// c = a - b. Returns borrow: 0 or -1.
-int p256_sub(const p256_int* a, const p256_int* b, p256_int* c) {
- int i;
- p256_sddigit borrow = 0;
-
- for (i = 0; i < P256_NDIGITS; ++i) {
- borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i);
- if (c) P256_DIGIT(c, i) = (p256_digit)borrow;
- borrow >>= P256_BITSPERDIGIT;
- }
- return (int)borrow;
-}
-
-// c = a + b. Returns carry: 0 or 1.
-int p256_add(const p256_int* a, const p256_int* b, p256_int* c) {
- int i;
- p256_ddigit carry = 0;
-
- for (i = 0; i < P256_NDIGITS; ++i) {
- carry += (p256_ddigit)P256_DIGIT(a, i) + P256_DIGIT(b, i);
- if (c) P256_DIGIT(c, i) = (p256_digit)carry;
- carry >>= P256_BITSPERDIGIT;
- }
- return (int)carry;
-}
-
-// b = a + d. Returns carry, 0 or 1.
-int p256_add_d(const p256_int* a, p256_digit d, p256_int* b) {
- int i;
- p256_ddigit carry = d;
-
- for (i = 0; i < P256_NDIGITS; ++i) {
- carry += (p256_ddigit)P256_DIGIT(a, i);
- if (b) P256_DIGIT(b, i) = (p256_digit)carry;
- carry >>= P256_BITSPERDIGIT;
- }
- return (int)carry;
-}
-
-// b = 1/a mod MOD, binary euclid.
-void p256_modinv_vartime(const p256_int* MOD,
- const p256_int* a,
- p256_int* b) {
- p256_int R = P256_ZERO;
- p256_int S = P256_ONE;
- p256_int U = *MOD;
- p256_int V = *a;
-
- for (;;) {
- if (p256_is_even(&U)) {
- p256_shr1(&U, 0, &U);
- if (p256_is_even(&R)) {
- p256_shr1(&R, 0, &R);
- } else {
- // R = (R+MOD)/2
- p256_shr1(&R, p256_add(&R, MOD, &R), &R);
- }
- } else if (p256_is_even(&V)) {
- p256_shr1(&V, 0, &V);
- if (p256_is_even(&S)) {
- p256_shr1(&S, 0, &S);
- } else {
- // S = (S+MOD)/2
- p256_shr1(&S, p256_add(&S, MOD, &S) , &S);
- }
- } else { // U,V both odd.
- if (!p256_sub(&V, &U, NULL)) {
- p256_sub(&V, &U, &V);
- if (p256_sub(&S, &R, &S)) p256_add(&S, MOD, &S);
- if (p256_is_zero(&V)) break; // done.
- } else {
- p256_sub(&U, &V, &U);
- if (p256_sub(&R, &S, &R)) p256_add(&R, MOD, &R);
- }
- }
- }
-
- p256_mod(MOD, &R, b);
-}
-
-void p256_mod(const p256_int* MOD,
- const p256_int* in,
- p256_int* out) {
- if (out != in) *out = *in;
- addM(MOD, 0, P256_DIGITS(out), subM(MOD, 0, P256_DIGITS(out), -1));
-}
-
-// Verify y^2 == x^3 - 3x + b mod p
-// and 0 < x < p and 0 < y < p
-int p256_is_valid_point(const p256_int* x, const p256_int* y) {
- p256_int y2, x3;
-
- if (p256_cmp(&SECP256r1_p, x) <= 0 ||
- p256_cmp(&SECP256r1_p, y) <= 0 ||
- p256_is_zero(x) ||
- p256_is_zero(y)) return 0;
-
- p256_modmul(&SECP256r1_p, y, 0, y, &y2); // y^2
-
- p256_modmul(&SECP256r1_p, x, 0, x, &x3); // x^2
- p256_modmul(&SECP256r1_p, x, 0, &x3, &x3); // x^3
- if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - x
- if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 2x
- if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 3x
- if (p256_add(&x3, &SECP256r1_b, &x3)) // x^3 - 3x + b
- p256_sub(&x3, &SECP256r1_p, &x3);
-
- return p256_cmp(&y2, &x3) == 0;
-}
-
-void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst) {
- int i;
- const uint8_t* p = &src[0];
-
- for (i = P256_NDIGITS - 1; i >= 0; --i) {
- P256_DIGIT(dst, i) =
- (p[0] << 24) |
- (p[1] << 16) |
- (p[2] << 8) |
- p[3];
- p += 4;
- }
-}
diff --git a/libmincrypt/p256_ec.c b/libmincrypt/p256_ec.c
deleted file mode 100644
index 90262cc..0000000
--- a/libmincrypt/p256_ec.c
+++ /dev/null
@@ -1,1279 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. 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.
- */
-
-// This is an implementation of the P256 elliptic curve group. It's written to
-// be portable 32-bit, although it's still constant-time.
-//
-// WARNING: Implementing these functions in a constant-time manner is far from
-// obvious. Be careful when touching this code.
-//
-// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
-
-#include <stdint.h>
-#include <stdio.h>
-
-#include <string.h>
-#include <stdlib.h>
-
-#include "mincrypt/p256.h"
-
-typedef uint8_t u8;
-typedef uint32_t u32;
-typedef int32_t s32;
-typedef uint64_t u64;
-
-/* Our field elements are represented as nine 32-bit limbs.
- *
- * The value of an felem (field element) is:
- * x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228)
- *
- * That is, each limb is alternately 29 or 28-bits wide in little-endian
- * order.
- *
- * This means that an felem hits 2**257, rather than 2**256 as we would like. A
- * 28, 29, ... pattern would cause us to hit 2**256, but that causes problems
- * when multiplying as terms end up one bit short of a limb which would require
- * much bit-shifting to correct.
- *
- * Finally, the values stored in an felem are in Montgomery form. So the value
- * |y| is stored as (y*R) mod p, where p is the P-256 prime and R is 2**257.
- */
-typedef u32 limb;
-#define NLIMBS 9
-typedef limb felem[NLIMBS];
-
-static const limb kBottom28Bits = 0xfffffff;
-static const limb kBottom29Bits = 0x1fffffff;
-
-/* kOne is the number 1 as an felem. It's 2**257 mod p split up into 29 and
- * 28-bit words. */
-static const felem kOne = {
- 2, 0, 0, 0xffff800,
- 0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff,
- 0
-};
-static const felem kZero = {0};
-static const felem kP = {
- 0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff,
- 0, 0, 0x200000, 0xf000000,
- 0xfffffff
-};
-static const felem k2P = {
- 0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff,
- 0, 0, 0x400000, 0xe000000,
- 0x1fffffff
-};
-/* kPrecomputed contains precomputed values to aid the calculation of scalar
- * multiples of the base point, G. It's actually two, equal length, tables
- * concatenated.
- *
- * The first table contains (x,y) felem pairs for 16 multiples of the base
- * point, G.
- *
- * Index | Index (binary) | Value
- * 0 | 0000 | 0G (all zeros, omitted)
- * 1 | 0001 | G
- * 2 | 0010 | 2**64G
- * 3 | 0011 | 2**64G + G
- * 4 | 0100 | 2**128G
- * 5 | 0101 | 2**128G + G
- * 6 | 0110 | 2**128G + 2**64G
- * 7 | 0111 | 2**128G + 2**64G + G
- * 8 | 1000 | 2**192G
- * 9 | 1001 | 2**192G + G
- * 10 | 1010 | 2**192G + 2**64G
- * 11 | 1011 | 2**192G + 2**64G + G
- * 12 | 1100 | 2**192G + 2**128G
- * 13 | 1101 | 2**192G + 2**128G + G
- * 14 | 1110 | 2**192G + 2**128G + 2**64G
- * 15 | 1111 | 2**192G + 2**128G + 2**64G + G
- *
- * The second table follows the same style, but the terms are 2**32G,
- * 2**96G, 2**160G, 2**224G.
- *
- * This is ~2KB of data. */
-static const limb kPrecomputed[NLIMBS * 2 * 15 * 2] = {
- 0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, 0x119e7edc, 0xd4a6eab, 0x3120bee,
- 0x1d2aac15, 0xf25357c, 0x19e45cdd, 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3,
- 0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, 0x1fe49073, 0x3fa36cc, 0x5ebcd2c,
- 0xb402f2f, 0x15c70bf, 0x1561925c, 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22,
- 0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, 0x56c7109, 0xa267a00, 0xb57c050,
- 0x98fb57, 0xaa837cc, 0x60c0792, 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b,
- 0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, 0x1d96a5a9, 0x843a649, 0xc3ab0fa,
- 0x6e2e7d5, 0x7673a2a, 0x178b65e8, 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2,
- 0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, 0x172db40f, 0x83e277d, 0xb0dd609,
- 0xfd1da12, 0x35c6e52, 0x19ede20c, 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581,
- 0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, 0xc35df9f, 0x48764cd, 0x76dbcca,
- 0xca4b366, 0xe9303ab, 0x1a7480e7, 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33,
- 0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6,
- 0x14e614aa, 0x9246317, 0x1bc83aca, 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd,
- 0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, 0x6a2901a, 0x69a8556, 0x7e7c0,
- 0x9b7d8d3, 0x309a80, 0x1ad05f7f, 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881,
- 0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, 0xbe80c51, 0xc22be3e, 0xe35e65a,
- 0x1aeb7aa0, 0xc375315, 0xf67bc99, 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26,
- 0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, 0x1a90c502, 0x2f32042, 0xa17769b,
- 0xafd8c7c, 0x8191c6e, 0x1dcdb237, 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023,
- 0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, 0x2fc513c, 0x407f75c, 0xbaab133,
- 0x9705fe9, 0xb88b8e7, 0x734c993, 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa,
- 0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, 0xbc62f16, 0x2b6fcc7, 0xf5a4e29,
- 0x13560b66, 0xc0b6ac2, 0x51ae690, 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc,
- 0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, 0x1f83de85, 0x27de188, 0x66f70b8,
- 0x181cd51f, 0x96b6e4c, 0x188f2335, 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59,
- 0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, 0x79061, 0x823d9d2, 0x8213f39,
- 0x1128ae0b, 0xd095d05, 0xb85c0c2, 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689,
- 0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, 0x1051a729, 0x4be3499, 0x52b23aa,
- 0x109f307e, 0x6f5b6bb, 0x1f84e1e7, 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3,
- 0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, 0x1a4a7529, 0xcb7beb1, 0xb2a78a1,
- 0x1ab21f1f, 0x6361ccf, 0x6c9179d, 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f,
- 0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, 0xc50124c, 0x50daa90, 0xb13f72,
- 0xb06aa75, 0x70f5cc6, 0x1649e5aa, 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d,
- 0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, 0x18c9e11e, 0x20bca9a, 0x66f496b,
- 0x3eef294, 0x67500d2, 0xd7f613c, 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a,
- 0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, 0x17fa56ff, 0x65ef930, 0x21dc4a,
- 0x2b37659, 0x450fe17, 0xb357b65, 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f,
- 0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, 0x725522b, 0xdc78583, 0x40eeabb,
- 0x1fde328a, 0xbd61d96, 0xd28c387, 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc,
- 0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, 0x6316abb, 0x2413c8e, 0x5425bf9,
- 0x1bed3e3a, 0xf272274, 0x1f5e7326, 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce,
- 0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, 0x1dc4ef73, 0x8956f34, 0xe4b5cf2,
- 0x1b0d3a18, 0x3194a36, 0x6c2641f, 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca,
- 0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, 0x10e3edc9, 0x9c19bf2, 0x5882229,
- 0x1b2b4162, 0xa5cef1a, 0x1543622b, 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57,
- 0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, 0xa038113, 0xa4a1769, 0x11fbc6c,
- 0x917e50e, 0xeec3da8, 0x169d6eac, 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa,
- 0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, 0x196142cc, 0x7bf0fa9, 0x957651,
- 0x4e0f215, 0xed439f8, 0x3f46bd5, 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec,
- 0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, 0x19d1c12d, 0xf20bd46, 0x1951fa7,
- 0xa3656c3, 0x523a425, 0xfcd0692, 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c,
- 0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, 0x2e74779, 0x576138, 0x9587927,
- 0x133130fa, 0xbe05516, 0x9f4d619, 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298,
- 0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, 0x14e2f5d8, 0xf858d3a, 0x942eea8,
- 0xda5b765, 0x6edafff, 0xa9d18cc, 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2,
- 0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, 0x4c146c0, 0x6bdf55a, 0x4e4457d,
- 0x16152289, 0xac78ec2, 0x1a59c5a2, 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4,
- 0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, 0x81d55d7, 0xa5bef68, 0xb7b30d8,
- 0xbe73d6f, 0xaa88141, 0xd976c81, 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78,
-};
-
-
-/* Field element operations: */
-
-/* NON_ZERO_TO_ALL_ONES returns:
- * 0xffffffff for 0 < x <= 2**31
- * 0 for x == 0 or x > 2**31.
- *
- * x must be a u32 or an equivalent type such as limb. */
-#define NON_ZERO_TO_ALL_ONES(x) ((((u32)(x) - 1) >> 31) - 1)
-
-/* felem_reduce_carry adds a multiple of p in order to cancel |carry|,
- * which is a term at 2**257.
- *
- * On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28.
- * On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29. */
-static void felem_reduce_carry(felem inout, limb carry) {
- const u32 carry_mask = NON_ZERO_TO_ALL_ONES(carry);
-
- inout[0] += carry << 1;
- inout[3] += 0x10000000 & carry_mask;
- /* carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the
- * previous line therefore this doesn't underflow. */
- inout[3] -= carry << 11;
- inout[4] += (0x20000000 - 1) & carry_mask;
- inout[5] += (0x10000000 - 1) & carry_mask;
- inout[6] += (0x20000000 - 1) & carry_mask;
- inout[6] -= carry << 22;
- /* This may underflow if carry is non-zero but, if so, we'll fix it in the
- * next line. */
- inout[7] -= 1 & carry_mask;
- inout[7] += carry << 25;
-}
-
-/* felem_sum sets out = in+in2.
- *
- * On entry, in[i]+in2[i] must not overflow a 32-bit word.
- * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */
-static void felem_sum(felem out, const felem in, const felem in2) {
- limb carry = 0;
- unsigned i;
-
- for (i = 0;; i++) {
- out[i] = in[i] + in2[i];
- out[i] += carry;
- carry = out[i] >> 29;
- out[i] &= kBottom29Bits;
-
- i++;
- if (i == NLIMBS)
- break;
-
- out[i] = in[i] + in2[i];
- out[i] += carry;
- carry = out[i] >> 28;
- out[i] &= kBottom28Bits;
- }
-
- felem_reduce_carry(out, carry);
-}
-
-#define two31m3 (((limb)1) << 31) - (((limb)1) << 3)
-#define two30m2 (((limb)1) << 30) - (((limb)1) << 2)
-#define two30p13m2 (((limb)1) << 30) + (((limb)1) << 13) - (((limb)1) << 2)
-#define two31m2 (((limb)1) << 31) - (((limb)1) << 2)
-#define two31p24m2 (((limb)1) << 31) + (((limb)1) << 24) - (((limb)1) << 2)
-#define two30m27m2 (((limb)1) << 30) - (((limb)1) << 27) - (((limb)1) << 2)
-
-/* zero31 is 0 mod p. */
-static const felem zero31 = { two31m3, two30m2, two31m2, two30p13m2, two31m2, two30m2, two31p24m2, two30m27m2, two31m2 };
-
-/* felem_diff sets out = in-in2.
- *
- * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and
- * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
- * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
-static void felem_diff(felem out, const felem in, const felem in2) {
- limb carry = 0;
- unsigned i;
-
- for (i = 0;; i++) {
- out[i] = in[i] - in2[i];
- out[i] += zero31[i];
- out[i] += carry;
- carry = out[i] >> 29;
- out[i] &= kBottom29Bits;
-
- i++;
- if (i == NLIMBS)
- break;
-
- out[i] = in[i] - in2[i];
- out[i] += zero31[i];
- out[i] += carry;
- carry = out[i] >> 28;
- out[i] &= kBottom28Bits;
- }
-
- felem_reduce_carry(out, carry);
-}
-
-/* felem_reduce_degree sets out = tmp/R mod p where tmp contains 64-bit words
- * with the same 29,28,... bit positions as an felem.
- *
- * The values in felems are in Montgomery form: x*R mod p where R = 2**257.
- * Since we just multiplied two Montgomery values together, the result is
- * x*y*R*R mod p. We wish to divide by R in order for the result also to be
- * in Montgomery form.
- *
- * On entry: tmp[i] < 2**64
- * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */
-static void felem_reduce_degree(felem out, u64 tmp[17]) {
- /* The following table may be helpful when reading this code:
- *
- * Limb number: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10...
- * Width (bits): 29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29
- * Start bit: 0 | 29| 57| 86|114|143|171|200|228|257|285
- * (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285 */
- limb tmp2[18], carry, x, xMask;
- unsigned i;
-
- /* tmp contains 64-bit words with the same 29,28,29-bit positions as an
- * felem. So the top of an element of tmp might overlap with another
- * element two positions down. The following loop eliminates this
- * overlap. */
- tmp2[0] = (limb)(tmp[0] & kBottom29Bits);
-
- /* In the following we use "(limb) tmp[x]" and "(limb) (tmp[x]>>32)" to try
- * and hint to the compiler that it can do a single-word shift by selecting
- * the right register rather than doing a double-word shift and truncating
- * afterwards. */
- tmp2[1] = ((limb) tmp[0]) >> 29;
- tmp2[1] |= (((limb)(tmp[0] >> 32)) << 3) & kBottom28Bits;
- tmp2[1] += ((limb) tmp[1]) & kBottom28Bits;
- carry = tmp2[1] >> 28;
- tmp2[1] &= kBottom28Bits;
-
- for (i = 2; i < 17; i++) {
- tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25;
- tmp2[i] += ((limb)(tmp[i - 1])) >> 28;
- tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 4) & kBottom29Bits;
- tmp2[i] += ((limb) tmp[i]) & kBottom29Bits;
- tmp2[i] += carry;
- carry = tmp2[i] >> 29;
- tmp2[i] &= kBottom29Bits;
-
- i++;
- if (i == 17)
- break;
- tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25;
- tmp2[i] += ((limb)(tmp[i - 1])) >> 29;
- tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 3) & kBottom28Bits;
- tmp2[i] += ((limb) tmp[i]) & kBottom28Bits;
- tmp2[i] += carry;
- carry = tmp2[i] >> 28;
- tmp2[i] &= kBottom28Bits;
- }
-
- tmp2[17] = ((limb)(tmp[15] >> 32)) >> 25;
- tmp2[17] += ((limb)(tmp[16])) >> 29;
- tmp2[17] += (((limb)(tmp[16] >> 32)) << 3);
- tmp2[17] += carry;
-
- /* Montgomery elimination of terms.
- *
- * Since R is 2**257, we can divide by R with a bitwise shift if we can
- * ensure that the right-most 257 bits are all zero. We can make that true by
- * adding multiplies of p without affecting the value.
- *
- * So we eliminate limbs from right to left. Since the bottom 29 bits of p
- * are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0.
- * We can do that for 8 further limbs and then right shift to eliminate the
- * extra factor of R. */
- for (i = 0;; i += 2) {
- tmp2[i + 1] += tmp2[i] >> 29;
- x = tmp2[i] & kBottom29Bits;
- xMask = NON_ZERO_TO_ALL_ONES(x);
- tmp2[i] = 0;
-
- /* The bounds calculations for this loop are tricky. Each iteration of
- * the loop eliminates two words by adding values to words to their
- * right.
- *
- * The following table contains the amounts added to each word (as an
- * offset from the value of i at the top of the loop). The amounts are
- * accounted for from the first and second half of the loop separately
- * and are written as, for example, 28 to mean a value <2**28.
- *
- * Word: 3 4 5 6 7 8 9 10
- * Added in top half: 28 11 29 21 29 28
- * 28 29
- * 29
- * Added in bottom half: 29 10 28 21 28 28
- * 29
- *
- * The value that is currently offset 7 will be offset 5 for the next
- * iteration and then offset 3 for the iteration after that. Therefore
- * the total value added will be the values added at 7, 5 and 3.
- *
- * The following table accumulates these values. The sums at the bottom
- * are written as, for example, 29+28, to mean a value < 2**29+2**28.
- *
- * Word: 3 4 5 6 7 8 9 10 11 12 13
- * 28 11 10 29 21 29 28 28 28 28 28
- * 29 28 11 28 29 28 29 28 29 28
- * 29 28 21 21 29 21 29 21
- * 10 29 28 21 28 21 28
- * 28 29 28 29 28 29 28
- * 11 10 29 10 29 10
- * 29 28 11 28 11
- * 29 29
- * --------------------------------------------
- * 30+ 31+ 30+ 31+ 30+
- * 28+ 29+ 28+ 29+ 21+
- * 21+ 28+ 21+ 28+ 10
- * 10 21+ 10 21+
- * 11 11
- *
- * So the greatest amount is added to tmp2[10] and tmp2[12]. If
- * tmp2[10/12] has an initial value of <2**29, then the maximum value
- * will be < 2**31 + 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32,
- * as required. */
- tmp2[i + 3] += (x << 10) & kBottom28Bits;
- tmp2[i + 4] += (x >> 18);
-
- tmp2[i + 6] += (x << 21) & kBottom29Bits;
- tmp2[i + 7] += x >> 8;
-
- /* At position 200, which is the starting bit position for word 7, we
- * have a factor of 0xf000000 = 2**28 - 2**24. */
- tmp2[i + 7] += 0x10000000 & xMask;
- /* Word 7 is 28 bits wide, so the 2**28 term exactly hits word 8. */
- tmp2[i + 8] += (x - 1) & xMask;
- tmp2[i + 7] -= (x << 24) & kBottom28Bits;
- tmp2[i + 8] -= x >> 4;
-
- tmp2[i + 8] += 0x20000000 & xMask;
- tmp2[i + 8] -= x;
- tmp2[i + 8] += (x << 28) & kBottom29Bits;
- tmp2[i + 9] += ((x >> 1) - 1) & xMask;
-
- if (i+1 == NLIMBS)
- break;
- tmp2[i + 2] += tmp2[i + 1] >> 28;
- x = tmp2[i + 1] & kBottom28Bits;
- xMask = NON_ZERO_TO_ALL_ONES(x);
- tmp2[i + 1] = 0;
-
- tmp2[i + 4] += (x << 11) & kBottom29Bits;
- tmp2[i + 5] += (x >> 18);
-
- tmp2[i + 7] += (x << 21) & kBottom28Bits;
- tmp2[i + 8] += x >> 7;
-
- /* At position 199, which is the starting bit of the 8th word when
- * dealing with a context starting on an odd word, we have a factor of
- * 0x1e000000 = 2**29 - 2**25. Since we have not updated i, the 8th
- * word from i+1 is i+8. */
- tmp2[i + 8] += 0x20000000 & xMask;
- tmp2[i + 9] += (x - 1) & xMask;
- tmp2[i + 8] -= (x << 25) & kBottom29Bits;
- tmp2[i + 9] -= x >> 4;
-
- tmp2[i + 9] += 0x10000000 & xMask;
- tmp2[i + 9] -= x;
- tmp2[i + 10] += (x - 1) & xMask;
- }
-
- /* We merge the right shift with a carry chain. The words above 2**257 have
- * widths of 28,29,... which we need to correct when copying them down. */
- carry = 0;
- for (i = 0; i < 8; i++) {
- /* The maximum value of tmp2[i + 9] occurs on the first iteration and
- * is < 2**30+2**29+2**28. Adding 2**29 (from tmp2[i + 10]) is
- * therefore safe. */
- out[i] = tmp2[i + 9];
- out[i] += carry;
- out[i] += (tmp2[i + 10] << 28) & kBottom29Bits;
- carry = out[i] >> 29;
- out[i] &= kBottom29Bits;
-
- i++;
- out[i] = tmp2[i + 9] >> 1;
- out[i] += carry;
- carry = out[i] >> 28;
- out[i] &= kBottom28Bits;
- }
-
- out[8] = tmp2[17];
- out[8] += carry;
- carry = out[8] >> 29;
- out[8] &= kBottom29Bits;
-
- felem_reduce_carry(out, carry);
-}
-
-/* felem_square sets out=in*in.
- *
- * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29.
- * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
-static void felem_square(felem out, const felem in) {
- u64 tmp[17];
-
- tmp[0] = ((u64) in[0]) * in[0];
- tmp[1] = ((u64) in[0]) * (in[1] << 1);
- tmp[2] = ((u64) in[0]) * (in[2] << 1) +
- ((u64) in[1]) * (in[1] << 1);
- tmp[3] = ((u64) in[0]) * (in[3] << 1) +
- ((u64) in[1]) * (in[2] << 1);
- tmp[4] = ((u64) in[0]) * (in[4] << 1) +
- ((u64) in[1]) * (in[3] << 2) + ((u64) in[2]) * in[2];
- tmp[5] = ((u64) in[0]) * (in[5] << 1) + ((u64) in[1]) *
- (in[4] << 1) + ((u64) in[2]) * (in[3] << 1);
- tmp[6] = ((u64) in[0]) * (in[6] << 1) + ((u64) in[1]) *
- (in[5] << 2) + ((u64) in[2]) * (in[4] << 1) +
- ((u64) in[3]) * (in[3] << 1);
- tmp[7] = ((u64) in[0]) * (in[7] << 1) + ((u64) in[1]) *
- (in[6] << 1) + ((u64) in[2]) * (in[5] << 1) +
- ((u64) in[3]) * (in[4] << 1);
- /* tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + 2**60 + 2**60,
- * which is < 2**64 as required. */
- tmp[8] = ((u64) in[0]) * (in[8] << 1) + ((u64) in[1]) *
- (in[7] << 2) + ((u64) in[2]) * (in[6] << 1) +
- ((u64) in[3]) * (in[5] << 2) + ((u64) in[4]) * in[4];
- tmp[9] = ((u64) in[1]) * (in[8] << 1) + ((u64) in[2]) *
- (in[7] << 1) + ((u64) in[3]) * (in[6] << 1) +
- ((u64) in[4]) * (in[5] << 1);
- tmp[10] = ((u64) in[2]) * (in[8] << 1) + ((u64) in[3]) *
- (in[7] << 2) + ((u64) in[4]) * (in[6] << 1) +
- ((u64) in[5]) * (in[5] << 1);
- tmp[11] = ((u64) in[3]) * (in[8] << 1) + ((u64) in[4]) *
- (in[7] << 1) + ((u64) in[5]) * (in[6] << 1);
- tmp[12] = ((u64) in[4]) * (in[8] << 1) +
- ((u64) in[5]) * (in[7] << 2) + ((u64) in[6]) * in[6];
- tmp[13] = ((u64) in[5]) * (in[8] << 1) +
- ((u64) in[6]) * (in[7] << 1);
- tmp[14] = ((u64) in[6]) * (in[8] << 1) +
- ((u64) in[7]) * (in[7] << 1);
- tmp[15] = ((u64) in[7]) * (in[8] << 1);
- tmp[16] = ((u64) in[8]) * in[8];
-
- felem_reduce_degree(out, tmp);
-}
-
-/* felem_mul sets out=in*in2.
- *
- * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and
- * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
- * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
-static void felem_mul(felem out, const felem in, const felem in2) {
- u64 tmp[17];
-
- tmp[0] = ((u64) in[0]) * in2[0];
- tmp[1] = ((u64) in[0]) * (in2[1] << 0) +
- ((u64) in[1]) * (in2[0] << 0);
- tmp[2] = ((u64) in[0]) * (in2[2] << 0) + ((u64) in[1]) *
- (in2[1] << 1) + ((u64) in[2]) * (in2[0] << 0);
- tmp[3] = ((u64) in[0]) * (in2[3] << 0) + ((u64) in[1]) *
- (in2[2] << 0) + ((u64) in[2]) * (in2[1] << 0) +
- ((u64) in[3]) * (in2[0] << 0);
- tmp[4] = ((u64) in[0]) * (in2[4] << 0) + ((u64) in[1]) *
- (in2[3] << 1) + ((u64) in[2]) * (in2[2] << 0) +
- ((u64) in[3]) * (in2[1] << 1) +
- ((u64) in[4]) * (in2[0] << 0);
- tmp[5] = ((u64) in[0]) * (in2[5] << 0) + ((u64) in[1]) *
- (in2[4] << 0) + ((u64) in[2]) * (in2[3] << 0) +
- ((u64) in[3]) * (in2[2] << 0) + ((u64) in[4]) *
- (in2[1] << 0) + ((u64) in[5]) * (in2[0] << 0);
- tmp[6] = ((u64) in[0]) * (in2[6] << 0) + ((u64) in[1]) *
- (in2[5] << 1) + ((u64) in[2]) * (in2[4] << 0) +
- ((u64) in[3]) * (in2[3] << 1) + ((u64) in[4]) *
- (in2[2] << 0) + ((u64) in[5]) * (in2[1] << 1) +
- ((u64) in[6]) * (in2[0] << 0);
- tmp[7] = ((u64) in[0]) * (in2[7] << 0) + ((u64) in[1]) *
- (in2[6] << 0) + ((u64) in[2]) * (in2[5] << 0) +
- ((u64) in[3]) * (in2[4] << 0) + ((u64) in[4]) *
- (in2[3] << 0) + ((u64) in[5]) * (in2[2] << 0) +
- ((u64) in[6]) * (in2[1] << 0) +
- ((u64) in[7]) * (in2[0] << 0);
- /* tmp[8] has the greatest value but doesn't overflow. See logic in
- * felem_square. */
- tmp[8] = ((u64) in[0]) * (in2[8] << 0) + ((u64) in[1]) *
- (in2[7] << 1) + ((u64) in[2]) * (in2[6] << 0) +
- ((u64) in[3]) * (in2[5] << 1) + ((u64) in[4]) *
- (in2[4] << 0) + ((u64) in[5]) * (in2[3] << 1) +
- ((u64) in[6]) * (in2[2] << 0) + ((u64) in[7]) *
- (in2[1] << 1) + ((u64) in[8]) * (in2[0] << 0);
- tmp[9] = ((u64) in[1]) * (in2[8] << 0) + ((u64) in[2]) *
- (in2[7] << 0) + ((u64) in[3]) * (in2[6] << 0) +
- ((u64) in[4]) * (in2[5] << 0) + ((u64) in[5]) *
- (in2[4] << 0) + ((u64) in[6]) * (in2[3] << 0) +
- ((u64) in[7]) * (in2[2] << 0) +
- ((u64) in[8]) * (in2[1] << 0);
- tmp[10] = ((u64) in[2]) * (in2[8] << 0) + ((u64) in[3]) *
- (in2[7] << 1) + ((u64) in[4]) * (in2[6] << 0) +
- ((u64) in[5]) * (in2[5] << 1) + ((u64) in[6]) *
- (in2[4] << 0) + ((u64) in[7]) * (in2[3] << 1) +
- ((u64) in[8]) * (in2[2] << 0);
- tmp[11] = ((u64) in[3]) * (in2[8] << 0) + ((u64) in[4]) *
- (in2[7] << 0) + ((u64) in[5]) * (in2[6] << 0) +
- ((u64) in[6]) * (in2[5] << 0) + ((u64) in[7]) *
- (in2[4] << 0) + ((u64) in[8]) * (in2[3] << 0);
- tmp[12] = ((u64) in[4]) * (in2[8] << 0) + ((u64) in[5]) *
- (in2[7] << 1) + ((u64) in[6]) * (in2[6] << 0) +
- ((u64) in[7]) * (in2[5] << 1) +
- ((u64) in[8]) * (in2[4] << 0);
- tmp[13] = ((u64) in[5]) * (in2[8] << 0) + ((u64) in[6]) *
- (in2[7] << 0) + ((u64) in[7]) * (in2[6] << 0) +
- ((u64) in[8]) * (in2[5] << 0);
- tmp[14] = ((u64) in[6]) * (in2[8] << 0) + ((u64) in[7]) *
- (in2[7] << 1) + ((u64) in[8]) * (in2[6] << 0);
- tmp[15] = ((u64) in[7]) * (in2[8] << 0) +
- ((u64) in[8]) * (in2[7] << 0);
- tmp[16] = ((u64) in[8]) * (in2[8] << 0);
-
- felem_reduce_degree(out, tmp);
-}
-
-static void felem_assign(felem out, const felem in) {
- memcpy(out, in, sizeof(felem));
-}
-
-/* felem_inv calculates |out| = |in|^{-1}
- *
- * Based on Fermat's Little Theorem:
- * a^p = a (mod p)
- * a^{p-1} = 1 (mod p)
- * a^{p-2} = a^{-1} (mod p)
- */
-static void felem_inv(felem out, const felem in) {
- felem ftmp, ftmp2;
- /* each e_I will hold |in|^{2^I - 1} */
- felem e2, e4, e8, e16, e32, e64;
- unsigned i;
-
- felem_square(ftmp, in); /* 2^1 */
- felem_mul(ftmp, in, ftmp); /* 2^2 - 2^0 */
- felem_assign(e2, ftmp);
- felem_square(ftmp, ftmp); /* 2^3 - 2^1 */
- felem_square(ftmp, ftmp); /* 2^4 - 2^2 */
- felem_mul(ftmp, ftmp, e2); /* 2^4 - 2^0 */
- felem_assign(e4, ftmp);
- felem_square(ftmp, ftmp); /* 2^5 - 2^1 */
- felem_square(ftmp, ftmp); /* 2^6 - 2^2 */
- felem_square(ftmp, ftmp); /* 2^7 - 2^3 */
- felem_square(ftmp, ftmp); /* 2^8 - 2^4 */
- felem_mul(ftmp, ftmp, e4); /* 2^8 - 2^0 */
- felem_assign(e8, ftmp);
- for (i = 0; i < 8; i++) {
- felem_square(ftmp, ftmp);
- } /* 2^16 - 2^8 */
- felem_mul(ftmp, ftmp, e8); /* 2^16 - 2^0 */
- felem_assign(e16, ftmp);
- for (i = 0; i < 16; i++) {
- felem_square(ftmp, ftmp);
- } /* 2^32 - 2^16 */
- felem_mul(ftmp, ftmp, e16); /* 2^32 - 2^0 */
- felem_assign(e32, ftmp);
- for (i = 0; i < 32; i++) {
- felem_square(ftmp, ftmp);
- } /* 2^64 - 2^32 */
- felem_assign(e64, ftmp);
- felem_mul(ftmp, ftmp, in); /* 2^64 - 2^32 + 2^0 */
- for (i = 0; i < 192; i++) {
- felem_square(ftmp, ftmp);
- } /* 2^256 - 2^224 + 2^192 */
-
- felem_mul(ftmp2, e64, e32); /* 2^64 - 2^0 */
- for (i = 0; i < 16; i++) {
- felem_square(ftmp2, ftmp2);
- } /* 2^80 - 2^16 */
- felem_mul(ftmp2, ftmp2, e16); /* 2^80 - 2^0 */
- for (i = 0; i < 8; i++) {
- felem_square(ftmp2, ftmp2);
- } /* 2^88 - 2^8 */
- felem_mul(ftmp2, ftmp2, e8); /* 2^88 - 2^0 */
- for (i = 0; i < 4; i++) {
- felem_square(ftmp2, ftmp2);
- } /* 2^92 - 2^4 */
- felem_mul(ftmp2, ftmp2, e4); /* 2^92 - 2^0 */
- felem_square(ftmp2, ftmp2); /* 2^93 - 2^1 */
- felem_square(ftmp2, ftmp2); /* 2^94 - 2^2 */
- felem_mul(ftmp2, ftmp2, e2); /* 2^94 - 2^0 */
- felem_square(ftmp2, ftmp2); /* 2^95 - 2^1 */
- felem_square(ftmp2, ftmp2); /* 2^96 - 2^2 */
- felem_mul(ftmp2, ftmp2, in); /* 2^96 - 3 */
-
- felem_mul(out, ftmp2, ftmp); /* 2^256 - 2^224 + 2^192 + 2^96 - 3 */
-}
-
-/* felem_scalar_3 sets out=3*out.
- *
- * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
- * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
-static void felem_scalar_3(felem out) {
- limb carry = 0;
- unsigned i;
-
- for (i = 0;; i++) {
- out[i] *= 3;
- out[i] += carry;
- carry = out[i] >> 29;
- out[i] &= kBottom29Bits;
-
- i++;
- if (i == NLIMBS)
- break;
-
- out[i] *= 3;
- out[i] += carry;
- carry = out[i] >> 28;
- out[i] &= kBottom28Bits;
- }
-
- felem_reduce_carry(out, carry);
-}
-
-/* felem_scalar_4 sets out=4*out.
- *
- * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
- * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
-static void felem_scalar_4(felem out) {
- limb carry = 0, next_carry;
- unsigned i;
-
- for (i = 0;; i++) {
- next_carry = out[i] >> 27;
- out[i] <<= 2;
- out[i] &= kBottom29Bits;
- out[i] += carry;
- carry = next_carry + (out[i] >> 29);
- out[i] &= kBottom29Bits;
-
- i++;
- if (i == NLIMBS)
- break;
-
- next_carry = out[i] >> 26;
- out[i] <<= 2;
- out[i] &= kBottom28Bits;
- out[i] += carry;
- carry = next_carry + (out[i] >> 28);
- out[i] &= kBottom28Bits;
- }
-
- felem_reduce_carry(out, carry);
-}
-
-/* felem_scalar_8 sets out=8*out.
- *
- * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
- * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
-static void felem_scalar_8(felem out) {
- limb carry = 0, next_carry;
- unsigned i;
-
- for (i = 0;; i++) {
- next_carry = out[i] >> 26;
- out[i] <<= 3;
- out[i] &= kBottom29Bits;
- out[i] += carry;
- carry = next_carry + (out[i] >> 29);
- out[i] &= kBottom29Bits;
-
- i++;
- if (i == NLIMBS)
- break;
-
- next_carry = out[i] >> 25;
- out[i] <<= 3;
- out[i] &= kBottom28Bits;
- out[i] += carry;
- carry = next_carry + (out[i] >> 28);
- out[i] &= kBottom28Bits;
- }
-
- felem_reduce_carry(out, carry);
-}
-
-/* felem_is_zero_vartime returns 1 iff |in| == 0. It takes a variable amount of
- * time depending on the value of |in|. */
-static char felem_is_zero_vartime(const felem in) {
- limb carry;
- int i;
- limb tmp[NLIMBS];
-
- felem_assign(tmp, in);
-
- /* First, reduce tmp to a minimal form. */
- do {
- carry = 0;
- for (i = 0;; i++) {
- tmp[i] += carry;
- carry = tmp[i] >> 29;
- tmp[i] &= kBottom29Bits;
-
- i++;
- if (i == NLIMBS)
- break;
-
- tmp[i] += carry;
- carry = tmp[i] >> 28;
- tmp[i] &= kBottom28Bits;
- }
-
- felem_reduce_carry(tmp, carry);
- } while (carry);
-
- /* tmp < 2**257, so the only possible zero values are 0, p and 2p. */
- return memcmp(tmp, kZero, sizeof(tmp)) == 0 ||
- memcmp(tmp, kP, sizeof(tmp)) == 0 ||
- memcmp(tmp, k2P, sizeof(tmp)) == 0;
-}
-
-
-/* Group operations:
- *
- * Elements of the elliptic curve group are represented in Jacobian
- * coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in
- * Jacobian form. */
-
-/* point_double sets {x_out,y_out,z_out} = 2*{x,y,z}.
- *
- * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l */
-static void point_double(felem x_out, felem y_out, felem z_out, const felem x,
- const felem y, const felem z) {
- felem delta, gamma, alpha, beta, tmp, tmp2;
-
- felem_square(delta, z);
- felem_square(gamma, y);
- felem_mul(beta, x, gamma);
-
- felem_sum(tmp, x, delta);
- felem_diff(tmp2, x, delta);
- felem_mul(alpha, tmp, tmp2);
- felem_scalar_3(alpha);
-
- felem_sum(tmp, y, z);
- felem_square(tmp, tmp);
- felem_diff(tmp, tmp, gamma);
- felem_diff(z_out, tmp, delta);
-
- felem_scalar_4(beta);
- felem_square(x_out, alpha);
- felem_diff(x_out, x_out, beta);
- felem_diff(x_out, x_out, beta);
-
- felem_diff(tmp, beta, x_out);
- felem_mul(tmp, alpha, tmp);
- felem_square(tmp2, gamma);
- felem_scalar_8(tmp2);
- felem_diff(y_out, tmp, tmp2);
-}
-
-/* point_add_mixed sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,1}.
- * (i.e. the second point is affine.)
- *
- * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
- *
- * Note that this function does not handle P+P, infinity+P nor P+infinity
- * correctly. */
-static void point_add_mixed(felem x_out, felem y_out, felem z_out,
- const felem x1, const felem y1, const felem z1,
- const felem x2, const felem y2) {
- felem z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp;
-
- felem_square(z1z1, z1);
- felem_sum(tmp, z1, z1);
-
- felem_mul(u2, x2, z1z1);
- felem_mul(z1z1z1, z1, z1z1);
- felem_mul(s2, y2, z1z1z1);
- felem_diff(h, u2, x1);
- felem_sum(i, h, h);
- felem_square(i, i);
- felem_mul(j, h, i);
- felem_diff(r, s2, y1);
- felem_sum(r, r, r);
- felem_mul(v, x1, i);
-
- felem_mul(z_out, tmp, h);
- felem_square(rr, r);
- felem_diff(x_out, rr, j);
- felem_diff(x_out, x_out, v);
- felem_diff(x_out, x_out, v);
-
- felem_diff(tmp, v, x_out);
- felem_mul(y_out, tmp, r);
- felem_mul(tmp, y1, j);
- felem_diff(y_out, y_out, tmp);
- felem_diff(y_out, y_out, tmp);
-}
-
-/* point_add sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,z2}.
- *
- * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
- *
- * Note that this function does not handle P+P, infinity+P nor P+infinity
- * correctly. */
-static void point_add(felem x_out, felem y_out, felem z_out, const felem x1,
- const felem y1, const felem z1, const felem x2,
- const felem y2, const felem z2) {
- felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp;
-
- felem_square(z1z1, z1);
- felem_square(z2z2, z2);
- felem_mul(u1, x1, z2z2);
-
- felem_sum(tmp, z1, z2);
- felem_square(tmp, tmp);
- felem_diff(tmp, tmp, z1z1);
- felem_diff(tmp, tmp, z2z2);
-
- felem_mul(z2z2z2, z2, z2z2);
- felem_mul(s1, y1, z2z2z2);
-
- felem_mul(u2, x2, z1z1);
- felem_mul(z1z1z1, z1, z1z1);
- felem_mul(s2, y2, z1z1z1);
- felem_diff(h, u2, u1);
- felem_sum(i, h, h);
- felem_square(i, i);
- felem_mul(j, h, i);
- felem_diff(r, s2, s1);
- felem_sum(r, r, r);
- felem_mul(v, u1, i);
-
- felem_mul(z_out, tmp, h);
- felem_square(rr, r);
- felem_diff(x_out, rr, j);
- felem_diff(x_out, x_out, v);
- felem_diff(x_out, x_out, v);
-
- felem_diff(tmp, v, x_out);
- felem_mul(y_out, tmp, r);
- felem_mul(tmp, s1, j);
- felem_diff(y_out, y_out, tmp);
- felem_diff(y_out, y_out, tmp);
-}
-
-/* point_add_or_double_vartime sets {x_out,y_out,z_out} = {x1,y1,z1} +
- * {x2,y2,z2}.
- *
- * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
- *
- * This function handles the case where {x1,y1,z1}={x2,y2,z2}. */
-static void point_add_or_double_vartime(
- felem x_out, felem y_out, felem z_out, const felem x1, const felem y1,
- const felem z1, const felem x2, const felem y2, const felem z2) {
- felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp;
- char x_equal, y_equal;
-
- felem_square(z1z1, z1);
- felem_square(z2z2, z2);
- felem_mul(u1, x1, z2z2);
-
- felem_sum(tmp, z1, z2);
- felem_square(tmp, tmp);
- felem_diff(tmp, tmp, z1z1);
- felem_diff(tmp, tmp, z2z2);
-
- felem_mul(z2z2z2, z2, z2z2);
- felem_mul(s1, y1, z2z2z2);
-
- felem_mul(u2, x2, z1z1);
- felem_mul(z1z1z1, z1, z1z1);
- felem_mul(s2, y2, z1z1z1);
- felem_diff(h, u2, u1);
- x_equal = felem_is_zero_vartime(h);
- felem_sum(i, h, h);
- felem_square(i, i);
- felem_mul(j, h, i);
- felem_diff(r, s2, s1);
- y_equal = felem_is_zero_vartime(r);
- if (x_equal && y_equal) {
- point_double(x_out, y_out, z_out, x1, y1, z1);
- return;
- }
- felem_sum(r, r, r);
- felem_mul(v, u1, i);
-
- felem_mul(z_out, tmp, h);
- felem_square(rr, r);
- felem_diff(x_out, rr, j);
- felem_diff(x_out, x_out, v);
- felem_diff(x_out, x_out, v);
-
- felem_diff(tmp, v, x_out);
- felem_mul(y_out, tmp, r);
- felem_mul(tmp, s1, j);
- felem_diff(y_out, y_out, tmp);
- felem_diff(y_out, y_out, tmp);
-}
-
-/* copy_conditional sets out=in if mask = 0xffffffff in constant time.
- *
- * On entry: mask is either 0 or 0xffffffff. */
-static void copy_conditional(felem out, const felem in, limb mask) {
- int i;
-
- for (i = 0; i < NLIMBS; i++) {
- const limb tmp = mask & (in[i] ^ out[i]);
- out[i] ^= tmp;
- }
-}
-
-/* select_affine_point sets {out_x,out_y} to the index'th entry of table.
- * On entry: index < 16, table[0] must be zero. */
-static void select_affine_point(felem out_x, felem out_y, const limb* table,
- limb index) {
- limb i, j;
-
- memset(out_x, 0, sizeof(felem));
- memset(out_y, 0, sizeof(felem));
-
- for (i = 1; i < 16; i++) {
- limb mask = i ^ index;
- mask |= mask >> 2;
- mask |= mask >> 1;
- mask &= 1;
- mask--;
- for (j = 0; j < NLIMBS; j++, table++) {
- out_x[j] |= *table & mask;
- }
- for (j = 0; j < NLIMBS; j++, table++) {
- out_y[j] |= *table & mask;
- }
- }
-}
-
-/* select_jacobian_point sets {out_x,out_y,out_z} to the index'th entry of
- * table. On entry: index < 16, table[0] must be zero. */
-static void select_jacobian_point(felem out_x, felem out_y, felem out_z,
- const limb* table, limb index) {
- limb i, j;
-
- memset(out_x, 0, sizeof(felem));
- memset(out_y, 0, sizeof(felem));
- memset(out_z, 0, sizeof(felem));
-
- /* The implicit value at index 0 is all zero. We don't need to perform that
- * iteration of the loop because we already set out_* to zero. */
- table += 3 * NLIMBS;
-
- // Hit all entries to obscure cache profiling.
- for (i = 1; i < 16; i++) {
- limb mask = i ^ index;
- mask |= mask >> 2;
- mask |= mask >> 1;
- mask &= 1;
- mask--;
- for (j = 0; j < NLIMBS; j++, table++) {
- out_x[j] |= *table & mask;
- }
- for (j = 0; j < NLIMBS; j++, table++) {
- out_y[j] |= *table & mask;
- }
- for (j = 0; j < NLIMBS; j++, table++) {
- out_z[j] |= *table & mask;
- }
- }
-}
-
-/* scalar_base_mult sets {nx,ny,nz} = scalar*G where scalar is a little-endian
- * number. Note that the value of scalar must be less than the order of the
- * group. */
-static void scalar_base_mult(felem nx, felem ny, felem nz,
- const p256_int* scalar) {
- int i, j;
- limb n_is_infinity_mask = -1, p_is_noninfinite_mask, mask;
- u32 table_offset;
-
- felem px, py;
- felem tx, ty, tz;
-
- memset(nx, 0, sizeof(felem));
- memset(ny, 0, sizeof(felem));
- memset(nz, 0, sizeof(felem));
-
- /* The loop adds bits at positions 0, 64, 128 and 192, followed by
- * positions 32,96,160 and 224 and does this 32 times. */
- for (i = 0; i < 32; i++) {
- if (i) {
- point_double(nx, ny, nz, nx, ny, nz);
- }
- table_offset = 0;
- for (j = 0; j <= 32; j += 32) {
- char bit0 = p256_get_bit(scalar, 31 - i + j);
- char bit1 = p256_get_bit(scalar, 95 - i + j);
- char bit2 = p256_get_bit(scalar, 159 - i + j);
- char bit3 = p256_get_bit(scalar, 223 - i + j);
- limb index = bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3);
-
- select_affine_point(px, py, kPrecomputed + table_offset, index);
- table_offset += 30 * NLIMBS;
-
- /* Since scalar is less than the order of the group, we know that
- * {nx,ny,nz} != {px,py,1}, unless both are zero, which we handle
- * below. */
- point_add_mixed(tx, ty, tz, nx, ny, nz, px, py);
- /* The result of point_add_mixed is incorrect if {nx,ny,nz} is zero
- * (a.k.a. the point at infinity). We handle that situation by
- * copying the point from the table. */
- copy_conditional(nx, px, n_is_infinity_mask);
- copy_conditional(ny, py, n_is_infinity_mask);
- copy_conditional(nz, kOne, n_is_infinity_mask);
-
- /* Equally, the result is also wrong if the point from the table is
- * zero, which happens when the index is zero. We handle that by
- * only copying from {tx,ty,tz} to {nx,ny,nz} if index != 0. */
- p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index);
- mask = p_is_noninfinite_mask & ~n_is_infinity_mask;
- copy_conditional(nx, tx, mask);
- copy_conditional(ny, ty, mask);
- copy_conditional(nz, tz, mask);
- /* If p was not zero, then n is now non-zero. */
- n_is_infinity_mask &= ~p_is_noninfinite_mask;
- }
- }
-}
-
-/* point_to_affine converts a Jacobian point to an affine point. If the input
- * is the point at infinity then it returns (0, 0) in constant time. */
-static void point_to_affine(felem x_out, felem y_out, const felem nx,
- const felem ny, const felem nz) {
- felem z_inv, z_inv_sq;
- felem_inv(z_inv, nz);
- felem_square(z_inv_sq, z_inv);
- felem_mul(x_out, nx, z_inv_sq);
- felem_mul(z_inv, z_inv, z_inv_sq);
- felem_mul(y_out, ny, z_inv);
-}
-
-/* scalar_base_mult sets {nx,ny,nz} = scalar*{x,y}. */
-static void scalar_mult(felem nx, felem ny, felem nz, const felem x,
- const felem y, const p256_int* scalar) {
- int i;
- felem px, py, pz, tx, ty, tz;
- felem precomp[16][3];
- limb n_is_infinity_mask, index, p_is_noninfinite_mask, mask;
-
- /* We precompute 0,1,2,... times {x,y}. */
- memset(precomp, 0, sizeof(felem) * 3);
- memcpy(&precomp[1][0], x, sizeof(felem));
- memcpy(&precomp[1][1], y, sizeof(felem));
- memcpy(&precomp[1][2], kOne, sizeof(felem));
-
- for (i = 2; i < 16; i += 2) {
- point_double(precomp[i][0], precomp[i][1], precomp[i][2],
- precomp[i / 2][0], precomp[i / 2][1], precomp[i / 2][2]);
-
- point_add_mixed(precomp[i + 1][0], precomp[i + 1][1], precomp[i + 1][2],
- precomp[i][0], precomp[i][1], precomp[i][2], x, y);
- }
-
- memset(nx, 0, sizeof(felem));
- memset(ny, 0, sizeof(felem));
- memset(nz, 0, sizeof(felem));
- n_is_infinity_mask = -1;
-
- /* We add in a window of four bits each iteration and do this 64 times. */
- for (i = 0; i < 256; i += 4) {
- if (i) {
- point_double(nx, ny, nz, nx, ny, nz);
- point_double(nx, ny, nz, nx, ny, nz);
- point_double(nx, ny, nz, nx, ny, nz);
- point_double(nx, ny, nz, nx, ny, nz);
- }
-
- index = (p256_get_bit(scalar, 255 - i - 0) << 3) |
- (p256_get_bit(scalar, 255 - i - 1) << 2) |
- (p256_get_bit(scalar, 255 - i - 2) << 1) |
- p256_get_bit(scalar, 255 - i - 3);
-
- /* See the comments in scalar_base_mult about handling infinities. */
- select_jacobian_point(px, py, pz, precomp[0][0], index);
- point_add(tx, ty, tz, nx, ny, nz, px, py, pz);
- copy_conditional(nx, px, n_is_infinity_mask);
- copy_conditional(ny, py, n_is_infinity_mask);
- copy_conditional(nz, pz, n_is_infinity_mask);
-
- p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index);
- mask = p_is_noninfinite_mask & ~n_is_infinity_mask;
-
- copy_conditional(nx, tx, mask);
- copy_conditional(ny, ty, mask);
- copy_conditional(nz, tz, mask);
- n_is_infinity_mask &= ~p_is_noninfinite_mask;
- }
-}
-
-#define kRDigits {2, 0, 0, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfffffffd, 1} // 2^257 mod p256.p
-
-#define kRInvDigits {0x80000000, 1, 0xffffffff, 0, 0x80000001, 0xfffffffe, 1, 0x7fffffff} // 1 / 2^257 mod p256.p
-
-static const p256_int kR = { kRDigits };
-static const p256_int kRInv = { kRInvDigits };
-
-/* to_montgomery sets out = R*in. */
-static void to_montgomery(felem out, const p256_int* in) {
- p256_int in_shifted;
- int i;
-
- p256_init(&in_shifted);
- p256_modmul(&SECP256r1_p, in, 0, &kR, &in_shifted);
-
- for (i = 0; i < NLIMBS; i++) {
- if ((i & 1) == 0) {
- out[i] = P256_DIGIT(&in_shifted, 0) & kBottom29Bits;
- p256_shr(&in_shifted, 29, &in_shifted);
- } else {
- out[i] = P256_DIGIT(&in_shifted, 0) & kBottom28Bits;
- p256_shr(&in_shifted, 28, &in_shifted);
- }
- }
-
- p256_clear(&in_shifted);
-}
-
-/* from_montgomery sets out=in/R. */
-static void from_montgomery(p256_int* out, const felem in) {
- p256_int result, tmp;
- int i, top;
-
- p256_init(&result);
- p256_init(&tmp);
-
- p256_add_d(&tmp, in[NLIMBS - 1], &result);
- for (i = NLIMBS - 2; i >= 0; i--) {
- if ((i & 1) == 0) {
- top = p256_shl(&result, 29, &tmp);
- } else {
- top = p256_shl(&result, 28, &tmp);
- }
- top |= p256_add_d(&tmp, in[i], &result);
- }
-
- p256_modmul(&SECP256r1_p, &kRInv, top, &result, out);
-
- p256_clear(&result);
- p256_clear(&tmp);
-}
-
-/* p256_base_point_mul sets {out_x,out_y} = nG, where n is < the
- * order of the group. */
-void p256_base_point_mul(const p256_int* n, p256_int* out_x, p256_int* out_y) {
- felem x, y, z;
-
- scalar_base_mult(x, y, z, n);
-
- {
- felem x_affine, y_affine;
-
- point_to_affine(x_affine, y_affine, x, y, z);
- from_montgomery(out_x, x_affine);
- from_montgomery(out_y, y_affine);
- }
-}
-
-/* p256_points_mul_vartime sets {out_x,out_y} = n1*G + n2*{in_x,in_y}, where
- * n1 and n2 are < the order of the group.
- *
- * As indicated by the name, this function operates in variable time. This
- * is safe because it's used for signature validation which doesn't deal
- * with secrets. */
-void p256_points_mul_vartime(
- const p256_int* n1, const p256_int* n2, const p256_int* in_x,
- const p256_int* in_y, p256_int* out_x, p256_int* out_y) {
- felem x1, y1, z1, x2, y2, z2, px, py;
-
- /* If both scalars are zero, then the result is the point at infinity. */
- if (p256_is_zero(n1) != 0 && p256_is_zero(n2) != 0) {
- p256_clear(out_x);
- p256_clear(out_y);
- return;
- }
-
- to_montgomery(px, in_x);
- to_montgomery(py, in_y);
- scalar_base_mult(x1, y1, z1, n1);
- scalar_mult(x2, y2, z2, px, py, n2);
-
- if (p256_is_zero(n2) != 0) {
- /* If n2 == 0, then {x2,y2,z2} is zero and the result is just
- * {x1,y1,z1}. */
- } else if (p256_is_zero(n1) != 0) {
- /* If n1 == 0, then {x1,y1,z1} is zero and the result is just
- * {x2,y2,z2}. */
- memcpy(x1, x2, sizeof(x2));
- memcpy(y1, y2, sizeof(y2));
- memcpy(z1, z2, sizeof(z2));
- } else {
- /* This function handles the case where {x1,y1,z1} == {x2,y2,z2}. */
- point_add_or_double_vartime(x1, y1, z1, x1, y1, z1, x2, y2, z2);
- }
-
- point_to_affine(px, py, x1, y1, z1);
- from_montgomery(out_x, px);
- from_montgomery(out_y, py);
-}
diff --git a/libmincrypt/p256_ecdsa.c b/libmincrypt/p256_ecdsa.c
deleted file mode 100644
index f2264b0..0000000
--- a/libmincrypt/p256_ecdsa.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. 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 <string.h>
-
-#include "mincrypt/p256_ecdsa.h"
-#include "mincrypt/p256.h"
-
-int p256_ecdsa_verify(const p256_int* key_x, const p256_int* key_y,
- const p256_int* message,
- const p256_int* r, const p256_int* s) {
- p256_int u, v;
-
- // Check public key.
- if (!p256_is_valid_point(key_x, key_y)) return 0;
-
- // Check r and s are != 0 % n.
- p256_mod(&SECP256r1_n, r, &u);
- p256_mod(&SECP256r1_n, s, &v);
- if (p256_is_zero(&u) || p256_is_zero(&v)) return 0;
-
- p256_modinv_vartime(&SECP256r1_n, s, &v);
- p256_modmul(&SECP256r1_n, message, 0, &v, &u); // message / s % n
- p256_modmul(&SECP256r1_n, r, 0, &v, &v); // r / s % n
-
- p256_points_mul_vartime(&u, &v,
- key_x, key_y,
- &u, &v);
-
- p256_mod(&SECP256r1_n, &u, &u); // (x coord % p) % n
- return p256_cmp(r, &u) == 0;
-}
-
diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c
deleted file mode 100644
index 9061b3a..0000000
--- a/libmincrypt/rsa.c
+++ /dev/null
@@ -1,308 +0,0 @@
-/* rsa.c
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in the
-** documentation and/or other materials provided with the distribution.
-** * Neither the name of Google Inc. nor the names of its contributors may
-** be used to endorse or promote products derived from this software
-** without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. 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 "mincrypt/rsa.h"
-#include "mincrypt/sha.h"
-#include "mincrypt/sha256.h"
-
-// a[] -= mod
-static void subM(const RSAPublicKey* key,
- uint32_t* a) {
- int64_t A = 0;
- int i;
- for (i = 0; i < key->len; ++i) {
- A += (uint64_t)a[i] - key->n[i];
- a[i] = (uint32_t)A;
- A >>= 32;
- }
-}
-
-// return a[] >= mod
-static int geM(const RSAPublicKey* key,
- const uint32_t* a) {
- int i;
- for (i = key->len; i;) {
- --i;
- if (a[i] < key->n[i]) return 0;
- if (a[i] > key->n[i]) return 1;
- }
- return 1; // equal
-}
-
-// montgomery c[] += a * b[] / R % mod
-static void montMulAdd(const RSAPublicKey* key,
- uint32_t* c,
- const uint32_t a,
- const uint32_t* b) {
- uint64_t A = (uint64_t)a * b[0] + c[0];
- uint32_t d0 = (uint32_t)A * key->n0inv;
- uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
- int i;
-
- for (i = 1; i < key->len; ++i) {
- A = (A >> 32) + (uint64_t)a * b[i] + c[i];
- B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
- c[i - 1] = (uint32_t)B;
- }
-
- A = (A >> 32) + (B >> 32);
-
- c[i - 1] = (uint32_t)A;
-
- if (A >> 32) {
- subM(key, c);
- }
-}
-
-// montgomery c[] = a[] * b[] / R % mod
-static void montMul(const RSAPublicKey* key,
- uint32_t* c,
- const uint32_t* a,
- const uint32_t* b) {
- int i;
- for (i = 0; i < key->len; ++i) {
- c[i] = 0;
- }
- for (i = 0; i < key->len; ++i) {
- montMulAdd(key, c, a[i], b);
- }
-}
-
-// In-place public exponentiation.
-// Input and output big-endian byte array in inout.
-static void modpow(const RSAPublicKey* key,
- uint8_t* inout) {
- uint32_t a[RSANUMWORDS];
- uint32_t aR[RSANUMWORDS];
- uint32_t aaR[RSANUMWORDS];
- uint32_t* aaa = 0;
- int i;
-
- // Convert from big endian byte array to little endian word array.
- for (i = 0; i < key->len; ++i) {
- uint32_t tmp =
- (inout[((key->len - 1 - i) * 4) + 0] << 24) |
- (inout[((key->len - 1 - i) * 4) + 1] << 16) |
- (inout[((key->len - 1 - i) * 4) + 2] << 8) |
- (inout[((key->len - 1 - i) * 4) + 3] << 0);
- a[i] = tmp;
- }
-
- if (key->exponent == 65537) {
- aaa = aaR; // Re-use location.
- montMul(key, aR, a, key->rr); // aR = a * RR / R mod M
- for (i = 0; i < 16; i += 2) {
- montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M
- montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M
- }
- montMul(key, aaa, aR, a); // aaa = aR * a / R mod M
- } else if (key->exponent == 3) {
- aaa = aR; // Re-use location.
- montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */
- montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
- montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */
- }
-
- // Make sure aaa < mod; aaa is at most 1x mod too large.
- if (geM(key, aaa)) {
- subM(key, aaa);
- }
-
- // Convert to bigendian byte array
- for (i = key->len - 1; i >= 0; --i) {
- uint32_t tmp = aaa[i];
- *inout++ = tmp >> 24;
- *inout++ = tmp >> 16;
- *inout++ = tmp >> 8;
- *inout++ = tmp >> 0;
- }
-}
-
-// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature.
-// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the
-// other flavor which omits the optional parameter entirely). This code does not
-// accept signatures without the optional parameter.
-
-/*
-static const uint8_t sha_padding[RSANUMBYTES] = {
- 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30,
- 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
- 0x05, 0x00, 0x04, 0x14,
-
- // 20 bytes of hash go here.
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-};
-*/
-
-// SHA-1 of PKCS1.5 signature sha_padding for 2048 bit, as above.
-// At the location of the bytes of the hash all 00 are hashed.
-static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = {
- 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e,
- 0x6e, 0xfc, 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68,
- 0x7c, 0xfb, 0xf1, 0x67
-};
-
-/*
-static const uint8_t sha256_padding[RSANUMBYTES] = {
- 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x31, 0x30,
- 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
- 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20,
-
- // 32 bytes of hash go here.
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-};
-*/
-
-// SHA-256 of PKCS1.5 signature sha256_padding for 2048 bit, as above.
-// At the location of the bytes of the hash all 00 are hashed.
-static const uint8_t kExpectedPadSha256Rsa2048[SHA256_DIGEST_SIZE] = {
- 0xab, 0x28, 0x8d, 0x8a, 0xd7, 0xd9, 0x59, 0x92,
- 0xba, 0xcc, 0xf8, 0x67, 0x20, 0xe1, 0x15, 0x2e,
- 0x39, 0x8d, 0x80, 0x36, 0xd6, 0x6f, 0xf0, 0xfd,
- 0x90, 0xe8, 0x7d, 0x8b, 0xe1, 0x7c, 0x87, 0x59,
-};
-
-// Verify a 2048-bit RSA PKCS1.5 signature against an expected hash.
-// Both e=3 and e=65537 are supported. hash_len may be
-// SHA_DIGEST_SIZE (== 20) to indicate a SHA-1 hash, or
-// SHA256_DIGEST_SIZE (== 32) to indicate a SHA-256 hash. No other
-// values are supported.
-//
-// Returns 1 on successful verification, 0 on failure.
-int RSA_verify(const RSAPublicKey *key,
- const uint8_t *signature,
- const int len,
- const uint8_t *hash,
- const int hash_len) {
- uint8_t buf[RSANUMBYTES];
- int i;
- const uint8_t* padding_hash;
-
- if (key->len != RSANUMWORDS) {
- return 0; // Wrong key passed in.
- }
-
- if (len != sizeof(buf)) {
- return 0; // Wrong input length.
- }
-
- if (hash_len != SHA_DIGEST_SIZE &&
- hash_len != SHA256_DIGEST_SIZE) {
- return 0; // Unsupported hash.
- }
-
- if (key->exponent != 3 && key->exponent != 65537) {
- return 0; // Unsupported exponent.
- }
-
- for (i = 0; i < len; ++i) { // Copy input to local workspace.
- buf[i] = signature[i];
- }
-
- modpow(key, buf); // In-place exponentiation.
-
- // Xor sha portion, so it all becomes 00 iff equal.
- for (i = len - hash_len; i < len; ++i) {
- buf[i] ^= *hash++;
- }
-
- // Hash resulting buf, in-place.
- switch (hash_len) {
- case SHA_DIGEST_SIZE:
- padding_hash = kExpectedPadShaRsa2048;
- SHA_hash(buf, len, buf);
- break;
- case SHA256_DIGEST_SIZE:
- padding_hash = kExpectedPadSha256Rsa2048;
- SHA256_hash(buf, len, buf);
- break;
- default:
- return 0;
- }
-
- // Compare against expected hash value.
- for (i = 0; i < hash_len; ++i) {
- if (buf[i] != padding_hash[i]) {
- return 0;
- }
- }
-
- return 1; // All checked out OK.
-}
diff --git a/libmincrypt/sha.c b/libmincrypt/sha.c
deleted file mode 100644
index 5bef32e..0000000
--- a/libmincrypt/sha.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/* sha.c
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in the
-** documentation and/or other materials provided with the distribution.
-** * Neither the name of Google Inc. nor the names of its contributors may
-** be used to endorse or promote products derived from this software
-** without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. 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.
-*/
-
-// Optimized for minimal code size.
-
-#include "mincrypt/sha.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-
-#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits))))
-
-static void SHA1_Transform(SHA_CTX* ctx) {
- uint32_t W[80];
- uint32_t A, B, C, D, E;
- uint8_t* p = ctx->buf;
- int t;
-
- for(t = 0; t < 16; ++t) {
- uint32_t tmp = *p++ << 24;
- tmp |= *p++ << 16;
- tmp |= *p++ << 8;
- tmp |= *p++;
- W[t] = tmp;
- }
-
- for(; t < 80; t++) {
- W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
- }
-
- A = ctx->state[0];
- B = ctx->state[1];
- C = ctx->state[2];
- D = ctx->state[3];
- E = ctx->state[4];
-
- for(t = 0; t < 80; t++) {
- uint32_t tmp = rol(5,A) + E + W[t];
-
- if (t < 20)
- tmp += (D^(B&(C^D))) + 0x5A827999;
- else if ( t < 40)
- tmp += (B^C^D) + 0x6ED9EBA1;
- else if ( t < 60)
- tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC;
- else
- tmp += (B^C^D) + 0xCA62C1D6;
-
- E = D;
- D = C;
- C = rol(30,B);
- B = A;
- A = tmp;
- }
-
- ctx->state[0] += A;
- ctx->state[1] += B;
- ctx->state[2] += C;
- ctx->state[3] += D;
- ctx->state[4] += E;
-}
-
-static const HASH_VTAB SHA_VTAB = {
- SHA_init,
- SHA_update,
- SHA_final,
- SHA_hash,
- SHA_DIGEST_SIZE
-};
-
-void SHA_init(SHA_CTX* ctx) {
- ctx->f = &SHA_VTAB;
- ctx->state[0] = 0x67452301;
- ctx->state[1] = 0xEFCDAB89;
- ctx->state[2] = 0x98BADCFE;
- ctx->state[3] = 0x10325476;
- ctx->state[4] = 0xC3D2E1F0;
- ctx->count = 0;
-}
-
-
-void SHA_update(SHA_CTX* ctx, const void* data, int len) {
- int i = (int) (ctx->count & 63);
- const uint8_t* p = (const uint8_t*)data;
-
- ctx->count += len;
-
- while (len--) {
- ctx->buf[i++] = *p++;
- if (i == 64) {
- SHA1_Transform(ctx);
- i = 0;
- }
- }
-}
-
-
-const uint8_t* SHA_final(SHA_CTX* ctx) {
- uint8_t *p = ctx->buf;
- uint64_t cnt = ctx->count * 8;
- int i;
-
- SHA_update(ctx, (uint8_t*)"\x80", 1);
- while ((ctx->count & 63) != 56) {
- SHA_update(ctx, (uint8_t*)"\0", 1);
- }
- for (i = 0; i < 8; ++i) {
- uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
- SHA_update(ctx, &tmp, 1);
- }
-
- for (i = 0; i < 5; i++) {
- uint32_t tmp = ctx->state[i];
- *p++ = tmp >> 24;
- *p++ = tmp >> 16;
- *p++ = tmp >> 8;
- *p++ = tmp >> 0;
- }
-
- return ctx->buf;
-}
-
-/* Convenience function */
-const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest) {
- SHA_CTX ctx;
- SHA_init(&ctx);
- SHA_update(&ctx, data, len);
- memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE);
- return digest;
-}
diff --git a/libmincrypt/sha256.c b/libmincrypt/sha256.c
deleted file mode 100644
index eb6e308..0000000
--- a/libmincrypt/sha256.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/* sha256.c
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in the
-** documentation and/or other materials provided with the distribution.
-** * Neither the name of Google Inc. nor the names of its contributors may
-** be used to endorse or promote products derived from this software
-** without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. 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.
-*/
-
-// Optimized for minimal code size.
-
-#include "mincrypt/sha256.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-
-#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits))))
-#define shr(value, bits) ((value) >> (bits))
-
-static const uint32_t K[64] = {
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
- 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
- 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
- 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
- 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
- 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
- 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
- 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
- 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
-
-static void SHA256_Transform(SHA256_CTX* ctx) {
- uint32_t W[64];
- uint32_t A, B, C, D, E, F, G, H;
- uint8_t* p = ctx->buf;
- int t;
-
- for(t = 0; t < 16; ++t) {
- uint32_t tmp = *p++ << 24;
- tmp |= *p++ << 16;
- tmp |= *p++ << 8;
- tmp |= *p++;
- W[t] = tmp;
- }
-
- for(; t < 64; t++) {
- uint32_t s0 = ror(W[t-15], 7) ^ ror(W[t-15], 18) ^ shr(W[t-15], 3);
- uint32_t s1 = ror(W[t-2], 17) ^ ror(W[t-2], 19) ^ shr(W[t-2], 10);
- W[t] = W[t-16] + s0 + W[t-7] + s1;
- }
-
- A = ctx->state[0];
- B = ctx->state[1];
- C = ctx->state[2];
- D = ctx->state[3];
- E = ctx->state[4];
- F = ctx->state[5];
- G = ctx->state[6];
- H = ctx->state[7];
-
- for(t = 0; t < 64; t++) {
- uint32_t s0 = ror(A, 2) ^ ror(A, 13) ^ ror(A, 22);
- uint32_t maj = (A & B) ^ (A & C) ^ (B & C);
- uint32_t t2 = s0 + maj;
- uint32_t s1 = ror(E, 6) ^ ror(E, 11) ^ ror(E, 25);
- uint32_t ch = (E & F) ^ ((~E) & G);
- uint32_t t1 = H + s1 + ch + K[t] + W[t];
-
- H = G;
- G = F;
- F = E;
- E = D + t1;
- D = C;
- C = B;
- B = A;
- A = t1 + t2;
- }
-
- ctx->state[0] += A;
- ctx->state[1] += B;
- ctx->state[2] += C;
- ctx->state[3] += D;
- ctx->state[4] += E;
- ctx->state[5] += F;
- ctx->state[6] += G;
- ctx->state[7] += H;
-}
-
-static const HASH_VTAB SHA256_VTAB = {
- SHA256_init,
- SHA256_update,
- SHA256_final,
- SHA256_hash,
- SHA256_DIGEST_SIZE
-};
-
-void SHA256_init(SHA256_CTX* ctx) {
- ctx->f = &SHA256_VTAB;
- ctx->state[0] = 0x6a09e667;
- ctx->state[1] = 0xbb67ae85;
- ctx->state[2] = 0x3c6ef372;
- ctx->state[3] = 0xa54ff53a;
- ctx->state[4] = 0x510e527f;
- ctx->state[5] = 0x9b05688c;
- ctx->state[6] = 0x1f83d9ab;
- ctx->state[7] = 0x5be0cd19;
- ctx->count = 0;
-}
-
-
-void SHA256_update(SHA256_CTX* ctx, const void* data, int len) {
- int i = (int) (ctx->count & 63);
- const uint8_t* p = (const uint8_t*)data;
-
- ctx->count += len;
-
- while (len--) {
- ctx->buf[i++] = *p++;
- if (i == 64) {
- SHA256_Transform(ctx);
- i = 0;
- }
- }
-}
-
-
-const uint8_t* SHA256_final(SHA256_CTX* ctx) {
- uint8_t *p = ctx->buf;
- uint64_t cnt = ctx->count * 8;
- int i;
-
- SHA256_update(ctx, (uint8_t*)"\x80", 1);
- while ((ctx->count & 63) != 56) {
- SHA256_update(ctx, (uint8_t*)"\0", 1);
- }
- for (i = 0; i < 8; ++i) {
- uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
- SHA256_update(ctx, &tmp, 1);
- }
-
- for (i = 0; i < 8; i++) {
- uint32_t tmp = ctx->state[i];
- *p++ = tmp >> 24;
- *p++ = tmp >> 16;
- *p++ = tmp >> 8;
- *p++ = tmp >> 0;
- }
-
- return ctx->buf;
-}
-
-/* Convenience function */
-const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest) {
- SHA256_CTX ctx;
- SHA256_init(&ctx);
- SHA256_update(&ctx, data, len);
- memcpy(digest, SHA256_final(&ctx), SHA256_DIGEST_SIZE);
- return digest;
-}
diff --git a/libmincrypt/test/Android.mk b/libmincrypt/test/Android.mk
deleted file mode 100644
index 73ff7d0..0000000
--- a/libmincrypt/test/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := rsa_test
-LOCAL_SRC_FILES := rsa_test.c
-LOCAL_STATIC_LIBRARIES := libmincrypt
-include $(BUILD_HOST_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := ecdsa_test
-LOCAL_SRC_FILES := ecdsa_test.c
-LOCAL_STATIC_LIBRARIES := libmincrypt
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libmincrypt/test/ecdsa_test.c b/libmincrypt/test/ecdsa_test.c
deleted file mode 100644
index 24ec013..0000000
--- a/libmincrypt/test/ecdsa_test.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Google Inc. nor the names of its contributors may
- * be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. 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 <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-
-#include "mincrypt/dsa_sig.h"
-#include "mincrypt/p256.h"
-#include "mincrypt/p256_ecdsa.h"
-#include "mincrypt/sha256.h"
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-/**
- * Messages signed using:
- *
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIDw6UiziVMbjlfSpOAIpA2tcL+v1OlznZLnpadO8BGi1oAoGCCqGSM49
-AwEHoUQDQgAEZw7VAOjAXYRFuhZWYBgjahdOvkwcAnjGkxQWytZW+iS1hI3ZGE24
-6XmNka9IGxAgj2n/ip+MuZJMFoJ9DRea3g==
------END EC PRIVATE KEY-----
- */
-
-p256_int key_x = {
- .a = {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu,
- 0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u}
-};
-p256_int key_y = {
- .a = {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au,
- 0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u}
-};
-
-char* message_1 =
- "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59"
- "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3"
- "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e"
- "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe"
- "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02"
- "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3"
- "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d"
- "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08"
- "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9"
- "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6"
- "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e"
- "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de"
- "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5"
- "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d"
- "d5 9d 73 be 12";
-
-char* signature_1 =
- "30 44 02 20 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7"
- "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66"
- "7c 98 25 d9 02 20 54 f3 7f 5a e9 36 9c a2 f0 51"
- "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7"
- "ea 57 7e 88 46 12";
-
-// Same as signature 1, but with leading zeroes.
-char* message_2 =
- "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59"
- "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3"
- "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e"
- "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe"
- "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02"
- "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3"
- "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d"
- "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08"
- "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9"
- "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6"
- "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e"
- "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de"
- "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5"
- "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d"
- "d5 9d 73 be 12";
-
-char* signature_2 =
- "30 46 02 21 00 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7"
- "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66"
- "7c 98 25 d9 02 21 00 54 f3 7f 5a e9 36 9c a2 f0 51"
- "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7"
- "ea 57 7e 88 46 12";
-
-// Excessive zeroes on the signature
-char* message_3 =
- "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59"
- "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3"
- "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e"
- "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe"
- "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02"
- "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3"
- "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d"
- "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08"
- "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9"
- "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6"
- "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e"
- "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de"
- "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5"
- "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d"
- "d5 9d 73 be 12";
-
-char* signature_3 =
- "30 4c 02 24 00 00 00 00 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7"
- "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66"
- "7c 98 25 d9 02 24 00 00 00 00 54 f3 7f 5a e9 36 9c a2 f0 51"
- "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7"
- "ea 57 7e 88 46 12";
-
-
-char* good_dsa_signature_1 =
- "30 0D 02 01 01 02 08 00 A5 55 5A 01 FF A5 01";
-p256_int good_dsa_signature_1_r = {
- .a = {0x00000001U, 0x00000000U, 0x00000000U, 0x00000000U,
- 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}
-};
-p256_int good_dsa_signature_1_s = {
- .a = {0x01FFA501U, 0x00A5555AU, 0x00000000U, 0x00000000U,
- 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}
-};
-
-
-char* bad_dsa_signature_1 =
- "a0 06 02 01 01 02 01 01";
-
-char* bad_dsa_signature_2 =
- "30 07 02 01 01 02 01 01";
-
-char* bad_dsa_signature_3 =
- "30 06 82 01 01 02 01 01";
-
-char* bad_dsa_signature_4 =
- "30 06 02 00 01 02 01 01";
-
-char* bad_dsa_signature_5 =
- "30 06 02 01 01 82 01 01";
-
-char* bad_dsa_signature_6 =
- "30 05 02 01 01 02 00";
-
-char* bad_dsa_signature_7 =
- "30 06 02 01 01 02 00 01";
-
-unsigned char* parsehex(char* str, int* len) {
- // result can't be longer than input
- unsigned char* result = malloc(strlen(str));
-
- unsigned char* p = result;
- *len = 0;
-
- while (*str) {
- int b;
-
- while (isspace(*str)) str++;
-
- switch (*str) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- b = (*str - '0') << 4; break;
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- b = (*str - 'a' + 10) << 4; break;
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- b = (*str - 'A' + 10) << 4; break;
- case '\0':
- return result;
- default:
- return NULL;
- }
- str++;
-
- while (isspace(*str)) str++;
-
- switch (*str) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- b |= *str - '0'; break;
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- b |= *str - 'a' + 10; break;
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- b |= *str - 'A' + 10; break;
- default:
- return NULL;
- }
- str++;
-
- *p++ = b;
- ++*len;
- }
-
- return result;
-}
-
-int main(int arg __unused, char** argv __unused) {
-
- unsigned char hash_buf[SHA256_DIGEST_SIZE];
-
- unsigned char* message;
- int mlen;
- unsigned char* signature;
- int slen;
-
- p256_int hash;
- p256_int r;
- p256_int s;
-
- int success = 1;
-
-#define CHECK_DSA_SIG(sig, good) do {\
- message = parsehex(sig, &mlen); \
- int result = dsa_sig_unpack(message, mlen, &r, &s); \
- printf(#sig ": %s\n", result ? "good" : "bad"); \
- success = success && !(good ^ result); \
- free(message); \
- } while(0)
-#define CHECK_GOOD_DSA_SIG(n) do {\
- CHECK_DSA_SIG(good_dsa_signature_##n, 1); \
- int result = !memcmp(P256_DIGITS(&good_dsa_signature_##n##_r), P256_DIGITS(&r), \
- P256_NBYTES); \
- success = success && result; \
- printf(" R value %s\n", result ? "good" : "bad"); \
- result = !memcmp(P256_DIGITS(&good_dsa_signature_##n##_s), P256_DIGITS(&s), \
- P256_NBYTES); \
- success = success && result; \
- printf(" S value %s\n", result ? "good" : "bad"); \
- } while (0)
-#define CHECK_BAD_DSA_SIG(n) \
- CHECK_DSA_SIG(bad_dsa_signature_##n, 0)
-
- CHECK_GOOD_DSA_SIG(1);
-
- CHECK_BAD_DSA_SIG(1);
- CHECK_BAD_DSA_SIG(2);
- CHECK_BAD_DSA_SIG(3);
- CHECK_BAD_DSA_SIG(4);
- CHECK_BAD_DSA_SIG(5);
- CHECK_BAD_DSA_SIG(6);
- CHECK_BAD_DSA_SIG(7);
-
-
-#define TEST_MESSAGE(n) do {\
- message = parsehex(message_##n, &mlen); \
- SHA256_hash(message, mlen, hash_buf); \
- p256_from_bin(hash_buf, &hash); \
- signature = parsehex(signature_##n, &slen); \
- int result = dsa_sig_unpack(signature, slen, &r, &s); \
- if (result) { result = p256_ecdsa_verify(&key_x, &key_y, &hash, &r, &s); } \
- printf("message %d: %s\n", n, result ? "verified" : "not verified"); \
- success = success && result; \
- free(signature); \
- } while(0)
-
- TEST_MESSAGE(1);
- TEST_MESSAGE(2);
- TEST_MESSAGE(3);
-
- printf("\n%s\n\n", success ? "PASS" : "FAIL");
-
- return !success;
-}
diff --git a/libmincrypt/test/rsa_test.c b/libmincrypt/test/rsa_test.c
deleted file mode 100644
index 055138f..0000000
--- a/libmincrypt/test/rsa_test.c
+++ /dev/null
@@ -1,842 +0,0 @@
-/*
-** Copyright 2013, The Android Open Source Project
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in the
-** documentation and/or other materials provided with the distribution.
-** * Neither the name of Google Inc. nor the names of its contributors may
-** be used to endorse or promote products derived from this software
-** without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED BY Google Inc. ``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 Google Inc. 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 <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-
-#include "mincrypt/rsa.h"
-#include "mincrypt/sha.h"
-
-#ifndef __unused
-#define __unused __attribute__((unused))
-#endif
-
-// RSA test data taken from:
-//
-// ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt
-
-// This is the result (reformatted) of running DumpPublicKey on:
-//
-// # Example 15: A 2048-bit RSA key pair
-// # -----------------------------------
-//
-//
-// # Public key
-// # ----------
-//
-// # Modulus:
-// df 27 1f d2 5f 86 44 49 6b 0c 81 be 4b d5 02 97
-// ef 09 9b 00 2a 6f d6 77 27 eb 44 9c ea 56 6e d6
-// a3 98 1a 71 31 2a 14 1c ab c9 81 5c 12 09 e3 20
-// a2 5b 32 46 4e 99 99 f1 8c a1 3a 9f d3 89 25 58
-// f9 e0 ad ef dd 36 50 dd 23 a3 f0 36 d6 0f e3 98
-// 84 37 06 a4 0b 0b 84 62 c8 be e3 bc e1 2f 1f 28
-// 60 c2 44 4c dc 6a 44 47 6a 75 ff 4a a2 42 73 cc
-// be 3b f8 02 48 46 5f 8f f8 c3 a7 f3 36 7d fc 0d
-// f5 b6 50 9a 4f 82 81 1c ed d8 1c da aa 73 c4 91
-// da 41 21 70 d5 44 d4 ba 96 b9 7f 0a fc 80 65 49
-// 8d 3a 49 fd 91 09 92 a1 f0 72 5b e2 4f 46 5c fe
-// 7e 0e ab f6 78 99 6c 50 bc 5e 75 24 ab f7 3f 15
-// e5 be f7 d5 18 39 4e 31 38 ce 49 44 50 6a aa af
-// 3f 9b 23 6d ca b8 fc 00 f8 7a f5 96 fd c3 d9 d6
-// c7 5c d5 08 36 2f ae 2c be dd cc 4c 74 50 b1 7b
-// 77 6c 07 9e cc a1 f2 56 35 1a 43 b9 7d be 21 53
-//
-// # Exponent:
-// 01 00 01
-
-RSAPublicKey key_15 = {
- .len = 64,
- .n0inv = 0xf0053525,
- .n = {2109612371u,890913721u,3433165398u,2003568542u,
- 1951445371u,3202206796u,909094444u,3344749832u,
- 4257470934u,4168807830u,3401120768u,1067131757u,
- 1349167791u,953043268u,406408753u,3854497749u,
- 2885107477u,3160306980u,2023320656u,2114890742u,
- 1330011390u,4034026466u,2433323681u,2369407485u,
- 4236272969u,2528739082u,3578057914u,3661701488u,
- 2859713681u,3990363354u,1333952796u,4122366106u,
- 914226189u,4173572083u,1212571535u,3191601154u,
- 2722264012u,1786117962u,3697951815u,1623344204u,
- 3777961768u,3367953340u,185304162u,2218198692u,
- 3591365528u,597946422u,3711324381u,4192251375u,
- 3548980568u,2359376543u,1318689265u,2723885638u,
- 302637856u,2882109788u,824841244u,2744654449u,
- 3931533014u,669729948u,711972471u,4010384128u,
- 1272251031u,1795981758u,1602634825u,3743883218u},
- .rr = {820482522u,2494434288u,1082168230u,731376296u,
- 1306039452u,3139792975u,2575869288u,3874938710u,
- 3198185181u,153506080u,1236489694u,1061859740u,
- 1174461268u,115279508u,1782749185u,238124145u,
- 3587596076u,2259236093u,1112265915u,4048059865u,
- 3890381098u,999426242u,794481771u,3804065613u,
- 2786019148u,461403875u,3072256692u,4079652654u,
- 3056719901u,1871565394u,212974856u,3359008174u,
- 1397773937u,3796256698u,914342841u,1097174457u,
- 3322220191u,3170814748u,2439215020u,618719336u,
- 3629353460u,496817177u,317052742u,380264245u,
- 1976007217u,2697736152u,312540864u,4291855337u,
- 697006561u,4234182488u,3904590917u,2609582216u,
- 451424084u,1805773827u,776344974u,1064489733u,
- 2633377036u,1954826648u,3202815814u,2240368662u,
- 2618582484u,2211196815u,4107362845u,3640258615u},
- .exponent = 65537,
-};
-
-// PKCS#1 v1.5 Signature Example 15.1
-
-char* message_1 =
- "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59"
- "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3"
- "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e"
- "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe"
- "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02"
- "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3"
- "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d"
- "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08"
- "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9"
- "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6"
- "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e"
- "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de"
- "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5"
- "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d"
- "d5 9d 73 be 12";
-
-char* signature_1 =
- "b7 5a 54 66 b6 5d 0f 30 0e f5 38 33 f2 17 5c 8a"
- "34 7a 38 04 fc 63 45 1d c9 02 f0 b7 1f 90 83 45"
- "9e d3 7a 51 79 a3 b7 23 a5 3f 10 51 64 2d 77 37"
- "4c 4c 6c 8d bb 1c a2 05 25 f5 c9 f3 2d b7 76 95"
- "35 56 da 31 29 0e 22 19 74 82 ce b6 99 06 c4 6a"
- "75 8f b0 e7 40 9b a8 01 07 7d 2a 0a 20 ea e7 d1"
- "d6 d3 92 ab 49 57 e8 6b 76 f0 65 2d 68 b8 39 88"
- "a7 8f 26 e1 11 72 ea 60 9b f8 49 fb bd 78 ad 7e"
- "dc e2 1d e6 62 a0 81 36 8c 04 06 07 ce e2 9d b0"
- "62 72 27 f4 49 63 ad 17 1d 22 93 b6 33 a3 92 e3"
- "31 dc a5 4f e3 08 27 52 f4 3f 63 c1 61 b4 47 a4"
- "c6 5a 68 75 67 0d 5f 66 00 fc c8 60 a1 ca eb 0a"
- "88 f8 fd ec 4e 56 43 98 a5 c4 6c 87 f6 8c e0 70"
- "01 f6 21 3a be 0a b5 62 5f 87 d1 90 25 f0 8d 81"
- "da c7 bd 45 86 bc 93 82 19 1f 6d 28 80 f6 22 7e"
- "5d f3 ee d2 1e 77 92 d2 49 48 04 87 f3 65 52 61";
-
-// PKCS#1 v1.5 Signature Example 15.2
-
-char *message_2 =
- "c1 4b 4c 60 75 b2 f9 aa d6 61 de f4 ec fd 3c b9 "
- "33 c6 23 f4 e6 3b f5 34 10 d2 f0 16 d1 ab 98 e2 "
- "72 9e cc f8 00 6c d8 e0 80 50 73 7d 95 fd bf 29 "
- "6b 66 f5 b9 79 2a 90 29 36 c4 f7 ac 69 f5 14 53 "
- "ce 43 69 45 2d c2 2d 96 f0 37 74 81 14 66 20 00 "
- "dd 9c d3 a5 e1 79 f4 e0 f8 1f a6 a0 31 1c a1 ae "
- "e6 51 9a 0f 63 ce c7 8d 27 bb 72 63 93 fb 7f 1f "
- "88 cd e7 c9 7f 8a 66 cd 66 30 12 81 da c3 f3 a4 "
- "33 24 8c 75 d6 c2 dc d7 08 b6 a9 7b 0a 3f 32 5e "
- "0b 29 64 f8 a5 81 9e 47 9b ";
-
-char* signature_2 =
- "af a7 34 34 62 be a1 22 cc 14 9f ca 70 ab da e7"
- "94 46 67 7d b5 37 36 66 af 7d c3 13 01 5f 4d e7"
- "86 e6 e3 94 94 6f ad 3c c0 e2 b0 2b ed ba 50 47"
- "fe 9e 2d 7d 09 97 05 e4 a3 9f 28 68 32 79 cf 0a"
- "c8 5c 15 30 41 22 42 c0 e9 18 95 3b e0 00 e9 39"
- "cf 3b f1 82 52 5e 19 93 70 fa 79 07 eb a6 9d 5d"
- "b4 63 10 17 c0 e3 6d f7 03 79 b5 db 8d 4c 69 5a"
- "97 9a 8e 61 73 22 40 65 d7 dc 15 13 2e f2 8c d8"
- "22 79 51 63 06 3b 54 c6 51 14 1b e8 6d 36 e3 67"
- "35 bc 61 f3 1f ca 57 4e 53 09 f3 a3 bb df 91 ef"
- "f1 2b 99 e9 cc 17 44 f1 ee 9a 1b d2 2c 5b ad 96"
- "ad 48 19 29 25 1f 03 43 fd 36 bc f0 ac de 7f 11"
- "e5 ad 60 97 77 21 20 27 96 fe 06 1f 9a da 1f c4"
- "c8 e0 0d 60 22 a8 35 75 85 ff e9 fd d5 93 31 a2"
- "8c 4a a3 12 15 88 fb 6c f6 83 96 d8 ac 05 46 59"
- "95 00 c9 70 85 00 a5 97 2b d5 4f 72 cf 8d b0 c8";
-
-// PKCS#1 v1.5 Signature Example 15.3
-
-char* message_3 =
- "d0 23 71 ad 7e e4 8b bf db 27 63 de 7a 84 3b 94 "
- "08 ce 5e b5 ab f8 47 ca 3d 73 59 86 df 84 e9 06 "
- "0b db cd d3 a5 5b a5 5d de 20 d4 76 1e 1a 21 d2 "
- "25 c1 a1 86 f4 ac 4b 30 19 d3 ad f7 8f e6 33 46 "
- "67 f5 6f 70 c9 01 a0 a2 70 0c 6f 0d 56 ad d7 19 "
- "59 2d c8 8f 6d 23 06 c7 00 9f 6e 7a 63 5b 4c b3 "
- "a5 02 df e6 8d dc 58 d0 3b e1 0a 11 70 00 4f e7 "
- "4d d3 e4 6b 82 59 1f f7 54 14 f0 c4 a0 3e 60 5e "
- "20 52 4f 24 16 f1 2e ca 58 9f 11 1b 75 d6 39 c6 "
- "1b aa 80 ca fd 05 cf 35 00 24 4a 21 9e d9 ce d9 "
- "f0 b1 02 97 18 2b 65 3b 52 6f 40 0f 29 53 ba 21 "
- "4d 5b cd 47 88 41 32 87 2a e9 0d 4d 6b 1f 42 15 "
- "39 f9 f3 46 62 a5 6d c0 e7 b4 b9 23 b6 23 1e 30 "
- "d2 67 67 97 81 7f 7c 33 7b 5a c8 24 ba 93 14 3b "
- "33 81 fa 3d ce 0e 6a eb d3 8e 67 73 51 87 b1 eb "
- "d9 5c 02 ";
-
-char* signature_3 =
- "3b ac 63 f8 6e 3b 70 27 12 03 10 6b 9c 79 aa bd"
- "9f 47 7c 56 e4 ee 58 a4 fc e5 ba f2 ca b4 96 0f"
- "88 39 1c 9c 23 69 8b e7 5c 99 ae df 9e 1a bf 17"
- "05 be 1d ac 33 14 0a db 48 eb 31 f4 50 bb 9e fe"
- "83 b7 b9 0d b7 f1 57 6d 33 f4 0c 1c ba 4b 8d 6b"
- "1d 33 23 56 4b 0f 17 74 11 4f a7 c0 8e 6d 1e 20"
- "dd 8f bb a9 b6 ac 7a d4 1e 26 b4 56 8f 4a 8a ac"
- "bf d1 78 a8 f8 d2 c9 d5 f5 b8 81 12 93 5a 8b c9"
- "ae 32 cd a4 0b 8d 20 37 55 10 73 50 96 53 68 18"
- "ce 2b 2d b7 1a 97 72 c9 b0 dd a0 9a e1 01 52 fa"
- "11 46 62 18 d0 91 b5 3d 92 54 30 61 b7 29 4a 55"
- "be 82 ff 35 d5 c3 2f a2 33 f0 5a aa c7 58 50 30"
- "7e cf 81 38 3c 11 16 74 39 7b 1a 1b 9d 3b f7 61"
- "2c cb e5 ba cd 2b 38 f0 a9 83 97 b2 4c 83 65 8f"
- "b6 c0 b4 14 0e f1 19 70 c4 63 0d 44 34 4e 76 ea"
- "ed 74 dc be e8 11 db f6 57 59 41 f0 8a 65 23 b8";
-
-// PKCS#1 v1.5 Signature Example 15.4
-
-char* message_4 =
- "29 03 55 84 ab 7e 02 26 a9 ec 4b 02 e8 dc f1 27 "
- "2d c9 a4 1d 73 e2 82 00 07 b0 f6 e2 1f ec cd 5b "
- "d9 db b9 ef 88 cd 67 58 76 9e e1 f9 56 da 7a d1 "
- "84 41 de 6f ab 83 86 db c6 93 ";
-
-char* signature_4 =
- "28 d8 e3 fc d5 dd db 21 ff bd 8d f1 63 0d 73 77"
- "aa 26 51 e1 4c ad 1c 0e 43 cc c5 2f 90 7f 94 6d"
- "66 de 72 54 e2 7a 6c 19 0e b0 22 ee 89 ec f6 22"
- "4b 09 7b 71 06 8c d6 07 28 a1 ae d6 4b 80 e5 45"
- "7b d3 10 6d d9 17 06 c9 37 c9 79 5f 2b 36 36 7f"
- "f1 53 dc 25 19 a8 db 9b df 2c 80 74 30 c4 51 de"
- "17 bb cd 0c e7 82 b3 e8 f1 02 4d 90 62 4d ea 7f"
- "1e ed c7 42 0b 7e 7c aa 65 77 ce f4 31 41 a7 26"
- "42 06 58 0e 44 a1 67 df 5e 41 ee a0 e6 9a 80 54"
- "54 c4 0e ef c1 3f 48 e4 23 d7 a3 2d 02 ed 42 c0"
- "ab 03 d0 a7 cf 70 c5 86 0a c9 2e 03 ee 00 5b 60"
- "ff 35 03 42 4b 98 cc 89 45 68 c7 c5 6a 02 33 55"
- "1c eb e5 88 cf 8b 01 67 b7 df 13 ad ca d8 28 67"
- "68 10 49 9c 70 4d a7 ae 23 41 4d 69 e3 c0 d2 db"
- "5d cb c2 61 3b c1 20 42 1f 9e 36 53 c5 a8 76 72"
- "97 64 3c 7e 07 40 de 01 63 55 45 3d 6c 95 ae 72";
-
-// PKCS#1 v1.5 Signature Example 15.5
-
-char* message_5 =
- "bd a3 a1 c7 90 59 ea e5 98 30 8d 3d f6 09 ";
-
-char* signature_5 =
- "a1 56 17 6c b9 67 77 c7 fb 96 10 5d bd 91 3b c4"
- "f7 40 54 f6 80 7c 60 08 a1 a9 56 ea 92 c1 f8 1c"
- "b8 97 dc 4b 92 ef 9f 4e 40 66 8d c7 c5 56 90 1a"
- "cb 6c f2 69 fe 61 5b 0f b7 2b 30 a5 13 38 69 23"
- "14 b0 e5 87 8a 88 c2 c7 77 4b d1 69 39 b5 ab d8"
- "2b 44 29 d6 7b d7 ac 8e 5e a7 fe 92 4e 20 a6 ec"
- "66 22 91 f2 54 8d 73 4f 66 34 86 8b 03 9a a5 f9"
- "d4 d9 06 b2 d0 cb 85 85 bf 42 85 47 af c9 1c 6e"
- "20 52 dd cd 00 1c 3e f8 c8 ee fc 3b 6b 2a 82 b6"
- "f9 c8 8c 56 f2 e2 c3 cb 0b e4 b8 0d a9 5e ba 37"
- "1d 8b 5f 60 f9 25 38 74 3d db b5 da 29 72 c7 1f"
- "e7 b9 f1 b7 90 26 8a 0e 77 0f c5 eb 4d 5d d8 52"
- "47 d4 8a e2 ec 3f 26 25 5a 39 85 52 02 06 a1 f2"
- "68 e4 83 e9 db b1 d5 ca b1 90 91 76 06 de 31 e7"
- "c5 18 2d 8f 15 1b f4 1d fe cc ae d7 cd e6 90 b2"
- "16 47 10 6b 49 0c 72 9d 54 a8 fe 28 02 a6 d1 26";
-
-// PKCS#1 v1.5 Signature Example 15.6
-
-char* message_6 =
- "c1 87 91 5e 4e 87 da 81 c0 8e d4 35 6a 0c ce ac "
- "1c 4f b5 c0 46 b4 52 81 b3 87 ec 28 f1 ab fd 56 "
- "7e 54 6b 23 6b 37 d0 1a e7 1d 3b 28 34 36 5d 3d "
- "f3 80 b7 50 61 b7 36 b0 13 0b 07 0b e5 8a e8 a4 "
- "6d 12 16 63 61 b6 13 db c4 7d fa eb 4c a7 46 45 "
- "6c 2e 88 83 85 52 5c ca 9d d1 c3 c7 a9 ad a7 6d "
- "6c ";;
-
-char* signature_6 =
- "9c ab 74 16 36 08 66 9f 75 55 a3 33 cf 19 6f e3"
- "a0 e9 e5 eb 1a 32 d3 4b b5 c8 5f f6 89 aa ab 0e"
- "3e 65 66 8e d3 b1 15 3f 94 eb 3d 8b e3 79 b8 ee"
- "f0 07 c4 a0 2c 70 71 ce 30 d8 bb 34 1e 58 c6 20"
- "f7 3d 37 b4 ec bf 48 be 29 4f 6c 9e 0e cb 5e 63"
- "fe c4 1f 12 0e 55 53 df a0 eb eb bb 72 64 0a 95"
- "37 ba dc b4 51 33 02 29 d9 f7 10 f6 2e 3e d8 ec"
- "78 4e 50 ee 1d 92 62 b4 26 71 34 00 11 d7 d0 98"
- "c6 f2 55 7b 21 31 fa 9b d0 25 46 36 59 7e 88 ec"
- "b3 5a 24 0e f0 fd 85 95 71 24 df 80 80 fe e1 e1"
- "49 af 93 99 89 e8 6b 26 c8 5a 58 81 fa e8 67 3d"
- "9f d4 08 00 dd 13 4e b9 bd b6 41 0f 42 0b 0a a9"
- "7b 20 ef cf 2e b0 c8 07 fa eb 83 a3 cc d9 b5 1d"
- "45 53 e4 1d fc 0d f6 ca 80 a1 e8 1d c2 34 bb 83"
- "89 dd 19 5a 38 b4 2d e4 ed c4 9d 34 64 78 b9 f1"
- "1f 05 57 20 5f 5b 0b d7 ff e9 c8 50 f3 96 d7 c4";;
-
-// PKCS#1 v1.5 Signature Example 15.7
-
-char* message_7 =
- "ab fa 2e cb 7d 29 bd 5b cb 99 31 ce 2b ad 2f 74 "
- "38 3e 95 68 3c ee 11 02 2f 08 e8 e7 d0 b8 fa 05 "
- "8b f9 eb 7e b5 f9 88 68 b5 bb 1f b5 c3 1c ed a3 "
- "a6 4f 1a 12 cd f2 0f cd 0e 5a 24 6d 7a 17 73 d8 "
- "db a0 e3 b2 77 54 5b ab e5 8f 2b 96 e3 f4 ed c1 "
- "8e ab f5 cd 2a 56 0f ca 75 fe 96 e0 7d 85 9d ef "
- "b2 56 4f 3a 34 f1 6f 11 e9 1b 3a 71 7b 41 af 53 "
- "f6 60 53 23 00 1a a4 06 c6 ";
-
-char* signature_7 =
- "c4 b4 37 bc f7 03 f3 52 e1 fa f7 4e b9 62 20 39"
- "42 6b 56 72 ca f2 a7 b3 81 c6 c4 f0 19 1e 7e 4a"
- "98 f0 ee bc d6 f4 17 84 c2 53 7f f0 f9 9e 74 98"
- "2c 87 20 1b fb c6 5e ae 83 2d b7 1d 16 da ca db"
- "09 77 e5 c5 04 67 9e 40 be 0f 9d b0 6f fd 84 8d"
- "d2 e5 c3 8a 7e c0 21 e7 f6 8c 47 df d3 8c c3 54"
- "49 3d 53 39 b4 59 5a 5b f3 1e 3f 8f 13 81 68 07"
- "37 3d f6 ad 0d c7 e7 31 e5 1a d1 9e b4 75 4b 13"
- "44 85 84 2f e7 09 d3 78 44 4d 8e 36 b1 72 4a 4f"
- "da 21 ca fe e6 53 ab 80 74 7f 79 52 ee 80 4d ea"
- "b1 03 9d 84 13 99 45 bb f4 be 82 00 87 53 f3 c5"
- "4c 78 21 a1 d2 41 f4 21 79 c7 94 ef 70 42 bb f9"
- "95 56 56 22 2e 45 c3 43 69 a3 84 69 7b 6a e7 42"
- "e1 8f a5 ca 7a ba d2 7d 9f e7 10 52 e3 31 0d 0f"
- "52 c8 d1 2e a3 3b f0 53 a3 00 f4 af c4 f0 98 df"
- "4e 6d 88 67 79 d6 45 94 d3 69 15 8f db c1 f6 94";
-
-// PKCS#1 v1.5 Signature Example 15.8
-
-char* message_8 =
- "df 40 44 a8 9a 83 e9 fc bf 12 62 54 0a e3 03 8b "
- "bc 90 f2 b2 62 8b f2 a4 46 7a c6 77 22 d8 54 6b "
- "3a 71 cb 0e a4 16 69 d5 b4 d6 18 59 c1 b4 e4 7c "
- "ec c5 93 3f 75 7e c8 6d b0 64 4e 31 18 12 d0 0f "
- "b8 02 f0 34 00 63 9c 0e 36 4d ae 5a eb c5 79 1b "
- "c6 55 76 23 61 bc 43 c5 3d 3c 78 86 76 8f 79 68 "
- "c1 c5 44 c6 f7 9f 7b e8 20 c7 e2 bd 2f 9d 73 e6 "
- "2d ed 6d 2e 93 7e 6a 6d ae f9 0e e3 7a 1a 52 a5 "
- "4f 00 e3 1a dd d6 48 94 cf 4c 02 e1 60 99 e2 9f "
- "9e b7 f1 a7 bb 7f 84 c4 7a 2b 59 48 13 be 02 a1 "
- "7b 7f c4 3b 34 c2 2c 91 92 52 64 12 6c 89 f8 6b "
- "b4 d8 7f 3e f1 31 29 6c 53 a3 08 e0 33 1d ac 8b "
- "af 3b 63 42 22 66 ec ef 2b 90 78 15 35 db da 41 "
- "cb d0 cf 22 a8 cb fb 53 2e c6 8f c6 af b2 ac 06 ";
-
-char* signature_8 =
- "14 14 b3 85 67 ae 6d 97 3e de 4a 06 84 2d cc 0e"
- "05 59 b1 9e 65 a4 88 9b db ab d0 fd 02 80 68 29"
- "13 ba cd 5d c2 f0 1b 30 bb 19 eb 81 0b 7d 9d ed"
- "32 b2 84 f1 47 bb e7 71 c9 30 c6 05 2a a7 34 13"
- "90 a8 49 f8 1d a9 cd 11 e5 ec cf 24 6d ba e9 5f"
- "a9 58 28 e9 ae 0c a3 55 03 25 32 6d ee f9 f4 95"
- "30 ba 44 1b ed 4a c2 9c 02 9c 9a 27 36 b1 a4 19"
- "0b 85 08 4a d1 50 42 6b 46 d7 f8 5b d7 02 f4 8d"
- "ac 5f 71 33 0b c4 23 a7 66 c6 5c c1 dc ab 20 d3"
- "d3 bb a7 2b 63 b3 ef 82 44 d4 2f 15 7c b7 e3 a8"
- "ba 5c 05 27 2c 64 cc 1a d2 1a 13 49 3c 39 11 f6"
- "0b 4e 9f 4e cc 99 00 eb 05 6e e5 9d 6f e4 b8 ff"
- "6e 80 48 cc c0 f3 8f 28 36 fd 3d fe 91 bf 4a 38"
- "6e 1e cc 2c 32 83 9f 0c a4 d1 b2 7a 56 8f a9 40"
- "dd 64 ad 16 bd 01 25 d0 34 8e 38 30 85 f0 88 94"
- "86 1c a1 89 87 22 7d 37 b4 2b 58 4a 83 57 cb 04";
-
-// PKCS#1 v1.5 Signature Example 15.9
-
-char* message_9 =
- "ea 94 1f f0 6f 86 c2 26 92 7f cf 0e 3b 11 b0 87 "
- "26 76 17 0c 1b fc 33 bd a8 e2 65 c7 77 71 f9 d0 "
- "85 01 64 a5 ee cb cc 5c e8 27 fb fa 07 c8 52 14 "
- "79 6d 81 27 e8 ca a8 18 94 ea 61 ce b1 44 9e 72 "
- "fe a0 a4 c9 43 b2 da 6d 9b 10 5f e0 53 b9 03 9a "
- "9c c5 3d 42 0b 75 39 fa b2 23 9c 6b 51 d1 7e 69 "
- "4c 95 7d 4b 0f 09 84 46 18 79 a0 75 9c 44 01 be "
- "ec d4 c6 06 a0 af bd 7a 07 6f 50 a2 df c2 80 7f "
- "24 f1 91 9b aa 77 46 d3 a6 4e 26 8e d3 f5 f8 e6 "
- "da 83 a2 a5 c9 15 2f 83 7c b0 78 12 bd 5b a7 d3 "
- "a0 79 85 de 88 11 3c 17 96 e9 b4 66 ec 29 9c 5a "
- "c1 05 9e 27 f0 94 15 ";
-
-char* signature_9 =
- "ce eb 84 cc b4 e9 09 92 65 65 07 21 ee a0 e8 ec"
- "89 ca 25 bd 35 4d 4f 64 56 49 67 be 9d 4b 08 b3"
- "f1 c0 18 53 9c 9d 37 1c f8 96 1f 22 91 fb e0 dc"
- "2f 2f 95 fe a4 7b 63 9f 1e 12 f4 bc 38 1c ef 0c"
- "2b 7a 7b 95 c3 ad f2 76 05 b7 f6 39 98 c3 cb ad"
- "54 28 08 c3 82 2e 06 4d 4a d1 40 93 67 9e 6e 01"
- "41 8a 6d 5c 05 96 84 cd 56 e3 4e d6 5a b6 05 b8"
- "de 4f cf a6 40 47 4a 54 a8 25 1b bb 73 26 a4 2d"
- "08 58 5c fc fc 95 67 69 b1 5b 6d 7f df 7d a8 4f"
- "81 97 6e aa 41 d6 92 38 0f f1 0e ae cf e0 a5 79"
- "68 29 09 b5 52 1f ad e8 54 d7 97 b8 a0 34 5b 9a"
- "86 4e 05 88 f6 ca dd bf 65 f1 77 99 8e 18 0d 1f"
- "10 24 43 e6 dc a5 3a 94 82 3c aa 9c 3b 35 f3 22"
- "58 3c 70 3a f6 74 76 15 9e c7 ec 93 d1 76 9b 30"
- "0a f0 e7 15 7d c2 98 c6 cd 2d ee 22 62 f8 cd dc"
- "10 f1 1e 01 74 14 71 bb fd 65 18 a1 75 73 45 75";
-
-// PKCS#1 v1.5 Signature Example 15.10
-
-char* message_10 =
- "d8 b8 16 45 c1 3c d7 ec f5 d0 0e d2 c9 1b 9a cd "
- "46 c1 55 68 e5 30 3c 4a 97 75 ed e7 6b 48 40 3d "
- "6b e5 6c 05 b6 b1 cf 77 c6 e7 5d e0 96 c5 cb 35 "
- "51 cb 6f a9 64 f3 c8 79 cf 58 9d 28 e1 da 2f 9d "
- "ec ";
-
-char* signature_10 =
- "27 45 07 4c a9 71 75 d9 92 e2 b4 47 91 c3 23 c5"
- "71 67 16 5c dd 8d a5 79 cd ef 46 86 b9 bb 40 4b"
- "d3 6a 56 50 4e b1 fd 77 0f 60 bf a1 88 a7 b2 4b"
- "0c 91 e8 81 c2 4e 35 b0 4d c4 dd 4c e3 85 66 bc"
- "c9 ce 54 f4 9a 17 5f c9 d0 b2 25 22 d9 57 90 47"
- "f9 ed 42 ec a8 3f 76 4a 10 16 39 97 94 7e 7d 2b"
- "52 ff 08 98 0e 7e 7c 22 57 93 7b 23 f3 d2 79 d4"
- "cd 17 d6 f4 95 54 63 73 d9 83 d5 36 ef d7 d1 b6"
- "71 81 ca 2c b5 0a c6 16 c5 c7 ab fb b9 26 0b 91"
- "b1 a3 8e 47 24 20 01 ff 45 2f 8d e1 0c a6 ea ea"
- "dc af 9e dc 28 95 6f 28 a7 11 29 1f c9 a8 08 78"
- "b8 ba 4c fe 25 b8 28 1c b8 0b c9 cd 6d 2b d1 82"
- "52 46 ee be 25 2d 99 57 ef 93 70 73 52 08 4e 6d"
- "36 d4 23 55 1b f2 66 a8 53 40 fb 4a 6a f3 70 88"
- "0a ab 07 15 3d 01 f4 8d 08 6d f0 bf be c0 5e 7b"
- "44 3b 97 e7 17 18 97 0e 2f 4b f6 20 23 e9 5b 67";
-
-// PKCS#1 v1.5 Signature Example 15.11
-
-char* message_11 =
- "e5 73 9b 6c 14 c9 2d 51 0d 95 b8 26 93 33 37 ff "
- "0d 24 ef 72 1a c4 ef 64 c2 ba d2 64 be 8b 44 ef "
- "a1 51 6e 08 a2 7e b6 b6 11 d3 30 1d f0 06 2d ae "
- "fc 73 a8 c0 d9 2e 2c 52 1f ac bc 7b 26 47 38 76 "
- "7e a6 fc 97 d5 88 a0 ba f6 ce 50 ad f7 9e 60 0b "
- "d2 9e 34 5f cb 1d ba 71 ac 5c 02 89 02 3f e4 a8 "
- "2b 46 a5 40 77 19 19 7d 2e 95 8e 35 31 fd 54 ae "
- "f9 03 aa bb 43 55 f8 83 18 99 4e d3 c3 dd 62 f4 "
- "20 a7 ";
-
-char* signature_11 =
- "be 40 a5 fb 94 f1 13 e1 b3 ef f6 b6 a3 39 86 f2"
- "02 e3 63 f0 74 83 b7 92 e6 8d fa 55 54 df 04 66"
- "cc 32 15 09 50 78 3b 4d 96 8b 63 9a 04 fd 2f b9"
- "7f 6e b9 67 02 1f 5a dc cb 9f ca 95 ac c8 f2 cd"
- "88 5a 38 0b 0a 4e 82 bc 76 07 64 db ab 88 c1 e6"
- "c0 25 5c aa 94 f2 32 19 9d 6f 59 7c c9 14 5b 00"
- "e3 d4 ba 34 6b 55 9a 88 33 ad 15 16 ad 51 63 f0"
- "16 af 6a 59 83 1c 82 ea 13 c8 22 4d 84 d0 76 5a"
- "9d 12 38 4d a4 60 a8 53 1b 4c 40 7e 04 f4 f3 50"
- "70 9e b9 f0 8f 5b 22 0f fb 45 ab f6 b7 5d 15 79"
- "fd 3f 1e b5 5f c7 5b 00 af 8b a3 b0 87 82 7f e9"
- "ae 9f b4 f6 c5 fa 63 03 1f e5 82 85 2f e2 83 4f"
- "9c 89 bf f5 3e 25 52 21 6b c7 c1 d4 a3 d5 dc 2b"
- "a6 95 5c d9 b1 7d 13 63 e7 fe e8 ed 76 29 75 3f"
- "f3 12 5e dd 48 52 1a e3 b9 b0 32 17 f4 49 6d 0d"
- "8e de 57 ac bc 5b d4 de ae 74 a5 6f 86 67 1d e2";
-
-// PKCS#1 v1.5 Signature Example 15.12
-
-char* message_12 =
- "7a f4 28 35 91 7a 88 d6 b3 c6 71 6b a2 f5 b0 d5 "
- "b2 0b d4 e2 e6 e5 74 e0 6a f1 ee f7 c8 11 31 be "
- "22 bf 81 28 b9 cb c6 ec 00 27 5b a8 02 94 a5 d1 "
- "17 2d 08 24 a7 9e 8f dd 83 01 83 e4 c0 0b 96 78 "
- "28 67 b1 22 7f ea 24 9a ad 32 ff c5 fe 00 7b c5 "
- "1f 21 79 2f 72 8d ed a8 b5 70 8a a9 9c ab ab 20 "
- "a4 aa 78 3e d8 6f 0f 27 b5 d5 63 f4 2e 07 15 8c "
- "ea 72 d0 97 aa 68 87 ec 41 1d d0 12 91 2a 5e 03 "
- "2b bf a6 78 50 71 44 bc c9 5f 39 b5 8b e7 bf d1 "
- "75 9a db 9a 91 fa 1d 6d 82 26 a8 34 3a 8b 84 9d "
- "ae 76 f7 b9 82 24 d5 9e 28 f7 81 f1 3e ce 60 5f "
- "84 f6 c9 0b ae 5f 8c f3 78 81 6f 40 20 a7 dd a1 "
- "be d9 0c 92 a2 36 34 d2 03 fa c3 fc d8 6d 68 d3 "
- "18 2a 7d 9c ca be 7b 07 95 f5 c6 55 e9 ac c4 e3 "
- "ec 18 51 40 d1 0c ef 05 34 64 ab 17 5c 83 bd 83 "
- "93 5e 3d ab af 34 62 ee be 63 d1 5f 57 3d 26 9a ";
-
-char* signature_12 =
- "4e 78 c5 90 2b 80 79 14 d1 2f a5 37 ae 68 71 c8"
- "6d b8 02 1e 55 d1 ad b8 eb 0c cf 1b 8f 36 ab 7d"
- "ad 1f 68 2e 94 7a 62 70 72 f0 3e 62 73 71 78 1d"
- "33 22 1d 17 4a be 46 0d bd 88 56 0c 22 f6 90 11"
- "6e 2f bb e6 e9 64 36 3a 3e 52 83 bb 5d 94 6e f1"
- "c0 04 7e ba 03 8c 75 6c 40 be 79 23 05 58 09 b0"
- "e9 f3 4a 03 a5 88 15 eb dd e7 67 93 1f 01 8f 6f"
- "18 78 f2 ef 4f 47 dd 37 40 51 dd 48 68 5d ed 6e"
- "fb 3e a8 02 1f 44 be 1d 7d 14 93 98 f9 8e a9 c0"
- "8d 62 88 8e bb 56 19 2d 17 74 7b 6b 8e 17 09 54"
- "31 f1 25 a8 a8 e9 96 2a a3 1c 28 52 64 e0 8f b2"
- "1a ac 33 6c e6 c3 8a a3 75 e4 2b c9 2a b0 ab 91"
- "03 84 31 e1 f9 2c 39 d2 af 5d ed 7e 43 bc 15 1e"
- "6e be a4 c3 e2 58 3a f3 43 7e 82 c4 3c 5e 3b 5b"
- "07 cf 03 59 68 3d 22 98 e3 59 48 ed 80 6c 06 3c"
- "60 6e a1 78 15 0b 1e fc 15 85 69 34 c7 25 5c fe";
-
-// PKCS#1 v1.5 Signature Example 15.13
-
-char* message_13 =
- "eb ae f3 f9 f2 3b df e5 fa 6b 8a f4 c2 08 c1 89 "
- "f2 25 1b f3 2f 5f 13 7b 9d e4 40 63 78 68 6b 3f "
- "07 21 f6 2d 24 cb 86 88 d6 fc 41 a2 7c ba e2 1d "
- "30 e4 29 fe ac c7 11 19 41 c2 77 ";
-
-char* signature_13 =
- "c4 8d be f5 07 11 4f 03 c9 5f af be b4 df 1b fa"
- "88 e0 18 4a 33 cc 4f 8a 9a 10 35 ff 7f 82 2a 5e"
- "38 cd a1 87 23 91 5f f0 78 24 44 29 e0 f6 08 1c"
- "14 fd 83 33 1f a6 5c 6b a7 bb 9a 12 db f6 62 23"
- "74 cd 0c a5 7d e3 77 4e 2b d7 ae 82 36 77 d0 61"
- "d5 3a e9 c4 04 0d 2d a7 ef 70 14 f3 bb dc 95 a3"
- "61 a4 38 55 c8 ce 9b 97 ec ab ce 17 4d 92 62 85"
- "14 2b 53 4a 30 87 f9 f4 ef 74 51 1e c7 42 b0 d5"
- "68 56 03 fa f4 03 b5 07 2b 98 5d f4 6a df 2d 25"
- "29 a0 2d 40 71 1e 21 90 91 70 52 37 1b 79 b7 49"
- "b8 3a bf 0a e2 94 86 c3 f2 f6 24 77 b2 bd 36 2b"
- "03 9c 01 3c 0c 50 76 ef 52 0d bb 40 5f 42 ce e9"
- "54 25 c3 73 a9 75 e1 cd d0 32 c4 96 22 c8 50 79"
- "b0 9e 88 da b2 b1 39 69 ef 7a 72 39 73 78 10 40"
- "45 9f 57 d5 01 36 38 48 3d e2 d9 1c b3 c4 90 da"
- "81 c4 6d e6 cd 76 ea 8a 0c 8f 6f e3 31 71 2d 24";
-
-// PKCS#1 v1.5 Signature Example 15.14
-
-char* message_14 =
- "c5 a2 71 12 78 76 1d fc dd 4f 0c 99 e6 f5 61 9d "
- "6c 48 b5 d4 c1 a8 09 82 fa a6 b4 cf 1c f7 a6 0f "
- "f3 27 ab ef 93 c8 01 42 9e fd e0 86 40 85 81 46 "
- "10 56 ac c3 3f 3d 04 f5 ad a2 12 16 ca cd 5f d1 "
- "f9 ed 83 20 3e 0e 2f e6 13 8e 3e ae 84 24 e5 91 "
- "5a 08 3f 3f 7a b7 60 52 c8 be 55 ae 88 2d 6e c1 "
- "48 2b 1e 45 c5 da e9 f4 10 15 40 53 27 02 2e c3 "
- "2f 0e a2 42 97 63 b2 55 04 3b 19 58 ee 3c f6 d6 "
- "39 83 59 6e b3 85 84 4f 85 28 cc 9a 98 65 83 5d "
- "c5 11 3c 02 b8 0d 0f ca 68 aa 25 e7 2b ca ae b3 "
- "cf 9d 79 d8 4f 98 4f d4 17 ";
-
-char* signature_14 =
- "6b d5 25 7a a0 66 11 fb 46 60 08 7c b4 bc 4a 9e"
- "44 91 59 d3 16 52 bd 98 08 44 da f3 b1 c7 b3 53"
- "f8 e5 61 42 f7 ea 98 57 43 3b 18 57 3b 4d ee de"
- "81 8a 93 b0 29 02 97 78 3f 1a 2f 23 cb c7 27 97"
- "a6 72 53 7f 01 f6 24 84 cd 41 62 c3 21 4b 9a c6"
- "28 22 4c 5d e0 1f 32 bb 9b 76 b2 73 54 f2 b1 51"
- "d0 e8 c4 21 3e 46 15 ad 0b c7 1f 51 5e 30 0d 6a"
- "64 c6 74 34 11 ff fd e8 e5 ff 19 0e 54 92 30 43"
- "12 6e cf c4 c4 53 90 22 66 8f b6 75 f2 5c 07 e2"
- "00 99 ee 31 5b 98 d6 af ec 4b 1a 9a 93 dc 33 49"
- "6a 15 bd 6f de 16 63 a7 d4 9b 9f 1e 63 9d 38 66"
- "4b 37 a0 10 b1 f3 5e 65 86 82 d9 cd 63 e5 7d e0"
- "f1 5e 8b dd 09 65 58 f0 7e c0 ca a2 18 a8 c0 6f"
- "47 88 45 39 40 28 7c 9d 34 b6 d4 0a 3f 09 bf 77"
- "99 fe 98 ae 4e b4 9f 3f f4 1c 50 40 a5 0c ef c9"
- "bd f2 39 4b 74 9c f1 64 48 0d f1 ab 68 80 27 3b";
-
-// PKCS#1 v1.5 Signature Example 15.15
-
-char* message_15 =
- "9b f8 aa 25 3b 87 2e a7 7a 7e 23 47 6b e2 6b 23 "
- "29 57 8c f6 ac 9e a2 80 5b 35 7f 6f c3 ad 13 0d "
- "ba eb 3d 86 9a 13 cc e7 a8 08 bb bb c9 69 85 7e "
- "03 94 5c 7b b6 1d f1 b5 c2 58 9b 8e 04 6c 2a 5d "
- "7e 40 57 b1 a7 4f 24 c7 11 21 63 64 28 85 29 ec "
- "95 70 f2 51 97 21 3b e1 f5 c2 e5 96 f8 bf 8b 2c "
- "f3 cb 38 aa 56 ff e5 e3 1d f7 39 58 20 e9 4e cf "
- "3b 11 89 a9 65 dc f9 a9 cb 42 98 d3 c8 8b 29 23 "
- "c1 9f c6 bc 34 aa ce ca d4 e0 93 1a 7c 4e 5d 73 "
- "dc 86 df a7 98 a8 47 6d 82 46 3e ef aa 90 a8 a9 "
- "19 2a b0 8b 23 08 8d d5 8e 12 80 f7 d7 2e 45 48 "
- "39 6b aa c1 12 25 2d d5 c5 34 6a db 20 04 a2 f7 "
- "10 1c cc 89 9c c7 fa fa e8 bb e2 95 73 88 96 a5 "
- "b2 01 22 85 01 4e f6 ";
-
-char* signature_15 =
- "27 f7 f4 da 9b d6 10 10 6e f5 7d 32 38 3a 44 8a"
- "8a 62 45 c8 3d c1 30 9c 6d 77 0d 35 7b a8 9e 73"
- "f2 ad 08 32 06 2e b0 fe 0a c9 15 57 5b cd 6b 8b"
- "ca db 4e 2b a6 fa 9d a7 3a 59 17 51 52 b2 d4 fe"
- "72 b0 70 c9 b7 37 9e 50 00 0e 55 e6 c2 69 f6 65"
- "8c 93 79 72 79 7d 3a dd 69 f1 30 e3 4b 85 bd ec"
- "9f 3a 9b 39 22 02 d6 f3 e4 30 d0 9c ac a8 22 77"
- "59 ab 82 5f 70 12 d2 ff 4b 5b 62 c8 50 4d ba d8"
- "55 c0 5e dd 5c ab 5a 4c cc dc 67 f0 1d d6 51 7c"
- "7d 41 c4 3e 2a 49 57 af f1 9d b6 f1 8b 17 85 9a"
- "f0 bc 84 ab 67 14 6e c1 a4 a6 0a 17 d7 e0 5f 8b"
- "4f 9c ed 6a d1 09 08 d8 d7 8f 7f c8 8b 76 ad c8"
- "29 0f 87 da f2 a7 be 10 ae 40 85 21 39 5d 54 ed"
- "25 56 fb 76 61 85 4a 73 0c e3 d8 2c 71 a8 d4 93"
- "ec 49 a3 78 ac 8a 3c 74 43 9f 7c c5 55 ba 13 f8"
- "59 07 08 90 ee 18 ff 65 8f a4 d7 41 96 9d 70 a5";
-
-// PKCS#1 v1.5 Signature Example 15.16
-
-char* message_16 =
- "32 47 48 30 e2 20 37 54 c8 bf 06 81 dc 4f 84 2a "
- "fe 36 09 30 37 86 16 c1 08 e8 33 65 6e 56 40 c8 "
- "68 56 88 5b b0 5d 1e b9 43 8e fe de 67 92 63 de "
- "07 cb 39 55 3f 6a 25 e0 06 b0 a5 23 11 a0 63 ca "
- "08 82 66 d2 56 4f f6 49 0c 46 b5 60 98 18 54 8f "
- "88 76 4d ad 34 a2 5e 3a 85 d5 75 02 3f 0b 9e 66 "
- "50 48 a0 3c 35 05 79 a9 d3 24 46 c7 bb 96 cc 92 "
- "e0 65 ab 94 d3 c8 95 2e 8d f6 8e f0 d9 fa 45 6b "
- "3a 06 bb 80 e3 bb c4 b2 8e 6a 94 b6 d0 ff 76 96 "
- "a6 4e fe 05 e7 35 fe a0 25 d7 bd bc 41 39 f3 a3 "
- "b5 46 07 5c ba 7e fa 94 73 74 d3 f0 ac 80 a6 8d "
- "76 5f 5d f6 21 0b ca 06 9a 2d 88 64 7a f7 ea 04 "
- "2d ac 69 0c b5 73 78 ec 07 77 61 4f b8 b6 5f f4 "
- "53 ca 6b 7d ce 60 98 45 1a 2f 8c 0d a9 bf ec f1 "
- "fd f3 91 bb aa 4e 2a 91 ca 18 a1 12 1a 75 23 a2 "
- "ab d4 25 14 f4 89 e8 ";
-
-char* signature_16 =
- "69 17 43 72 57 c2 2c cb 54 03 29 0c 3d ee 82 d9"
- "cf 75 50 b3 1b d3 1c 51 bd 57 bf d3 5d 45 2a b4"
- "db 7c 4b e6 b2 e2 5a c9 a5 9a 1d 2a 7f eb 62 7f"
- "0a fd 49 76 b3 00 3c c9 cf fd 88 96 50 5e c3 82"
- "f2 65 10 4d 4c f8 c9 32 fa 9f e8 6e 00 87 07 95"
- "99 12 38 9d a4 b2 d6 b3 69 b3 6a 5e 72 e2 9d 24"
- "c9 a9 8c 9d 31 a3 ab 44 e6 43 e6 94 12 66 a4 7a"
- "45 e3 44 6c e8 77 6a be 24 1a 8f 5f c6 42 3b 24"
- "b1 ff 25 0d c2 c3 a8 17 23 53 56 10 77 e8 50 a7"
- "69 b2 5f 03 25 da c8 89 65 a3 b9 b4 72 c4 94 e9"
- "5f 71 9b 4e ac 33 2c aa 7a 65 c7 df e4 6d 9a a7"
- "e6 e0 0f 52 5f 30 3d d6 3a b7 91 92 18 90 18 68"
- "f9 33 7f 8c d2 6a af e6 f3 3b 7f b2 c9 88 10 af"
- "19 f7 fc b2 82 ba 15 77 91 2c 1d 36 89 75 fd 5d"
- "44 0b 86 e1 0c 19 97 15 fa 0b 6f 42 50 b5 33 73"
- "2d 0b ef e1 54 51 50 fc 47 b8 76 de 09 b0 0a 94";
-
-// PKCS#1 v1.5 Signature Example 15.17
-
-char* message_17 =
- "00 8e 59 50 5e af b5 50 aa e5 e8 45 58 4c eb b0 "
- "0b 6d e1 73 3e 9f 95 d4 2c 88 2a 5b be b5 ce 1c "
- "57 e1 19 e7 c0 d4 da ca 9f 1f f7 87 02 17 f7 cf "
- "d8 a6 b3 73 97 7c ac 9c ab 8e 71 e4 20 ";
-
-char* signature_17 =
- "92 25 03 b6 73 ee 5f 3e 69 1e 1c a8 5e 9f f4 17"
- "3c f7 2b 05 ac 2c 13 1d a5 60 35 93 e3 bc 25 9c"
- "94 c1 f7 d3 a0 6a 5b 98 91 bf 11 3f a3 9e 59 ff"
- "7c 1e d6 46 5e 90 80 49 cb 89 e4 e1 25 cd 37 d2"
- "ff d9 22 7a 41 b4 a0 a1 9c 0a 44 fb bf 3d e5 5b"
- "ab 80 20 87 a3 bb 8d 4f f6 68 ee 6b bb 8a d8 9e"
- "68 57 a7 9a 9c 72 78 19 90 df cf 92 cd 51 94 04"
- "c9 50 f1 3d 11 43 c3 18 4f 1d 25 0c 90 e1 7a c6"
- "ce 36 16 3b 98 95 62 7a d6 ff ec 14 22 44 1f 55"
- "e4 49 9d ba 9b e8 95 46 ae 8b c6 3c ca 01 dd 08"
- "46 3a e7 f1 fc e3 d8 93 99 69 38 77 8c 18 12 e6"
- "74 ad 9c 30 9c 5a cc a3 fd e4 4e 7d d8 69 59 93"
- "e9 c1 fa 87 ac da 99 ec e5 c8 49 9e 46 89 57 ad"
- "66 35 9b f1 2a 51 ad be 78 d3 a2 13 b4 49 bf 0b"
- "5f 8d 4d 49 6a cf 03 d3 03 3b 7c cd 19 6b c2 2f"
- "68 fb 7b ef 4f 69 7c 5e a2 b3 50 62 f4 8a 36 dd";
-
-// PKCS#1 v1.5 Signature Example 15.18
-
-char* message_18 =
- "6a bc 54 cf 8d 1d ff 1f 53 b1 7d 81 60 36 88 78 "
- "a8 78 8c c6 d2 2f a5 c2 25 8c 88 e6 60 b0 9a 89 "
- "33 f9 f2 c0 50 4d da dc 21 f6 e7 5e 0b 83 3b eb "
- "55 52 29 de e6 56 b9 04 7b 92 f6 2e 76 b8 ff cc "
- "60 da b0 6b 80 ";
-
-char* signature_18 =
- "0b 6d af 42 f7 a8 62 14 7e 41 74 93 c2 c4 01 ef"
- "ae 32 63 6a b4 cb d4 41 92 bb f5 f1 95 b5 0a e0"
- "96 a4 75 a1 61 4f 0a 9f a8 f7 a0 26 cb 46 c6 50"
- "6e 51 8e 33 d8 3e 56 47 7a 87 5a ca 8c 7e 71 4c"
- "e1 bd bd 61 ef 5d 53 52 39 b3 3f 2b fd d6 17 71"
- "ba b6 27 76 d7 81 71 a1 42 3c ea 87 31 f8 2e 60"
- "76 6d 64 54 26 56 20 b1 5f 5c 5a 58 4f 55 f9 5b"
- "80 2f e7 8c 57 4e d5 da cf c8 31 f3 cf 2b 05 02"
- "c0 b2 98 f2 5c cf 11 f9 73 b3 1f 85 e4 74 42 19"
- "85 f3 cf f7 02 df 39 46 ef 0a 66 05 68 21 11 b2"
- "f5 5b 1f 8a b0 d2 ea 3a 68 3c 69 98 5e ad 93 ed"
- "44 9e a4 8f 03 58 dd f7 08 02 cb 41 de 2f d8 3f"
- "3c 80 80 82 d8 49 36 94 8e 0c 84 a1 31 b4 92 78"
- "27 46 05 27 bb 5c d2 4b fa b7 b4 8e 07 1b 24 17"
- "19 30 f9 97 63 27 2f 97 97 bc b7 6f 1d 24 81 57"
- "55 58 fc f2 60 b1 f0 e5 54 eb b3 df 3c fc b9 58";
-
-// PKCS#1 v1.5 Signature Example 15.19
-
-char* message_19 =
- "af 2d 78 15 2c f1 0e fe 01 d2 74 f2 17 b1 77 f6 "
- "b0 1b 5e 74 9f 15 67 71 5d a3 24 85 9c d3 dd 88 "
- "db 84 8e c7 9f 48 db ba 7b 6f 1d 33 11 1e f3 1b "
- "64 89 9e 73 91 c2 bf fd 69 f4 90 25 cf 20 1f c5 "
- "85 db d1 54 2c 1c 77 8a 2c e7 a7 ee 10 8a 30 9f "
- "ec a2 6d 13 3a 5f fe dc 4e 86 9d cd 76 56 59 6a "
- "c8 42 7e a3 ef 6e 3f d7 8f e9 9d 8d dc 71 d8 39 "
- "f6 78 6e 0d a6 e7 86 bd 62 b3 a4 f1 9b 89 1a 56 "
- "15 7a 55 4e c2 a2 b3 9e 25 a1 d7 c7 d3 73 21 c7 "
- "a1 d9 46 cf 4f be 75 8d 92 76 f0 85 63 44 9d 67 "
- "41 4a 2c 03 0f 42 51 cf e2 21 3d 04 a5 41 06 37 "
- "87 ";
-
-char* signature_19 =
- "20 9c 61 15 78 57 38 7b 71 e2 4b f3 dd 56 41 45"
- "50 50 3b ec 18 0f f5 3b dd 9b ac 06 2a 2d 49 95"
- "09 bf 99 12 81 b7 95 27 df 91 36 61 5b 7a 6d 9d"
- "b3 a1 03 b5 35 e0 20 2a 2c ac a1 97 a7 b7 4e 53"
- "56 f3 dd 59 5b 49 ac fd 9d 30 04 9a 98 ca 88 f6"
- "25 bc a1 d5 f2 2a 39 2d 8a 74 9e fb 6e ed 9b 78"
- "21 d3 11 0a c0 d2 44 19 9e cb 4a a3 d7 35 a8 3a"
- "2e 88 93 c6 bf 85 81 38 3c ca ee 83 46 35 b7 fa"
- "1f af fa 45 b1 3d 15 c1 da 33 af 71 e8 93 03 d6"
- "80 90 ff 62 ee 61 5f df 5a 84 d1 20 71 1d a5 3c"
- "28 89 19 8a b3 83 17 a9 73 4a b2 7d 67 92 4c ea"
- "74 15 6f f9 9b ef 98 76 bb 5c 33 9e 93 74 52 83"
- "e1 b3 4e 07 22 26 b8 80 45 e0 17 e9 f0 5b 2a 8c"
- "41 67 40 25 8e 22 3b 26 90 02 74 91 73 22 73 f3"
- "22 9d 9e f2 b1 b3 80 7e 32 10 18 92 0a d3 e5 3d"
- "ae 47 e6 d9 39 5c 18 4b 93 a3 74 c6 71 fa a2 ce";
-
-// PKCS#1 v1.5 Signature Example 15.20
-
-char* message_20 =
- "40 ee 99 24 58 d6 f6 14 86 d2 56 76 a9 6d d2 cb "
- "93 a3 7f 04 b1 78 48 2f 2b 18 6c f8 82 15 27 0d "
- "ba 29 d7 86 d7 74 b0 c5 e7 8c 7f 6e 56 a9 56 e7 "
- "f7 39 50 a2 b0 c0 c1 0a 08 db cd 67 e5 b2 10 bb "
- "21 c5 8e 27 67 d4 4f 7d d4 01 4e 39 66 14 3b f7 "
- "e3 d6 6f f0 c0 9b e4 c5 5f 93 b3 99 94 b8 51 8d "
- "9c 1d 76 d5 b4 73 74 de a0 8f 15 7d 57 d7 06 34 "
- "97 8f 38 56 e0 e5 b4 81 af bb db 5a 3a c4 8d 48 "
- "4b e9 2c 93 de 22 91 78 35 4c 2d e5 26 e9 c6 5a "
- "31 ed e1 ef 68 cb 63 98 d7 91 16 84 fe c0 ba bc "
- "3a 78 1a 66 66 07 83 50 69 74 d0 e1 48 25 10 1c "
- "3b fa ea ";
-
-char* signature_20 =
- "92 75 02 b8 24 af c4 25 13 ca 65 70 de 33 8b 8a"
- "64 c3 a8 5e b8 28 d3 19 36 24 f2 7e 8b 10 29 c5"
- "5c 11 9c 97 33 b1 8f 58 49 b3 50 09 18 bc c0 05"
- "51 d9 a8 fd f5 3a 97 74 9f a8 dc 48 0d 6f e9 74"
- "2a 58 71 f9 73 92 65 28 97 2a 1a f4 9e 39 25 b0"
- "ad f1 4a 84 27 19 b4 a5 a2 d8 9f a9 c0 b6 60 5d"
- "21 2b ed 1e 67 23 b9 34 06 ad 30 e8 68 29 a5 c7"
- "19 b8 90 b3 89 30 6d c5 50 64 86 ee 2f 36 a8 df"
- "e0 a9 6a f6 78 c9 cb d6 af f3 97 ca 20 0e 3e dc"
- "1e 36 bd 2f 08 b3 1d 54 0c 0c b2 82 a9 55 9e 4a"
- "dd 4f c9 e6 49 2e ed 0c cb d3 a6 98 2e 5f aa 2d"
- "dd 17 be 47 41 7c 80 b4 e5 45 2d 31 f7 24 01 a0"
- "42 32 51 09 54 4d 95 4c 01 93 90 79 d4 09 a5 c3"
- "78 d7 51 2d fc 2d 2a 71 ef cc 34 32 a7 65 d1 c6"
- "a5 2c fc e8 99 cd 79 b1 5b 4f c3 72 36 41 ef 6b"
- "d0 0a cc 10 40 7e 5d f5 8d d1 c3 c5 c5 59 a5 06";
-
-
-unsigned char* parsehex(char* str, int* len) {
- // result can't be longer than input
- unsigned char* result = malloc(strlen(str));
-
- unsigned char* p = result;
- *len = 0;
-
- while (*str) {
- int b;
-
- while (isspace(*str)) str++;
-
- switch (*str) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- b = (*str - '0') << 4; break;
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- b = (*str - 'a' + 10) << 4; break;
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- b = (*str - 'A' + 10) << 4; break;
- case '\0':
- return result;
- default:
- return NULL;
- }
- str++;
-
- while (isspace(*str)) str++;
-
- switch (*str) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- b |= *str - '0'; break;
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- b |= *str - 'a' + 10; break;
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- b |= *str - 'A' + 10; break;
- default:
- return NULL;
- }
- str++;
-
- *p++ = b;
- ++*len;
- }
-
- return result;
-}
-
-
-int main(int arg __unused, char** argv __unused) {
-
- unsigned char hash[SHA_DIGEST_SIZE];
-
- unsigned char* message;
- int mlen;
- unsigned char* signature;
- int slen;
-
-#define TEST_MESSAGE(n) do {\
- message = parsehex(message_##n, &mlen); \
- SHA_hash(message, mlen, hash); \
- signature = parsehex(signature_##n, &slen); \
- int result = RSA_verify(&key_15, signature, slen, hash, sizeof(hash)); \
- printf("message %d: %s\n", n, result ? "verified" : "not verified"); \
- success = success && result; \
- } while(0)
-
- int success = 1;
-
- TEST_MESSAGE(1);
- TEST_MESSAGE(2);
- TEST_MESSAGE(3);
- TEST_MESSAGE(4);
- TEST_MESSAGE(5);
- TEST_MESSAGE(6);
- TEST_MESSAGE(7);
- TEST_MESSAGE(8);
- TEST_MESSAGE(9);
- TEST_MESSAGE(10);
- TEST_MESSAGE(11);
- TEST_MESSAGE(12);
- TEST_MESSAGE(13);
- TEST_MESSAGE(14);
- TEST_MESSAGE(15);
- TEST_MESSAGE(16);
- TEST_MESSAGE(17);
- TEST_MESSAGE(18);
- TEST_MESSAGE(19);
- TEST_MESSAGE(20);
-
- printf("\n%s\n\n", success ? "PASS" : "FAIL");
-
- return !success;
-}
diff --git a/libmincrypt/tools/Android.mk b/libmincrypt/tools/Android.mk
deleted file mode 100644
index 3154914..0000000
--- a/libmincrypt/tools/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := dumpkey
-LOCAL_SRC_FILES := DumpPublicKey.java
-LOCAL_JAR_MANIFEST := DumpPublicKey.mf
-LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java
deleted file mode 100644
index 3eb1398..0000000
--- a/libmincrypt/tools/DumpPublicKey.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dumpkey;
-
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-import java.io.FileInputStream;
-import java.math.BigInteger;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.KeyStore;
-import java.security.Key;
-import java.security.PublicKey;
-import java.security.Security;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.ECPoint;
-
-/**
- * Command line tool to extract RSA public keys from X.509 certificates
- * and output source code with data initializers for the keys.
- * @hide
- */
-class DumpPublicKey {
- /**
- * @param key to perform sanity checks on
- * @return version number of key. Supported versions are:
- * 1: 2048-bit RSA key with e=3 and SHA-1 hash
- * 2: 2048-bit RSA key with e=65537 and SHA-1 hash
- * 3: 2048-bit RSA key with e=3 and SHA-256 hash
- * 4: 2048-bit RSA key with e=65537 and SHA-256 hash
- * @throws Exception if the key has the wrong size or public exponent
- */
- static int checkRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
- BigInteger pubexp = key.getPublicExponent();
- BigInteger modulus = key.getModulus();
- int version;
-
- if (pubexp.equals(BigInteger.valueOf(3))) {
- version = useSHA256 ? 3 : 1;
- } else if (pubexp.equals(BigInteger.valueOf(65537))) {
- version = useSHA256 ? 4 : 2;
- } else {
- throw new Exception("Public exponent should be 3 or 65537 but is " +
- pubexp.toString(10) + ".");
- }
-
- if (modulus.bitLength() != 2048) {
- throw new Exception("Modulus should be 2048 bits long but is " +
- modulus.bitLength() + " bits.");
- }
-
- return version;
- }
-
- /**
- * @param key to perform sanity checks on
- * @return version number of key. Supported versions are:
- * 5: 256-bit EC key with curve NIST P-256
- * @throws Exception if the key has the wrong size or public exponent
- */
- static int checkEC(ECPublicKey key) throws Exception {
- if (key.getParams().getCurve().getField().getFieldSize() != 256) {
- throw new Exception("Curve must be NIST P-256");
- }
-
- return 5;
- }
-
- /**
- * Perform sanity check on public key.
- */
- static int check(PublicKey key, boolean useSHA256) throws Exception {
- if (key instanceof RSAPublicKey) {
- return checkRSA((RSAPublicKey) key, useSHA256);
- } else if (key instanceof ECPublicKey) {
- if (!useSHA256) {
- throw new Exception("Must use SHA-256 with EC keys!");
- }
- return checkEC((ECPublicKey) key);
- } else {
- throw new Exception("Unsupported key class: " + key.getClass().getName());
- }
- }
-
- /**
- * @param key to output
- * @return a String representing this public key. If the key is a
- * version 1 key, the string will be a C initializer; this is
- * not true for newer key versions.
- */
- static String printRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
- int version = check(key, useSHA256);
-
- BigInteger N = key.getModulus();
-
- StringBuilder result = new StringBuilder();
-
- int nwords = N.bitLength() / 32; // # of 32 bit integers in modulus
-
- if (version > 1) {
- result.append("v");
- result.append(Integer.toString(version));
- result.append(" ");
- }
-
- result.append("{");
- result.append(nwords);
-
- BigInteger B = BigInteger.valueOf(0x100000000L); // 2^32
- BigInteger N0inv = B.subtract(N.modInverse(B)); // -1 / N[0] mod 2^32
-
- result.append(",0x");
- result.append(N0inv.toString(16));
-
- BigInteger R = BigInteger.valueOf(2).pow(N.bitLength());
- BigInteger RR = R.multiply(R).mod(N); // 2^4096 mod N
-
- // Write out modulus as little endian array of integers.
- result.append(",{");
- for (int i = 0; i < nwords; ++i) {
- long n = N.mod(B).longValue();
- result.append(n);
-
- if (i != nwords - 1) {
- result.append(",");
- }
-
- N = N.divide(B);
- }
- result.append("}");
-
- // Write R^2 as little endian array of integers.
- result.append(",{");
- for (int i = 0; i < nwords; ++i) {
- long rr = RR.mod(B).longValue();
- result.append(rr);
-
- if (i != nwords - 1) {
- result.append(",");
- }
-
- RR = RR.divide(B);
- }
- result.append("}");
-
- result.append("}");
- return result.toString();
- }
-
- /**
- * @param key to output
- * @return a String representing this public key. If the key is a
- * version 1 key, the string will be a C initializer; this is
- * not true for newer key versions.
- */
- static String printEC(ECPublicKey key) throws Exception {
- int version = checkEC(key);
-
- StringBuilder result = new StringBuilder();
-
- result.append("v");
- result.append(Integer.toString(version));
- result.append(" ");
-
- BigInteger X = key.getW().getAffineX();
- BigInteger Y = key.getW().getAffineY();
- int nbytes = key.getParams().getCurve().getField().getFieldSize() / 8; // # of 32 bit integers in X coordinate
-
- result.append("{");
- result.append(nbytes);
-
- BigInteger B = BigInteger.valueOf(0x100L); // 2^8
-
- // Write out Y coordinate as array of characters.
- result.append(",{");
- for (int i = 0; i < nbytes; ++i) {
- long n = X.mod(B).longValue();
- result.append(n);
-
- if (i != nbytes - 1) {
- result.append(",");
- }
-
- X = X.divide(B);
- }
- result.append("}");
-
- // Write out Y coordinate as array of characters.
- result.append(",{");
- for (int i = 0; i < nbytes; ++i) {
- long n = Y.mod(B).longValue();
- result.append(n);
-
- if (i != nbytes - 1) {
- result.append(",");
- }
-
- Y = Y.divide(B);
- }
- result.append("}");
-
- result.append("}");
- return result.toString();
- }
-
- static String print(PublicKey key, boolean useSHA256) throws Exception {
- if (key instanceof RSAPublicKey) {
- return printRSA((RSAPublicKey) key, useSHA256);
- } else if (key instanceof ECPublicKey) {
- return printEC((ECPublicKey) key);
- } else {
- throw new Exception("Unsupported key class: " + key.getClass().getName());
- }
- }
-
- public static void main(String[] args) {
- if (args.length < 1) {
- System.err.println("Usage: DumpPublicKey certfile ... > source.c");
- System.exit(1);
- }
- Security.addProvider(new BouncyCastleProvider());
- try {
- for (int i = 0; i < args.length; i++) {
- FileInputStream input = new FileInputStream(args[i]);
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
-
- boolean useSHA256 = false;
- String sigAlg = cert.getSigAlgName();
- if ("SHA1withRSA".equals(sigAlg) || "MD5withRSA".equals(sigAlg)) {
- // SignApk has historically accepted "MD5withRSA"
- // certificates, but treated them as "SHA1withRSA"
- // anyway. Continue to do so for backwards
- // compatibility.
- useSHA256 = false;
- } else if ("SHA256withRSA".equals(sigAlg) || "SHA256withECDSA".equals(sigAlg)) {
- useSHA256 = true;
- } else {
- System.err.println(args[i] + ": unsupported signature algorithm \"" +
- sigAlg + "\"");
- System.exit(1);
- }
-
- PublicKey key = cert.getPublicKey();
- check(key, useSHA256);
- System.out.print(print(key, useSHA256));
- System.out.println(i < args.length - 1 ? "," : "");
- }
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- System.exit(0);
- }
-}
diff --git a/libmincrypt/tools/DumpPublicKey.mf b/libmincrypt/tools/DumpPublicKey.mf
deleted file mode 100644
index 7bb3bc8..0000000
--- a/libmincrypt/tools/DumpPublicKey.mf
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.dumpkey.DumpPublicKey
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
new file mode 100644
index 0000000..5fb56f2
--- /dev/null
+++ b/libnativebridge/Android.bp
@@ -0,0 +1,24 @@
+
+cc_library {
+ name: "libnativebridge",
+
+ host_supported: true,
+ srcs: ["native_bridge.cc"],
+ shared_libs: ["liblog"],
+ clang: true,
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ cppflags: [
+ "-fvisibility=protected",
+ ],
+
+ host_ldlibs: ["-ldl"],
+ target: {
+ android: {
+ shared_libs: ["libdl"],
+ },
+ },
+}
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk
index d20d44c..3887b1b 100644
--- a/libnativebridge/Android.mk
+++ b/libnativebridge/Android.mk
@@ -1,58 +1,3 @@
LOCAL_PATH:= $(call my-dir)
-NATIVE_BRIDGE_COMMON_SRC_FILES := \
- native_bridge.cc
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_CLANG := true
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_CLANG := true
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-# Static library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_CLANG := true
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
include $(LOCAL_PATH)/tests/Android.mk
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 32a65ea..83f35b1 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -14,17 +14,21 @@
* limitations under the License.
*/
+#define LOG_TAG "nativebridge"
+
#include "nativebridge/native_bridge.h"
-#include <cstring>
-#include <cutils/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <unistd.h>
+#include <cstring>
+
+#include <log/log.h>
namespace android {
@@ -76,6 +80,19 @@
// Current state of the native bridge.
static NativeBridgeState state = NativeBridgeState::kNotSetup;
+// The version of NativeBridge implementation.
+// Different Nativebridge interface needs the service of different version of
+// Nativebridge implementation.
+// Used by isCompatibleWith() which is introduced in v2.
+enum NativeBridgeImplementationVersion {
+ // first version, not used.
+ DEFAULT_VERSION = 1,
+ // The version which signal semantic is introduced.
+ SIGNAL_VERSION = 2,
+ // The version which namespace semantic is introduced.
+ NAMESPACE_VERSION = 3,
+};
+
// Whether we had an error at some point.
static bool had_error = false;
@@ -96,8 +113,6 @@
// and hard code the directory name again here.
static constexpr const char* kCodeCacheDir = "code_cache";
-static constexpr uint32_t kLibNativeBridgeVersion = 2;
-
// Characters allowed in a native bridge filename. The first character must
// be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-].
static bool CharacterAllowed(char c, bool first) {
@@ -148,19 +163,18 @@
}
}
-static bool VersionCheck(const NativeBridgeCallbacks* cb) {
+// The policy of invoking Nativebridge changed in v3 with/without namespace.
+// Suggest Nativebridge implementation not maintain backward-compatible.
+static bool isCompatibleWith(const uint32_t version) {
// Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported
// version.
- if (cb == nullptr || cb->version == 0) {
+ if (callbacks == nullptr || callbacks->version == 0 || version == 0) {
return false;
}
// If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check.
- if (cb->version >= 2) {
- if (!callbacks->isCompatibleWith(kLibNativeBridgeVersion)) {
- // TODO: Scan which version is supported, and fall back to handle it.
- return false;
- }
+ if (callbacks->version >= SIGNAL_VERSION) {
+ return callbacks->isCompatibleWith(version);
}
return true;
@@ -201,7 +215,7 @@
callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
kNativeBridgeInterfaceSymbol));
if (callbacks != nullptr) {
- if (VersionCheck(callbacks)) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
// Store the handle for later.
native_bridge_handle = handle;
} else {
@@ -231,8 +245,10 @@
static const char* kRuntimeISA = "arm";
#elif defined(__aarch64__)
static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
static const char* kRuntimeISA = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+static const char* kRuntimeISA = "mips64";
#elif defined(__i386__)
static const char* kRuntimeISA = "x86";
#elif defined(__x86_64__)
@@ -514,8 +530,91 @@
}
NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
- if (NativeBridgeInitialized() && callbacks->version >= 2) {
- return callbacks->getSignalHandler(signal);
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(SIGNAL_VERSION)) {
+ return callbacks->getSignalHandler(signal);
+ } else {
+ ALOGE("not compatible with version %d, cannot get signal handler", SIGNAL_VERSION);
+ }
+ }
+ return nullptr;
+}
+
+int NativeBridgeUnloadLibrary(void* handle) {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->unloadLibrary(handle);
+ } else {
+ ALOGE("not compatible with version %d, cannot unload library", NAMESPACE_VERSION);
+ }
+ }
+ return -1;
+}
+
+const char* NativeBridgeGetError() {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->getError();
+ } else {
+ return "native bridge implementation is not compatible with version 3, cannot get message";
+ }
+ }
+ return "native bridge is not initialized";
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->isPathSupported(path);
+ } else {
+ ALOGE("not compatible with version %d, cannot check via library path", NAMESPACE_VERSION);
+ }
+ }
+ return false;
+}
+
+bool NativeBridgeInitNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path) {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->initNamespace(public_ns_sonames, anon_ns_library_path);
+ } else {
+ ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
+ }
+ }
+
+ return false;
+}
+
+native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
+ const char* ld_library_path,
+ const char* default_library_path,
+ uint64_t type,
+ const char* permitted_when_isolated_path,
+ native_bridge_namespace_t* parent_ns) {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->createNamespace(name,
+ ld_library_path,
+ default_library_path,
+ type,
+ permitted_when_isolated_path,
+ parent_ns);
+ } else {
+ ALOGE("not compatible with version %d, cannot create namespace %s", NAMESPACE_VERSION, name);
+ }
+ }
+
+ return nullptr;
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->loadLibraryExt(libpath, flag, ns);
+ } else {
+ ALOGE("not compatible with version %d, cannot load library in namespace", NAMESPACE_VERSION);
+ }
}
return nullptr;
}
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 7265939..4c3e862 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -20,7 +20,13 @@
PreInitializeNativeBridgeFail2_test.cpp \
ReSetupNativeBridge_test.cpp \
UnavailableNativeBridge_test.cpp \
- ValidNameNativeBridge_test.cpp
+ ValidNameNativeBridge_test.cpp \
+ NativeBridge3UnloadLibrary_test.cpp \
+ NativeBridge3GetError_test.cpp \
+ NativeBridge3IsPathSupported_test.cpp \
+ NativeBridge3InitNamespace_test.cpp \
+ NativeBridge3CreateNamespace_test.cpp \
+ NativeBridge3LoadLibraryExt_test.cpp
shared_libraries := \
@@ -31,7 +37,6 @@
$(foreach file,$(test_src_files), \
$(eval include $(CLEAR_VARS)) \
$(eval LOCAL_CLANG := true) \
- $(eval LOCAL_CPPFLAGS := -std=gnu++11) \
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
@@ -41,7 +46,6 @@
$(foreach file,$(test_src_files), \
$(eval include $(CLEAR_VARS)) \
$(eval LOCAL_CLANG := true) \
- $(eval LOCAL_CPPFLAGS := -std=gnu++11) \
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
diff --git a/libnativebridge/tests/Android.nativebridge-dummy.mk b/libnativebridge/tests/Android.nativebridge-dummy.mk
index 2efc176..2d78be0 100644
--- a/libnativebridge/tests/Android.nativebridge-dummy.mk
+++ b/libnativebridge/tests/Android.nativebridge-dummy.mk
@@ -12,8 +12,8 @@
LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
LOCAL_CLANG := true
LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
+LOCAL_CPPFLAGS := -fvisibility=protected
+LOCAL_SHARED_LIBRARIES := libdl
LOCAL_MULTILIB := both
include $(BUILD_SHARED_LIBRARY)
@@ -27,7 +27,7 @@
LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
LOCAL_CLANG := true
LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_CPPFLAGS := -fvisibility=protected
LOCAL_LDFLAGS := -ldl
LOCAL_MULTILIB := both
@@ -48,8 +48,8 @@
LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
LOCAL_CLANG := true
LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
+LOCAL_CPPFLAGS := -fvisibility=protected
+LOCAL_SHARED_LIBRARIES := libdl
LOCAL_MULTILIB := both
include $(BUILD_SHARED_LIBRARY)
@@ -63,8 +63,46 @@
LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
LOCAL_CLANG := true
LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+
+# v3.
+
+NATIVE_BRIDGE3_COMMON_SRC_FILES := \
+ DummyNativeBridge3.cpp
+
+# Shared library for target
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge3-dummy
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Shared library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge3-dummy
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
LOCAL_LDFLAGS := -ldl
LOCAL_MULTILIB := both
include $(BUILD_HOST_SHARED_LIBRARY)
+
+
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
new file mode 100644
index 0000000..13fce85
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge3.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// A dummy implementation of the native-bridge interface.
+
+#include "nativebridge/native_bridge.h"
+
+#include <signal.h>
+
+// NativeBridgeCallbacks implementations
+extern "C" bool native_bridge3_initialize(
+ const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+ const char* /* app_code_cache_dir */,
+ const char* /* isa */) {
+ return true;
+}
+
+extern "C" void* native_bridge3_loadLibrary(const char* /* libpath */, int /* flag */) {
+ return nullptr;
+}
+
+extern "C" void* native_bridge3_getTrampoline(void* /* handle */, const char* /* name */,
+ const char* /* shorty */, uint32_t /* len */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge3_isSupported(const char* /* libpath */) {
+ return false;
+}
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge3_getAppEnv(
+ const char* /* abi */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge3_isCompatibleWith(uint32_t version) {
+ // For testing, allow 1-3, but disallow 4+.
+ return version <= 3;
+}
+
+static bool native_bridge3_dummy_signal_handler(int, siginfo_t*, void*) {
+ // TODO: Implement something here. We'd either have to have a death test with a log here, or
+ // we'd have to be able to resume after the faulting instruction...
+ return true;
+}
+
+extern "C" android::NativeBridgeSignalHandlerFn native_bridge3_getSignalHandler(int signal) {
+ if (signal == SIGSEGV) {
+ return &native_bridge3_dummy_signal_handler;
+ }
+ return nullptr;
+}
+
+extern "C" int native_bridge3_unloadLibrary(void* /* handle */) {
+ return 0;
+}
+
+extern "C" const char* native_bridge3_getError() {
+ return nullptr;
+}
+
+extern "C" bool native_bridge3_isPathSupported(const char* /* path */) {
+ return true;
+}
+
+extern "C" bool native_bridge3_initNamespace(const char* /* public_ns_sonames */,
+ const char* /* anon_ns_library_path */) {
+ return true;
+}
+
+extern "C" android::native_bridge_namespace_t*
+native_bridge3_createNamespace(const char* /* name */,
+ const char* /* ld_library_path */,
+ const char* /* default_library_path */,
+ uint64_t /* type */,
+ const char* /* permitted_when_isolated_path */,
+ android::native_bridge_namespace_t* /* parent_ns */) {
+ return nullptr;
+}
+
+extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */,
+ int /* flag */,
+ android::native_bridge_namespace_t* /* ns */) {
+ return nullptr;
+}
+
+
+android::NativeBridgeCallbacks NativeBridgeItf {
+ // v1
+ .version = 3,
+ .initialize = &native_bridge3_initialize,
+ .loadLibrary = &native_bridge3_loadLibrary,
+ .getTrampoline = &native_bridge3_getTrampoline,
+ .isSupported = &native_bridge3_isSupported,
+ .getAppEnv = &native_bridge3_getAppEnv,
+ // v2
+ .isCompatibleWith = &native_bridge3_isCompatibleWith,
+ .getSignalHandler = &native_bridge3_getSignalHandler,
+ // v3
+ .unloadLibrary = &native_bridge3_unloadLibrary,
+ .getError = &native_bridge3_getError,
+ .isPathSupported = &native_bridge3_isPathSupported,
+ .initNamespace = &native_bridge3_initNamespace,
+ .createNamespace = &native_bridge3_createNamespace,
+ .loadLibraryExt = &native_bridge3_loadLibraryExt
+};
+
diff --git a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp
new file mode 100644
index 0000000..668d942
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_CreateNamespace) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(3U, NativeBridgeGetVersion());
+ ASSERT_EQ(nullptr, NativeBridgeCreateNamespace(nullptr, nullptr, nullptr,
+ 0, nullptr, nullptr));
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3GetError_test.cpp b/libnativebridge/tests/NativeBridge3GetError_test.cpp
new file mode 100644
index 0000000..0b9f582
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3GetError_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_GetError) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(3U, NativeBridgeGetVersion());
+ ASSERT_EQ(nullptr, NativeBridgeGetError());
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp
new file mode 100644
index 0000000..ae0fd2b
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_InitNamespace) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(3U, NativeBridgeGetVersion());
+ ASSERT_EQ(true, NativeBridgeInitNamespace(nullptr, nullptr));
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp
new file mode 100644
index 0000000..325e40b
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_IsPathSupported) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(3U, NativeBridgeGetVersion());
+ ASSERT_EQ(true, NativeBridgeIsPathSupported(nullptr));
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp
new file mode 100644
index 0000000..4caeb44
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_LoadLibraryExt) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(3U, NativeBridgeGetVersion());
+ ASSERT_EQ(nullptr, NativeBridgeLoadLibraryExt(nullptr, 0, nullptr));
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp
new file mode 100644
index 0000000..93a979c
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_UnloadLibrary) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(3U, NativeBridgeGetVersion());
+ ASSERT_EQ(0, NativeBridgeUnloadLibrary(nullptr));
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
index d489420..0f99816 100644
--- a/libnativebridge/tests/NativeBridgeTest.h
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -25,6 +25,8 @@
constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so";
constexpr const char* kCodeCache = "./code_cache";
constexpr const char* kCodeCacheStatFail = "./code_cache/temp";
+constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so";
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
namespace android {
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
index e1c0876..2067ed2 100644
--- a/libnativebridge/tests/NeedsNativeBridge_test.cpp
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -18,15 +18,17 @@
namespace android {
-static const char* kISAs[] = { "arm", "arm64", "mips", "x86", "x86_64", "random", "64arm", "64_x86",
- "64_x86_64", "", "reallylongstringabcd", nullptr };
+static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
+ "64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
#if defined(__arm__)
static const char* kRuntimeISA = "arm";
#elif defined(__aarch64__)
static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
static const char* kRuntimeISA = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+static const char* kRuntimeISA = "mips64";
#elif defined(__i386__)
static const char* kRuntimeISA = "x86";
#elif defined(__x86_64__)
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
index 69c30a1..5a2b0a1 100644
--- a/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
+++ b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
@@ -16,9 +16,6 @@
#include "NativeBridgeTest.h"
-#include <cstdio>
-#include <cstring>
-#include <cutils/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
@@ -26,6 +23,11 @@
#include <sys/mount.h>
#include <sys/stat.h>
+#include <cstdio>
+#include <cstring>
+
+#include <android/log.h>
+
namespace android {
TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail1) {
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
index 74e96e0..af976b1 100644
--- a/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
+++ b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-#include "NativeBridgeTest.h"
-
-#include <cstdio>
-#include <cstring>
-#include <cutils/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
@@ -26,6 +21,13 @@
#include <sys/mount.h>
#include <sys/stat.h>
+#include <cstdio>
+#include <cstring>
+
+#include <android/log.h>
+
+#include "NativeBridgeTest.h"
+
namespace android {
TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail2) {
diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
index d3bbebe..f3e5f38 100644
--- a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
+++ b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-#include "NativeBridgeTest.h"
-
-#include <cstdio>
-#include <cstring>
-#include <cutils/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
@@ -26,6 +21,13 @@
#include <sys/mount.h>
#include <sys/stat.h>
+#include <cstdio>
+#include <cstring>
+
+#include <android/log.h>
+
+#include "NativeBridgeTest.h"
+
namespace android {
static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
new file mode 100644
index 0000000..c1133fb
--- /dev/null
+++ b/libnativeloader/Android.bp
@@ -0,0 +1,32 @@
+// Shared library for target
+// ========================================================
+cc_library {
+ name: "libnativeloader",
+ host_supported: true,
+ srcs: ["native_loader.cpp"],
+ shared_libs: [
+ "libnativehelper",
+ "liblog",
+ "libcutils",
+ "libnativebridge",
+ "libbase",
+ ],
+ target: {
+ android: {
+ shared_libs: ["libdl"],
+ },
+ host: {
+ host_ldlibs: ["-ldl"],
+ },
+ },
+ clang: true,
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ cppflags: [
+ "-fvisibility=hidden",
+ ],
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+}
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
new file mode 100644
index 0000000..02e7075
--- /dev/null
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ANDROID_DLEXT_NAMESPACES_H__
+#define __ANDROID_DLEXT_NAMESPACES_H__
+
+#include <android/dlext.h>
+
+__BEGIN_DECLS
+
+/*
+ * Initializes public and anonymous namespaces. The public_ns_sonames is the list of sonames
+ * to be included into public namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
+ * The libraries in this list should be loaded prior to this call.
+ *
+ * The anon_ns_library_path is the search path for anonymous namespace. The anonymous namespace
+ * is used in the case when linker cannot identify the caller of dlopen/dlsym. This happens
+ * for the code not loaded by dynamic linker; for example calls from the mono-compiled code.
+ */
+extern bool android_init_namespaces(const char* public_ns_sonames,
+ const char* anon_ns_library_path);
+
+
+enum {
+ /* A regular namespace is the namespace with a custom search path that does
+ * not impose any restrictions on the location of native libraries.
+ */
+ ANDROID_NAMESPACE_TYPE_REGULAR = 0,
+
+ /* An isolated namespace requires all the libraries to be on the search path
+ * or under permitted_when_isolated_path. The search path is the union of
+ * ld_library_path and default_library_path.
+ */
+ ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+
+ /* The shared namespace clones the list of libraries of the caller namespace upon creation
+ * which means that they are shared between namespaces - the caller namespace and the new one
+ * will use the same copy of a library if it was loaded prior to android_create_namespace call.
+ *
+ * Note that libraries loaded after the namespace is created will not be shared.
+ *
+ * Shared namespaces can be isolated or regular. Note that they do not inherit the search path nor
+ * permitted_path from the caller's namespace.
+ */
+ ANDROID_NAMESPACE_TYPE_SHARED = 2,
+ ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED |
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+};
+
+/*
+ * Creates new linker namespace.
+ * ld_library_path and default_library_path represent the search path
+ * for the libraries in the namespace.
+ *
+ * The libraries in the namespace are searched by folowing order:
+ * 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH)
+ * 2. In directories specified by DT_RUNPATH of the "needed by" binary.
+ * 3. deault_library_path (This of this as namespace-local default library path)
+ *
+ * When type is ANDROID_NAMESPACE_TYPE_ISOLATED the resulting namespace requires all of
+ * the libraries to be on the search path or under the permitted_when_isolated_path;
+ * the search_path is ld_library_path:default_library_path. Note that the
+ * permitted_when_isolated_path path is not part of the search_path and
+ * does not affect the search order. It is a way to allow loading libraries from specific
+ * locations when using absolute path.
+ * If a library or any of its dependencies are outside of the permitted_when_isolated_path
+ * and search_path, and it is not part of the public namespace dlopen will fail.
+ */
+extern struct android_namespace_t* android_create_namespace(const char* name,
+ const char* ld_library_path,
+ const char* default_library_path,
+ uint64_t type,
+ const char* permitted_when_isolated_path,
+ android_namespace_t* parent);
+
+/*
+ * Get the default library search path.
+ * The path will be copied into buffer, which must have space for at least
+ * buffer_size chars. Elements are separated with ':', and the path will always
+ * be null-terminated.
+ *
+ * If buffer_size is too small to hold the entire default search path and the
+ * null terminator, this function will abort. There is currently no way to find
+ * out what the required buffer size is. At the time of this writing, PATH_MAX
+ * is sufficient and used by all callers of this function.
+ */
+extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
+
+__END_DECLS
+
+#endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
new file mode 100644
index 0000000..99ae3a7
--- /dev/null
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -0,0 +1,64 @@
+/*
+ * 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 NATIVE_LOADER_H_
+#define NATIVE_LOADER_H_
+
+#include "jni.h"
+#include <stdint.h>
+#include <string>
+#if defined(__ANDROID__)
+#include <android/dlext.h>
+#endif
+
+namespace android {
+
+__attribute__((visibility("default")))
+void InitializeNativeLoader();
+
+__attribute__((visibility("default")))
+jstring CreateClassLoaderNamespace(JNIEnv* env,
+ int32_t target_sdk_version,
+ jobject class_loader,
+ bool is_shared,
+ jstring library_path,
+ jstring permitted_path);
+
+__attribute__((visibility("default")))
+void* OpenNativeLibrary(JNIEnv* env,
+ int32_t target_sdk_version,
+ const char* path,
+ jobject class_loader,
+ jstring library_path,
+ bool* needs_native_bridge,
+ std::string* error_msg);
+
+__attribute__((visibility("default")))
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge);
+
+#if defined(__ANDROID__)
+// Look up linker namespace by class_loader. Returns nullptr if
+// there is no namespace associated with the class_loader.
+__attribute__((visibility("default")))
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+#endif
+
+__attribute__((visibility("default")))
+void ResetNativeLoader();
+
+}; // namespace android
+
+#endif // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
new file mode 100644
index 0000000..d7b5cb5
--- /dev/null
+++ b/libnativeloader/native_loader.cpp
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nativeloader/native_loader.h"
+#include "ScopedUtfChars.h"
+
+#include <dlfcn.h>
+#ifdef __ANDROID__
+#define LOG_TAG "libnativeloader"
+#include "nativeloader/dlext_namespaces.h"
+#include "cutils/properties.h"
+#include "log/log.h"
+#endif
+#include "nativebridge/native_bridge.h"
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <mutex>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+
+#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
+ "%s:%d: %s CHECK '" #predicate "' failed.",\
+ __FILE__, __LINE__, __FUNCTION__)
+
+namespace android {
+
+#if defined(__ANDROID__)
+class NativeLoaderNamespace {
+ public:
+ NativeLoaderNamespace()
+ : android_ns_(nullptr), native_bridge_ns_(nullptr) { }
+
+ explicit NativeLoaderNamespace(android_namespace_t* ns)
+ : android_ns_(ns), native_bridge_ns_(nullptr) { }
+
+ explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
+ : android_ns_(nullptr), native_bridge_ns_(ns) { }
+
+ NativeLoaderNamespace(NativeLoaderNamespace&& that) = default;
+ NativeLoaderNamespace(const NativeLoaderNamespace& that) = default;
+
+ NativeLoaderNamespace& operator=(const NativeLoaderNamespace& that) = default;
+
+ android_namespace_t* get_android_ns() const {
+ CHECK(native_bridge_ns_ == nullptr);
+ return android_ns_;
+ }
+
+ native_bridge_namespace_t* get_native_bridge_ns() const {
+ CHECK(android_ns_ == nullptr);
+ return native_bridge_ns_;
+ }
+
+ bool is_android_namespace() const {
+ return native_bridge_ns_ == nullptr;
+ }
+
+ private:
+ // Only one of them can be not null
+ android_namespace_t* android_ns_;
+ native_bridge_namespace_t* native_bridge_ns_;
+};
+
+static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot =
+ "/etc/public.libraries.txt";
+static constexpr const char* kPublicNativeLibrariesVendorConfig =
+ "/vendor/etc/public.libraries.txt";
+
+// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
+// System.load() with an absolute path which is outside of the classloader library search path.
+// This list includes all directories app is allowed to access this way.
+static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
+
+static bool is_debuggable() {
+ char debuggable[PROP_VALUE_MAX];
+ property_get("ro.debuggable", debuggable, "0");
+ return std::string(debuggable) == "1";
+}
+
+class LibraryNamespaces {
+ public:
+ LibraryNamespaces() : initialized_(false) { }
+
+ bool Create(JNIEnv* env,
+ jobject class_loader,
+ bool is_shared,
+ jstring java_library_path,
+ jstring java_permitted_path,
+ NativeLoaderNamespace* ns,
+ std::string* error_msg) {
+ std::string library_path; // empty string by default.
+
+ if (java_library_path != nullptr) {
+ ScopedUtfChars library_path_utf_chars(env, java_library_path);
+ library_path = library_path_utf_chars.c_str();
+ }
+
+ // (http://b/27588281) This is a workaround for apps using custom
+ // classloaders and calling System.load() with an absolute path which
+ // is outside of the classloader library search path.
+ //
+ // This part effectively allows such a classloader to access anything
+ // under /data and /mnt/expand
+ std::string permitted_path = kWhitelistedDirectories;
+
+ if (java_permitted_path != nullptr) {
+ ScopedUtfChars path(env, java_permitted_path);
+ if (path.c_str() != nullptr && path.size() > 0) {
+ permitted_path = permitted_path + ":" + path.c_str();
+ }
+ }
+
+ if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
+ return false;
+ }
+
+ bool found = FindNamespaceByClassLoader(env, class_loader, nullptr);
+
+ LOG_ALWAYS_FATAL_IF(found,
+ "There is already a namespace associated with this classloader");
+
+ uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ if (is_shared) {
+ namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
+ }
+
+ NativeLoaderNamespace parent_ns;
+ bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
+
+ bool is_native_bridge = false;
+
+ if (found_parent_namespace) {
+ is_native_bridge = !parent_ns.is_android_namespace();
+ } else if (!library_path.empty()) {
+ is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
+ }
+
+ NativeLoaderNamespace native_loader_ns;
+ if (!is_native_bridge) {
+ android_namespace_t* ns = android_create_namespace("classloader-namespace",
+ nullptr,
+ library_path.c_str(),
+ namespace_type,
+ permitted_path.c_str(),
+ parent_ns.get_android_ns());
+ if (ns == nullptr) {
+ *error_msg = dlerror();
+ return false;
+ }
+
+ native_loader_ns = NativeLoaderNamespace(ns);
+ } else {
+ native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
+ nullptr,
+ library_path.c_str(),
+ namespace_type,
+ permitted_path.c_str(),
+ parent_ns.get_native_bridge_ns());
+ if (ns == nullptr) {
+ *error_msg = NativeBridgeGetError();
+ return false;
+ }
+
+ native_loader_ns = NativeLoaderNamespace(ns);
+ }
+
+ namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
+
+ *ns = native_loader_ns;
+ return true;
+ }
+
+ bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) {
+ auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+ [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
+ return env->IsSameObject(value.first, class_loader);
+ });
+ if (it != namespaces_.end()) {
+ if (ns != nullptr) {
+ *ns = it->second;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void Initialize() {
+ // Once public namespace is initialized there is no
+ // point in running this code - it will have no effect
+ // on the current list of public libraries.
+ if (initialized_) {
+ return;
+ }
+
+ std::vector<std::string> sonames;
+ const char* android_root_env = getenv("ANDROID_ROOT");
+ std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";
+ std::string public_native_libraries_system_config =
+ root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
+
+ std::string error_msg;
+ LOG_ALWAYS_FATAL_IF(!ReadConfig(public_native_libraries_system_config, &sonames, &error_msg),
+ "Error reading public native library list from \"%s\": %s",
+ public_native_libraries_system_config.c_str(), error_msg.c_str());
+
+ // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
+ // variable to add libraries to the list. This is intended for platform tests only.
+ if (is_debuggable()) {
+ const char* additional_libs = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
+ if (additional_libs != nullptr && additional_libs[0] != '\0') {
+ std::vector<std::string> additional_libs_vector = base::Split(additional_libs, ":");
+ std::copy(additional_libs_vector.begin(),
+ additional_libs_vector.end(),
+ std::back_inserter(sonames));
+ }
+ }
+
+ // This file is optional, quietly ignore if the file does not exist.
+ ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
+
+ // android_init_namespaces() expects all the public libraries
+ // to be loaded so that they can be found by soname alone.
+ //
+ // TODO(dimitry): this is a bit misleading since we do not know
+ // if the vendor public library is going to be opened from /vendor/lib
+ // we might as well end up loading them from /system/lib
+ // 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) {
+ dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);
+ }
+
+ public_libraries_ = base::Join(sonames, ':');
+ }
+
+ void Reset() {
+ namespaces_.clear();
+ }
+
+ private:
+ bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
+ std::string* error_msg = nullptr) {
+ // Read list of public native libraries from the config file.
+ std::string file_content;
+ if(!base::ReadFileToString(configFile, &file_content)) {
+ if (error_msg) *error_msg = strerror(errno);
+ return false;
+ }
+
+ std::vector<std::string> lines = base::Split(file_content, "\n");
+
+ for (auto& line : lines) {
+ auto trimmed_line = base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+ continue;
+ }
+ size_t space_pos = trimmed_line.rfind(' ');
+ if (space_pos != std::string::npos) {
+ std::string type = trimmed_line.substr(space_pos + 1);
+ if (type != "32" && type != "64") {
+ if (error_msg) *error_msg = "Malformed line: " + line;
+ return false;
+ }
+#if defined(__LP64__)
+ // Skip 32 bit public library.
+ if (type == "32") {
+ continue;
+ }
+#else
+ // Skip 64 bit public library.
+ if (type == "64") {
+ continue;
+ }
+#endif
+ trimmed_line.resize(space_pos);
+ }
+
+ sonames->push_back(trimmed_line);
+ }
+
+ return true;
+ }
+
+ bool InitPublicNamespace(const char* library_path, std::string* error_msg) {
+ // Ask native bride if this apps library path should be handled by it
+ bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
+
+ // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
+ // code is one example) unknown to linker in which case linker uses anonymous
+ // namespace. The second argument specifies the search path for the anonymous
+ // namespace which is the library_path of the classloader.
+ initialized_ = android_init_namespaces(public_libraries_.c_str(),
+ is_native_bridge ? nullptr : library_path);
+ if (!initialized_) {
+ *error_msg = dlerror();
+ return false;
+ }
+
+ // and now initialize native bridge namespaces if necessary.
+ if (NativeBridgeInitialized()) {
+ initialized_ = NativeBridgeInitNamespace(public_libraries_.c_str(),
+ is_native_bridge ? library_path : nullptr);
+ if (!initialized_) {
+ *error_msg = NativeBridgeGetError();
+ }
+ }
+
+ return initialized_;
+ }
+
+ jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
+ jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
+ jmethodID get_parent = env->GetMethodID(class_loader_class,
+ "getParent",
+ "()Ljava/lang/ClassLoader;");
+
+ return env->CallObjectMethod(class_loader, get_parent);
+ }
+
+ bool FindParentNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader,
+ NativeLoaderNamespace* ns) {
+ jobject parent_class_loader = GetParentClassLoader(env, class_loader);
+
+ while (parent_class_loader != nullptr) {
+ if (FindNamespaceByClassLoader(env, parent_class_loader, ns)) {
+ return true;
+ }
+
+ parent_class_loader = GetParentClassLoader(env, parent_class_loader);
+ }
+
+ return false;
+ }
+
+ bool initialized_;
+ std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+ std::string public_libraries_;
+
+
+ DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
+};
+
+static std::mutex g_namespaces_mutex;
+static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
+#endif
+
+void InitializeNativeLoader() {
+#if defined(__ANDROID__)
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ g_namespaces->Initialize();
+#endif
+}
+
+void ResetNativeLoader() {
+#if defined(__ANDROID__)
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ g_namespaces->Reset();
+#endif
+}
+
+jstring CreateClassLoaderNamespace(JNIEnv* env,
+ int32_t target_sdk_version,
+ jobject class_loader,
+ bool is_shared,
+ jstring library_path,
+ jstring permitted_path) {
+#if defined(__ANDROID__)
+ UNUSED(target_sdk_version);
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+
+ std::string error_msg;
+ NativeLoaderNamespace ns;
+ bool success = g_namespaces->Create(env,
+ class_loader,
+ is_shared,
+ library_path,
+ permitted_path,
+ &ns,
+ &error_msg);
+ if (!success) {
+ return env->NewStringUTF(error_msg.c_str());
+ }
+#else
+ UNUSED(env, target_sdk_version, class_loader, is_shared,
+ library_path, permitted_path);
+#endif
+ return nullptr;
+}
+
+void* OpenNativeLibrary(JNIEnv* env,
+ int32_t target_sdk_version,
+ const char* path,
+ jobject class_loader,
+ jstring library_path,
+ bool* needs_native_bridge,
+ std::string* error_msg) {
+#if defined(__ANDROID__)
+ UNUSED(target_sdk_version);
+ if (class_loader == nullptr) {
+ *needs_native_bridge = false;
+ return dlopen(path, RTLD_NOW);
+ }
+
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ NativeLoaderNamespace ns;
+
+ if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
+ // This is the case where the classloader was not created by ApplicationLoaders
+ // In this case we create an isolated not-shared namespace for it.
+ if (!g_namespaces->Create(env, class_loader, false, library_path, nullptr, &ns, error_msg)) {
+ return nullptr;
+ }
+ }
+
+ if (ns.is_android_namespace()) {
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+ extinfo.library_namespace = ns.get_android_ns();
+
+ void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
+ if (handle == nullptr) {
+ *error_msg = dlerror();
+ }
+ *needs_native_bridge = false;
+ return handle;
+ } else {
+ void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
+ if (handle == nullptr) {
+ *error_msg = NativeBridgeGetError();
+ }
+ *needs_native_bridge = true;
+ return handle;
+ }
+#else
+ UNUSED(env, target_sdk_version, class_loader, library_path);
+ *needs_native_bridge = false;
+ void* handle = dlopen(path, RTLD_NOW);
+ if (handle == nullptr) {
+ if (NativeBridgeIsSupported(path)) {
+ *needs_native_bridge = true;
+ handle = NativeBridgeLoadLibrary(path, RTLD_NOW);
+ if (handle == nullptr) {
+ *error_msg = NativeBridgeGetError();
+ }
+ } else {
+ *needs_native_bridge = false;
+ *error_msg = dlerror();
+ }
+ }
+ return handle;
+#endif
+}
+
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge) {
+ return needs_native_bridge ? NativeBridgeUnloadLibrary(handle) :
+ dlclose(handle);
+}
+
+#if defined(__ANDROID__)
+// native_bridge_namespaces are not supported for callers of this function.
+// This function will return nullptr in the case when application is running
+// on native bridge.
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ NativeLoaderNamespace ns;
+ if (g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
+ return ns.is_android_namespace() ? ns.get_android_ns() : nullptr;
+ }
+
+ return nullptr;
+}
+#endif
+
+}; // android namespace
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
index 2060df4..2150279 100644
--- a/libnetutils/Android.mk
+++ b/libnetutils/Android.mk
@@ -4,7 +4,6 @@
LOCAL_SRC_FILES := \
dhcpclient.c \
dhcpmsg.c \
- dhcp_utils.c \
ifc_utils.c \
packet.c
@@ -16,11 +15,7 @@
LOCAL_CFLAGS := -Werror
-include $(BUILD_SHARED_LIBRARY)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := dhcptool.c
-LOCAL_SHARED_LIBRARIES := libnetutils
-LOCAL_MODULE := dhcptool
-LOCAL_MODULE_TAGS := debug
-include $(BUILD_EXECUTABLE)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
deleted file mode 100644
index 70e37c6..0000000
--- a/libnetutils/dhcp_utils.c
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * Copyright 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* Utilities for managing the dhcpcd DHCP client daemon */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#include <cutils/properties.h>
-
-static const char DAEMON_NAME[] = "dhcpcd";
-static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd";
-static const char HOSTNAME_PROP_NAME[] = "net.hostname";
-static const char DHCP_PROP_NAME_PREFIX[] = "dhcp";
-static const char DHCP_CONFIG_PATH[] = "/system/etc/dhcpcd/dhcpcd.conf";
-static const int NAP_TIME = 200; /* wait for 200ms at a time */
- /* when polling for property values */
-static const char DAEMON_NAME_RENEW[] = "iprenew";
-static char errmsg[100];
-/* interface length for dhcpcd daemon start (dhcpcd_<interface> as defined in init.rc file)
- * or for filling up system properties dhcpcd.<interface>.ipaddress, dhcpcd.<interface>.dns1
- * and other properties on a successful bind
- */
-#define MAX_INTERFACE_LENGTH 25
-
-/*
- * P2p interface names increase sequentially p2p-p2p0-1, p2p-p2p0-2.. after
- * group formation. This does not work well with system properties which can quickly
- * exhaust or for specifiying a dhcp start target in init which requires
- * interface to be pre-defined in init.rc file.
- *
- * This function returns a common string p2p for all p2p interfaces.
- */
-void get_p2p_interface_replacement(const char *interface, char *p2p_interface) {
- /* Use p2p for any interface starting with p2p. */
- if (strncmp(interface, "p2p",3) == 0) {
- strncpy(p2p_interface, "p2p", MAX_INTERFACE_LENGTH);
- } else {
- strncpy(p2p_interface, interface, MAX_INTERFACE_LENGTH);
- }
-}
-
-/*
- * Wait for a system property to be assigned a specified value.
- * If desired_value is NULL, then just wait for the property to
- * be created with any value. maxwait is the maximum amount of
- * time in seconds to wait before giving up.
- */
-static int wait_for_property(const char *name, const char *desired_value, int maxwait)
-{
- char value[PROPERTY_VALUE_MAX] = {'\0'};
- int maxnaps = (maxwait * 1000) / NAP_TIME;
-
- if (maxnaps < 1) {
- maxnaps = 1;
- }
-
- while (maxnaps-- >= 0) {
- if (property_get(name, value, NULL)) {
- if (desired_value == NULL ||
- strcmp(value, desired_value) == 0) {
- return 0;
- }
- }
- if (maxnaps >= 0) {
- usleep(NAP_TIME * 1000);
- }
- }
- return -1; /* failure */
-}
-
-static int fill_ip_info(const char *interface,
- char *ipaddr,
- char *gateway,
- uint32_t *prefixLength,
- char *dns[],
- char *server,
- uint32_t *lease,
- char *vendorInfo,
- char *domain,
- char *mtu)
-{
- char prop_name[PROPERTY_KEY_MAX];
- char prop_value[PROPERTY_VALUE_MAX];
- /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
- char p2p_interface[MAX_INTERFACE_LENGTH];
- int x;
-
- get_p2p_interface_replacement(interface, p2p_interface);
-
- snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, p2p_interface);
- property_get(prop_name, ipaddr, NULL);
-
- snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, p2p_interface);
- property_get(prop_name, gateway, NULL);
-
- snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, p2p_interface);
- property_get(prop_name, server, NULL);
-
- //TODO: Handle IPv6 when we change system property usage
- if (gateway[0] == '\0' || strncmp(gateway, "0.0.0.0", 7) == 0) {
- //DHCP server is our best bet as gateway
- strncpy(gateway, server, PROPERTY_VALUE_MAX);
- }
-
- snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, p2p_interface);
- if (property_get(prop_name, prop_value, NULL)) {
- int p;
- // this conversion is v4 only, but this dhcp client is v4 only anyway
- in_addr_t mask = ntohl(inet_addr(prop_value));
- // Check netmask is a valid IP address. ntohl gives NONE response (all 1's) for
- // non 255.255.255.255 inputs. if we get that value check if it is legit..
- if (mask == INADDR_NONE && strcmp(prop_value, "255.255.255.255") != 0) {
- snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
- return -1;
- }
- for (p = 0; p < 32; p++) {
- if (mask == 0) break;
- // check for non-contiguous netmask, e.g., 255.254.255.0
- if ((mask & 0x80000000) == 0) {
- snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
- return -1;
- }
- mask = mask << 1;
- }
- *prefixLength = p;
- }
-
- for (x=0; dns[x] != NULL; x++) {
- snprintf(prop_name, sizeof(prop_name), "%s.%s.dns%d", DHCP_PROP_NAME_PREFIX, p2p_interface, x+1);
- property_get(prop_name, dns[x], NULL);
- }
-
- snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, p2p_interface);
- if (property_get(prop_name, prop_value, NULL)) {
- *lease = atol(prop_value);
- }
-
- snprintf(prop_name, sizeof(prop_name), "%s.%s.vendorInfo", DHCP_PROP_NAME_PREFIX,
- p2p_interface);
- property_get(prop_name, vendorInfo, NULL);
-
- snprintf(prop_name, sizeof(prop_name), "%s.%s.domain", DHCP_PROP_NAME_PREFIX,
- p2p_interface);
- property_get(prop_name, domain, NULL);
-
- snprintf(prop_name, sizeof(prop_name), "%s.%s.mtu", DHCP_PROP_NAME_PREFIX,
- p2p_interface);
- property_get(prop_name, mtu, NULL);
-
- return 0;
-}
-
-/*
- * Start the dhcp client daemon, and wait for it to finish
- * configuring the interface.
- *
- * The device init.rc file needs a corresponding entry for this work.
- *
- * Example:
- * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL -f dhcpcd.conf
- */
-int dhcp_do_request(const char *interface,
- char *ipaddr,
- char *gateway,
- uint32_t *prefixLength,
- char *dns[],
- char *server,
- uint32_t *lease,
- char *vendorInfo,
- char *domain,
- char *mtu)
-{
- char result_prop_name[PROPERTY_KEY_MAX];
- char daemon_prop_name[PROPERTY_KEY_MAX];
- char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
- char daemon_cmd[PROPERTY_VALUE_MAX * 2 + sizeof(DHCP_CONFIG_PATH)];
- const char *ctrl_prop = "ctl.start";
- const char *desired_status = "running";
- /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
- char p2p_interface[MAX_INTERFACE_LENGTH];
-
- get_p2p_interface_replacement(interface, p2p_interface);
-
- snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
- DHCP_PROP_NAME_PREFIX,
- p2p_interface);
-
- snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
- DAEMON_PROP_NAME,
- p2p_interface);
-
- /* Erase any previous setting of the dhcp result property */
- property_set(result_prop_name, "");
-
- /* Start the daemon and wait until it's ready */
- if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0'))
- snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s -h %s %s", DAEMON_NAME,
- p2p_interface, DHCP_CONFIG_PATH, prop_value, interface);
- else
- snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s %s", DAEMON_NAME,
- p2p_interface, DHCP_CONFIG_PATH, interface);
- memset(prop_value, '\0', PROPERTY_VALUE_MAX);
- property_set(ctrl_prop, daemon_cmd);
- if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {
- snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
- return -1;
- }
-
- /* Wait for the daemon to return a result */
- if (wait_for_property(result_prop_name, NULL, 30) < 0) {
- snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish");
- return -1;
- }
-
- if (!property_get(result_prop_name, prop_value, NULL)) {
- /* shouldn't ever happen, given the success of wait_for_property() */
- snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set");
- return -1;
- }
- if (strcmp(prop_value, "ok") == 0) {
- if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
- server, lease, vendorInfo, domain, mtu) == -1) {
- return -1;
- }
- return 0;
- } else {
- snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
- return -1;
- }
-}
-
-/**
- * Stop the DHCP client daemon.
- */
-int dhcp_stop(const char *interface)
-{
- char result_prop_name[PROPERTY_KEY_MAX];
- char daemon_prop_name[PROPERTY_KEY_MAX];
- char daemon_cmd[PROPERTY_VALUE_MAX * 2];
- const char *ctrl_prop = "ctl.stop";
- const char *desired_status = "stopped";
-
- char p2p_interface[MAX_INTERFACE_LENGTH];
-
- get_p2p_interface_replacement(interface, p2p_interface);
-
- snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
- DHCP_PROP_NAME_PREFIX,
- p2p_interface);
-
- snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
- DAEMON_PROP_NAME,
- p2p_interface);
-
- snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
-
- /* Stop the daemon and wait until it's reported to be stopped */
- property_set(ctrl_prop, daemon_cmd);
- if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
- return -1;
- }
- property_set(result_prop_name, "failed");
- return 0;
-}
-
-/**
- * Release the current DHCP client lease.
- */
-int dhcp_release_lease(const char *interface)
-{
- char daemon_prop_name[PROPERTY_KEY_MAX];
- char daemon_cmd[PROPERTY_VALUE_MAX * 2];
- const char *ctrl_prop = "ctl.stop";
- const char *desired_status = "stopped";
-
- char p2p_interface[MAX_INTERFACE_LENGTH];
-
- get_p2p_interface_replacement(interface, p2p_interface);
-
- snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
- DAEMON_PROP_NAME,
- p2p_interface);
-
- snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
-
- /* Stop the daemon and wait until it's reported to be stopped */
- property_set(ctrl_prop, daemon_cmd);
- if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
- return -1;
- }
- return 0;
-}
-
-char *dhcp_get_errmsg() {
- return errmsg;
-}
-
-/**
- * The device init.rc file needs a corresponding entry.
- *
- * Example:
- * service iprenew_<interface> /system/bin/dhcpcd -n
- *
- */
-int dhcp_do_request_renew(const char *interface,
- char *ipaddr,
- char *gateway,
- uint32_t *prefixLength,
- char *dns[],
- char *server,
- uint32_t *lease,
- char *vendorInfo,
- char *domain,
- char *mtu)
-{
- char result_prop_name[PROPERTY_KEY_MAX];
- char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
- char daemon_cmd[PROPERTY_VALUE_MAX * 2];
- const char *ctrl_prop = "ctl.start";
-
- char p2p_interface[MAX_INTERFACE_LENGTH];
-
- get_p2p_interface_replacement(interface, p2p_interface);
-
- snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
- DHCP_PROP_NAME_PREFIX,
- p2p_interface);
-
- /* Erase any previous setting of the dhcp result property */
- property_set(result_prop_name, "");
-
- /* Start the renew daemon and wait until it's ready */
- snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME_RENEW,
- p2p_interface, interface);
- memset(prop_value, '\0', PROPERTY_VALUE_MAX);
- property_set(ctrl_prop, daemon_cmd);
-
- /* Wait for the daemon to return a result */
- if (wait_for_property(result_prop_name, NULL, 30) < 0) {
- snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP Renew to finish");
- return -1;
- }
-
- if (!property_get(result_prop_name, prop_value, NULL)) {
- /* shouldn't ever happen, given the success of wait_for_property() */
- snprintf(errmsg, sizeof(errmsg), "%s", "DHCP Renew result property was not set");
- return -1;
- }
- if (strcmp(prop_value, "ok") == 0) {
- return fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
- server, lease, vendorInfo, domain, mtu);
- } else {
- snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value);
- return -1;
- }
-}
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
index 240a789..11c116a 100644
--- a/libnetutils/dhcpclient.c
+++ b/libnetutils/dhcpclient.c
@@ -14,27 +14,25 @@
* limitations under the License.
*/
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-
-#include <time.h>
-#include <sys/time.h>
-#include <poll.h>
-
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-
-#include <cutils/properties.h>
#define LOG_TAG "DHCP"
-#include <cutils/log.h>
#include <dirent.h>
+#include <errno.h>
+#include <poll.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+#include <log/log.h>
#include <netutils/ifc.h>
#include "dhcpmsg.h"
diff --git a/libnetutils/dhcptool.c b/libnetutils/dhcptool.c
deleted file mode 100644
index 352ac5e..0000000
--- a/libnetutils/dhcptool.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <error.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-#include <netutils/dhcp.h>
-#include <netutils/ifc.h>
-
-int main(int argc, char* argv[]) {
- if (argc != 2) {
- error(EXIT_FAILURE, 0, "usage: %s INTERFACE", argv[0]);
- }
-
- char* interface = argv[1];
- if (ifc_init()) {
- error(EXIT_FAILURE, errno, "dhcptool %s: ifc_init failed", interface);
- }
-
- int rc = do_dhcp(interface);
- if (rc) {
- error(0, errno, "dhcptool %s: do_dhcp failed", interface);
- }
-
- ifc_close();
-
- return rc ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 956ed30..a098d59 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -1,33 +1,21 @@
/*
* Copyright 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.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
#include <arpa/inet.h>
-#include <net/if.h>
-#include <netdb.h>
-
+#include <errno.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
@@ -36,20 +24,29 @@
#include <linux/ipv6_route.h>
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
-
-#include "netutils/ifc.h"
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
#ifdef ANDROID
#define LOG_TAG "NetUtils"
-#include <cutils/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#else
-#include <stdio.h>
-#include <string.h>
#define ALOGD printf
#define ALOGW printf
#endif
+#include "netutils/ifc.h"
+
#if defined(__ANDROID__)
/* SIOCKILLADDR is an Android extension. */
#define SIOCKILLADDR 0x8939
@@ -57,6 +54,8 @@
static int ifc_ctl_sock = -1;
static int ifc_ctl_sock6 = -1;
+static pthread_mutex_t ifc_sock_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t ifc_sock6_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
void printerr(char *fmt, ...);
#define DBG 0
@@ -122,6 +121,8 @@
int ifc_init(void)
{
int ret;
+
+ pthread_mutex_lock(&ifc_sock_mutex);
if (ifc_ctl_sock == -1) {
ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (ifc_ctl_sock < 0) {
@@ -136,6 +137,7 @@
int ifc_init6(void)
{
+ pthread_mutex_lock(&ifc_sock6_mutex);
if (ifc_ctl_sock6 == -1) {
ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (ifc_ctl_sock6 < 0) {
@@ -152,6 +154,7 @@
(void)close(ifc_ctl_sock);
ifc_ctl_sock = -1;
}
+ pthread_mutex_unlock(&ifc_sock_mutex);
}
void ifc_close6(void)
@@ -160,6 +163,7 @@
(void)close(ifc_ctl_sock6);
ifc_ctl_sock6 = -1;
}
+ pthread_mutex_unlock(&ifc_sock6_mutex);
}
static void ifc_init_ifr(const char *name, struct ifreq *ifr)
@@ -253,15 +257,18 @@
int prefixlen) {
int ifindex, s, len, ret;
struct sockaddr_storage ss;
+ int saved_errno;
void *addr;
size_t addrlen;
struct {
struct nlmsghdr n;
struct ifaddrmsg r;
- // Allow for IPv6 address, headers, and padding.
+ // Allow for IPv6 address, headers, IPv4 broadcast addr and padding.
char attrbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct rtattr)) +
- NLMSG_ALIGN(INET6_ADDRLEN)];
+ NLMSG_ALIGN(INET6_ADDRLEN) +
+ NLMSG_ALIGN(sizeof(struct rtattr)) +
+ NLMSG_ALIGN(INET_ADDRLEN)];
} req;
struct rtattr *rta;
struct nlmsghdr *nh;
@@ -316,16 +323,32 @@
req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen);
memcpy(RTA_DATA(rta), addr, addrlen);
+ // Add an explicit IFA_BROADCAST for IPv4 RTM_NEWADDRs.
+ if (ss.ss_family == AF_INET && action == RTM_NEWADDR) {
+ rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len));
+ rta->rta_type = IFA_BROADCAST;
+ rta->rta_len = RTA_LENGTH(addrlen);
+ req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen);
+ ((struct in_addr *)addr)->s_addr |= htonl((1<<(32-prefixlen))-1);
+ memcpy(RTA_DATA(rta), addr, addrlen);
+ }
+
s = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
- if (send(s, &req, req.n.nlmsg_len, 0) < 0) {
- close(s);
+ if (s < 0) {
return -errno;
}
+ if (send(s, &req, req.n.nlmsg_len, 0) < 0) {
+ saved_errno = errno;
+ close(s);
+ return -saved_errno;
+ }
+
len = recv(s, buf, sizeof(buf), 0);
+ saved_errno = errno;
close(s);
if (len < 0) {
- return -errno;
+ return -saved_errno;
}
// Parse the acknowledgement to find the return code.
@@ -534,6 +557,7 @@
ifc_init();
if (ifc_ctl_sock < 0) {
+ ifc_close();
return -errno;
}
diff --git a/include/netutils/ifc.h b/libnetutils/include/netutils/ifc.h
similarity index 100%
rename from include/netutils/ifc.h
rename to libnetutils/include/netutils/ifc.h
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
index cd26d05..e53a4c8 100644
--- a/libnetutils/packet.c
+++ b/libnetutils/packet.c
@@ -1,37 +1,36 @@
/*
* Copyright 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.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <stdlib.h>
#include <string.h>
-#include <unistd.h>
-#include <sys/uio.h>
#include <sys/socket.h>
+#include <sys/uio.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
-#include <linux/if_packet.h>
-#include <linux/if_ether.h>
-#include <errno.h>
+#include <unistd.h>
#ifdef ANDROID
#define LOG_TAG "DHCP"
-#include <cutils/log.h>
+#include <log/log.h>
#else
#include <stdio.h>
-#include <string.h>
#define ALOGD printf
#define ALOGW printf
#endif
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
new file mode 100644
index 0000000..70ff528
--- /dev/null
+++ b/libpackagelistparser/Android.bp
@@ -0,0 +1,13 @@
+cc_library {
+
+ name: "libpackagelistparser",
+ srcs: ["packagelistparser.c"],
+ shared_libs: ["liblog"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+
+ clang: true,
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+}
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
new file mode 100644
index 0000000..d602c26
--- /dev/null
+++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015, Intel Corporation
+ * 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.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ * This is a parser library for parsing the packages.list file generated
+ * by PackageManager service.
+ *
+ * This simple parser is sensitive to format changes in
+ * frameworks/base/services/core/java/com/android/server/pm/Settings.java
+ * A dependency note has been added to that file to correct
+ * this parser.
+ */
+
+#ifndef PACKAGELISTPARSER_H_
+#define PACKAGELISTPARSER_H_
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/** The file containing the list of installed packages on the system */
+#define PACKAGES_LIST_FILE "/data/system/packages.list"
+
+typedef struct pkg_info pkg_info;
+typedef struct gid_list gid_list;
+
+struct gid_list {
+ size_t cnt;
+ gid_t *gids;
+};
+
+struct pkg_info {
+ char *name;
+ uid_t uid;
+ bool debuggable;
+ char *data_dir;
+ char *seinfo;
+ gid_list gids;
+ void *private_data;
+};
+
+/**
+ * Callback function to be used by packagelist_parse() routine.
+ * @param info
+ * The parsed package information
+ * @param userdata
+ * The supplied userdata pointer to packagelist_parse()
+ * @return
+ * true to keep processing, false to stop.
+ */
+typedef bool (*pfn_on_package)(pkg_info *info, void *userdata);
+
+/**
+ * Parses the file specified by PACKAGES_LIST_FILE and invokes the callback on
+ * each entry found. Once the callback is invoked, ownership of the pkg_info pointer
+ * is passed to the callback routine, thus they are required to perform any cleanup
+ * desired.
+ * @param callback
+ * The callback function called on each parsed line of the packages list.
+ * @param userdata
+ * An optional userdata supplied pointer to pass to the callback function.
+ * @return
+ * true on success false on failure.
+ */
+extern bool packagelist_parse(pfn_on_package callback, void *userdata);
+
+/**
+ * Frees a pkg_info structure.
+ * @param info
+ * The struct to free
+ */
+extern void packagelist_free(pkg_info *info);
+
+__END_DECLS
+
+#endif /* PACKAGELISTPARSER_H_ */
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
new file mode 100644
index 0000000..3e1a3d1
--- /dev/null
+++ b/libpackagelistparser/packagelistparser.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2015, Intel Corporation
+ * 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.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ */
+
+#define LOG_TAG "packagelistparser"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/limits.h>
+
+#include <log/log.h>
+#include <packagelistparser/packagelistparser.h>
+
+#define CLOGE(fmt, ...) \
+ do {\
+ IF_ALOGE() {\
+ ALOGE(fmt, ##__VA_ARGS__);\
+ }\
+ } while(0)
+
+static size_t get_gid_cnt(const char *gids)
+{
+ size_t cnt;
+
+ if (*gids == '\0') {
+ return 0;
+ }
+
+ if (!strcmp(gids, "none")) {
+ return 0;
+ }
+
+ for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
+ ;
+
+ return cnt;
+}
+
+static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
+{
+ gid_t gid;
+ char* token;
+ char *endptr;
+ size_t cmp = 0;
+
+ while ((token = strsep(&gids, ",\r\n"))) {
+
+ if (cmp > *cnt) {
+ return false;
+ }
+
+ gid = strtoul(token, &endptr, 10);
+ if (*endptr != '\0') {
+ return false;
+ }
+
+ /*
+ * if unsigned long is greater than size of gid_t,
+ * prevent a truncation based roll-over
+ */
+ if (gid > GID_MAX) {
+ CLOGE("A gid in field \"gid list\" greater than GID_MAX");
+ return false;
+ }
+
+ gid_list[cmp++] = gid;
+ }
+ return true;
+}
+
+extern bool packagelist_parse(pfn_on_package callback, void *userdata)
+{
+
+ FILE *fp;
+ char *cur;
+ char *next;
+ char *endptr;
+ unsigned long tmp;
+ ssize_t bytesread;
+
+ bool rc = false;
+ char *buf = NULL;
+ size_t buflen = 0;
+ unsigned long lineno = 1;
+ const char *errmsg = NULL;
+ struct pkg_info *pkg_info = NULL;
+
+ fp = fopen(PACKAGES_LIST_FILE, "re");
+ if (!fp) {
+ CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
+ strerror(errno));
+ return false;
+ }
+
+ while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
+
+ pkg_info = calloc(1, sizeof(*pkg_info));
+ if (!pkg_info) {
+ goto err;
+ }
+
+ next = buf;
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for \"package name\"";
+ goto err;
+ }
+
+ pkg_info->name = strdup(cur);
+ if (!pkg_info->name) {
+ goto err;
+ }
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"uid\"";
+ goto err;
+ }
+
+ tmp = strtoul(cur, &endptr, 10);
+ if (*endptr != '\0') {
+ errmsg = "Could not convert field \"uid\" to integer value";
+ goto err;
+ }
+
+ /*
+ * if unsigned long is greater than size of uid_t,
+ * prevent a truncation based roll-over
+ */
+ if (tmp > UID_MAX) {
+ errmsg = "Field \"uid\" greater than UID_MAX";
+ goto err;
+ }
+
+ pkg_info->uid = (uid_t) tmp;
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"debuggable\"";
+ goto err;
+ }
+
+ tmp = strtoul(cur, &endptr, 10);
+ if (*endptr != '\0') {
+ errmsg = "Could not convert field \"debuggable\" to integer value";
+ goto err;
+ }
+
+ /* should be a valid boolean of 1 or 0 */
+ if (!(tmp == 0 || tmp == 1)) {
+ errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
+ goto err;
+ }
+
+ pkg_info->debuggable = (bool) tmp;
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"data dir\"";
+ goto err;
+ }
+
+ pkg_info->data_dir = strdup(cur);
+ if (!pkg_info->data_dir) {
+ goto err;
+ }
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"seinfo\"";
+ goto err;
+ }
+
+ pkg_info->seinfo = strdup(cur);
+ if (!pkg_info->seinfo) {
+ goto err;
+ }
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"gid(s)\"";
+ goto err;
+ }
+
+ /*
+ * Parse the gid list, could be in the form of none, single gid or list:
+ * none
+ * gid
+ * gid, gid ...
+ */
+ pkg_info->gids.cnt = get_gid_cnt(cur);
+ if (pkg_info->gids.cnt > 0) {
+
+ pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
+ if (!pkg_info->gids.gids) {
+ goto err;
+ }
+
+ rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
+ if (!rc) {
+ errmsg = "Could not parse field \"gid list\"";
+ goto err;
+ }
+ }
+
+ rc = callback(pkg_info, userdata);
+ if (rc == false) {
+ /*
+ * We do not log this as this can be intentional from
+ * callback to abort processing. We go to out to not
+ * free the pkg_info
+ */
+ rc = true;
+ goto out;
+ }
+ lineno++;
+ }
+
+ rc = true;
+
+out:
+ free(buf);
+ fclose(fp);
+ return rc;
+
+err:
+ if (errmsg) {
+ CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
+ PACKAGES_LIST_FILE, lineno, errmsg);
+ }
+ rc = false;
+ packagelist_free(pkg_info);
+ goto out;
+}
+
+void packagelist_free(pkg_info *info)
+{
+ if (info) {
+ free(info->name);
+ free(info->data_dir);
+ free(info->seinfo);
+ free(info->gids.gids);
+ free(info);
+ }
+}
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 697db25..55891db 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -14,8 +14,6 @@
codeflinger/load_store.cpp \
codeflinger/blending.cpp \
codeflinger/texturing.cpp \
- codeflinger/tinyutils/SharedBuffer.cpp \
- codeflinger/tinyutils/VectorImpl.cpp \
fixed.cpp.arm \
picker.cpp.arm \
pixelflinger.cpp.arm \
@@ -52,6 +50,14 @@
arch-mips/t32cb16blend.S \
endif
+
+PIXELFLINGER_SRC_FILES_mips64 := \
+ codeflinger/MIPSAssembler.cpp \
+ codeflinger/MIPS64Assembler.cpp \
+ codeflinger/mips64_disassem.c \
+ arch-mips64/col32cb16blend.S \
+ arch-mips64/t32cb16blend.S \
+
#
# Shared library
#
@@ -61,19 +67,13 @@
LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
+LOCAL_SRC_FILES_mips64 := $(PIXELFLINGER_SRC_FILES_mips64)
LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-LOCAL_SHARED_LIBRARIES := libcutils liblog
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) \
+ external/safe-iop/include
+LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
-# Really this should go away entirely or at least not depend on
-# libhardware, but this at least gets us built.
-LOCAL_SHARED_LIBRARIES += libhardware_legacy
-LOCAL_CFLAGS += -DWITH_LIB_HARDWARE
-# t32cb16blend.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
-# arch-arm64/col32cb16blend.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libpixelflinger/arch-arm64/col32cb16blend.S b/libpixelflinger/arch-arm64/col32cb16blend.S
index 18a01fd..84596f9 100644
--- a/libpixelflinger/arch-arm64/col32cb16blend.S
+++ b/libpixelflinger/arch-arm64/col32cb16blend.S
@@ -26,7 +26,7 @@
* SUCH DAMAGE.
*/
.text
- .align
+ .balign 0
.global scanline_col32cb16blend_arm64
diff --git a/libpixelflinger/arch-arm64/t32cb16blend.S b/libpixelflinger/arch-arm64/t32cb16blend.S
index 7da8cf5..b1a950d 100644
--- a/libpixelflinger/arch-arm64/t32cb16blend.S
+++ b/libpixelflinger/arch-arm64/t32cb16blend.S
@@ -26,7 +26,7 @@
* SUCH DAMAGE.
*/
.text
- .align
+ .balign 0
.global scanline_t32cb16blend_arm64
diff --git a/libpixelflinger/arch-mips/col32cb16blend.S b/libpixelflinger/arch-mips/col32cb16blend.S
new file mode 100644
index 0000000..810294c
--- /dev/null
+++ b/libpixelflinger/arch-mips/col32cb16blend.S
@@ -0,0 +1,134 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+ .macro pixel dreg src f sR sG sB shift
+
+#if __mips==32 && __mips_isa_rev>=2
+ /* extract red */
+ ext $t4,\src,\shift+11,5
+ mul $t4,$t4,\f
+
+ /* extract green */
+ ext $t5,\src,\shift+5,6
+ mul $t5,$t5,\f
+
+ /* extract blue */
+ ext $t6,\src,\shift,5
+ mul $t6,$t6,\f
+#else
+ /* extract red */
+ srl $t4,\src,\shift+11
+ andi $t4, 0x1f
+ mul $t4,$t4,\f
+
+ /* extract green */
+ srl $t5,\src,\shift+5
+ andi $t5, 0x3f
+ mul $t5,$t5,\f
+
+ /* extract blue */
+ srl $t6,\src,\shift
+ andi $t6, 0x1f
+ mul $t6,$t6,\f
+#endif
+
+ srl $t4,$t4,8
+ srl $t5,$t5,8
+ srl $t6,$t6,8
+ addu $t4,$t4,\sR
+ addu $t5,$t5,\sG
+ addu \dreg,$t6,\sB
+ sll $t4,$t4,11
+ sll $t5,$t5,5
+ or \dreg,\dreg,$t4
+ or \dreg,\dreg,$t5
+ andi \dreg, 0xffff
+ .endm
+
+ .text
+ .balign 4
+
+ .global scanline_col32cb16blend_mips
+ .ent scanline_col32cb16blend_mips
+scanline_col32cb16blend_mips:
+
+ /* check if count is zero */
+ srl $v0,$a1,24 /* sA */
+ beqz $a2,done
+ li $t4, 0x100
+ srl $v1,$v0,7
+ addu $v0,$v1,$v0
+ subu $v0,$t4,$v0 /* f */
+#if __mips==32 && __mips_isa_rev>=2
+ ext $a3,$a1,3,5 /* sR */
+ ext $t0,$a1,10,6 /* sG */
+ ext $t1,$a1,19,5 /* sB */
+#else
+ srl $a3, $a1, 3
+ andi $a3, 0x1f /* sR */
+ srl $t0, $a1, 10
+ andi $t0, 0x3f /* sG */
+ srl $t1, $a1, 19
+ andi $t1, 0x1f /* sB */
+#endif
+
+ /* check if cnt is at least 4 */
+ addiu $a2,$a2,-4
+ bltz $a2,tail
+
+loop_4pixels:
+ lw $t7,0($a0)
+ lw $t8,4($a0)
+ addiu $a0,$a0,8
+ addiu $a2,$a2,-4
+ pixel $t2 $t7 $v0 $a3 $t0 $t1 0
+ pixel $t3 $t7 $v0 $a3 $t0 $t1 16
+#if __mips==32 && __mips_isa_rev>=2
+ ins $t2,$t3,16,16
+#else
+ sll $t3, 16
+ or $t2, $t2, $t3
+#endif
+ pixel $t7 $t8 $v0 $a3 $t0 $t1 0
+ pixel $t3 $t8 $v0 $a3 $t0 $t1 16
+#if __mips==32 && __mips_isa_rev>=2
+ ins $t7,$t3,16,16
+#else
+ sll $t3, 16
+ or $t7, $t7, $t3
+#endif
+ sw $t2,-8($a0)
+ sw $t7,-4($a0)
+ bgez $a2, loop_4pixels
+
+tail:
+ /* the pixel count underran, restore it now */
+ addiu $a2,$a2,4
+
+ /* handle the last 0..3 pixels */
+ beqz $a2,done
+
+loop_1pixel:
+ lhu $t7,0($a0)
+ addiu $a0,$a0,2
+ addiu $a2,$a2,-1
+ pixel $t2 $t7 $v0 $a3 $t0 $t1 0
+ sh $t2, -2($a0)
+ bnez $a2,loop_1pixel
+
+done:
+ j $ra
+ .end scanline_col32cb16blend_mips
diff --git a/libpixelflinger/arch-mips/t32cb16blend.S b/libpixelflinger/arch-mips/t32cb16blend.S
index c911fbb..1d2fb8f 100644
--- a/libpixelflinger/arch-mips/t32cb16blend.S
+++ b/libpixelflinger/arch-mips/t32cb16blend.S
@@ -33,232 +33,241 @@
*/
#if __mips==32 && __mips_isa_rev>=2
- .macro pixel dreg src fb shift
- /*
- * sA = s >> 24
- * f = 0x100 - (sA + (sA>>7))
- */
-DBG .set noat
-DBG rdhwr $at,$2
-DBG .set at
+ .macro pixel dreg src fb shift
+ /*
+ * sA = s >> 24
+ * f = 0x100 - (sA + (sA>>7))
+ */
+DBG .set noat
+DBG rdhwr $at,$2
+DBG .set at
- srl $t7,\src,24
- srl $t6,$t7,7
- addu $t7,$t6
- li $t6,0x100
- subu $t7,$t6,$t7
+ srl $t7,\src,24
+ srl $t6,$t7,7
+ addu $t7,$t6
+ li $t6,0x100
+ subu $t7,$t6,$t7
- /* red */
- ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11]
- mul $t6,$t8,$t7
- ext $t0,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5]
- ext $t8,\src,3,5 # src[7..3]
- srl $t6,8
- addu $t8,$t6
- ins \fb,$t8,\shift+6+5,5 # dst[\shift:15..11]
+ /* red */
+ ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11]
+ mul $t6,$t8,$t7
+ ext $t0,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5]
+ ext $t8,\src,3,5 # src[7..3]
+ srl $t6,8
+ addu $t8,$t6
+.if \shift!=0
+ sll $t8,\shift+11
+ or \fb,$t8
+.else
+ sll \fb,$t8,11
+.endif
- /* green */
- mul $t8,$t0,$t7
- ext $t0,\dreg,\shift,5 # start blue extraction dst[\shift:4..0]
- ext $t6,\src,2+8,6 # src[15..10]
- srl $t8,8
- addu $t8,$t6
+ /* green */
+ mul $t8,$t0,$t7
+ ext $t0,\dreg,\shift,5 # start blue extraction dst[\shift:4..0]
+ ext $t6,\src,2+8,6 # src[15..10]
+ srl $t8,8
+ addu $t8,$t6
- /* blue */
- mul $t0,$t0,$t7
- ins \fb,$t8,\shift+5,6 # finish green insertion dst[\shift:10..5]
- ext $t6,\src,(3+8+8),5
- srl $t8,$t0,8
- addu $t8,$t6
- ins \fb,$t8,\shift,5
+ /* blue */
+ mul $t0,$t0,$t7
+ sll $t8, $t8, \shift+5
+ or \fb, \fb, $t8
+ ext $t6,\src,(3+8+8),5
+ srl $t8,$t0,8
+ addu $t8,$t6
+ sll $t8, $t8, \shift
+ or \fb, \fb, $t8
-DBG .set noat
-DBG rdhwr $t8,$2
-DBG subu $t8,$at
-DBG sltu $at,$t8,$v0
-DBG movn $v0,$t8,$at
-DBG sgtu $at,$t8,$v1
-DBG movn $v1,$t8,$at
-DBG .set at
- .endm
+DBG .set noat
+DBG rdhwr $t8,$2
+DBG subu $t8,$at
+DBG sltu $at,$t8,$v0
+DBG movn $v0,$t8,$at
+DBG sgtu $at,$t8,$v1
+DBG movn $v1,$t8,$at
+DBG .set at
+ .endm
#else
- .macro pixel dreg src fb shift
- /*
- * sA = s >> 24
- * f = 0x100 - (sA + (sA>>7))
- */
-DBG .set push
-DBG .set noat
-DBG .set mips32r2
-DBG rdhwr $at,$2
-DBG .set pop
+ .macro pixel dreg src fb shift
+ /*
+ * sA = s >> 24
+ * f = 0x100 - (sA + (sA>>7))
+ */
+DBG .set push
+DBG .set noat
+DBG .set mips32r2
+DBG rdhwr $at,$2
+DBG .set pop
- srl $t7,\src,24
- srl $t6,$t7,7
- addu $t7,$t6
- li $t6,0x100
- subu $t7,$t6,$t7
+ srl $t7,\src,24
+ srl $t6,$t7,7
+ addu $t7,$t6
+ li $t6,0x100
+ subu $t7,$t6,$t7
- /*
- * red
- * dR = (d >> (6 + 5)) & 0x1f;
- * dR = (f*dR)>>8
- * sR = (s >> ( 3)) & 0x1f;
- * sR += dR
- * fb |= sR << 11
- */
- srl $t8,\dreg,\shift+6+5
+ /*
+ * red
+ * dR = (d >> (6 + 5)) & 0x1f;
+ * dR = (f*dR)>>8
+ * sR = (s >> ( 3)) & 0x1f;
+ * sR += dR
+ * fb |= sR << 11
+ */
+ srl $t8,\dreg,\shift+6+5
.if \shift==0
- and $t8,0x1f
+ and $t8,0x1f
.endif
- mul $t8,$t8,$t7
- srl $t6,\src,3
- and $t6,0x1f
- srl $t8,8
- addu $t8,$t6
+ mul $t8,$t8,$t7
+ srl $t6,\src,3
+ and $t6,0x1f
+ srl $t8,8
+ addu $t8,$t6
.if \shift!=0
- sll $t8,\shift+11
- or \fb,$t8
+ sll $t8,\shift+11
+ or \fb,$t8
.else
- sll \fb,$t8,11
+ sll \fb,$t8,11
.endif
/*
- * green
- * dG = (d >> 5) & 0x3f
- * dG = (f*dG) >> 8
- * sG = (s >> ( 8+2))&0x3F;
- */
- srl $t8,\dreg,\shift+5
- and $t8,0x3f
- mul $t8,$t8,$t7
- srl $t6,\src,8+2
- and $t6,0x3f
- srl $t8,8
- addu $t8,$t6
- sll $t8,\shift + 5
- or \fb,$t8
+ * green
+ * dG = (d >> 5) & 0x3f
+ * dG = (f*dG) >> 8
+ * sG = (s >> ( 8+2))&0x3F;
+ */
+ srl $t8,\dreg,\shift+5
+ and $t8,0x3f
+ mul $t8,$t8,$t7
+ srl $t6,\src,8+2
+ and $t6,0x3f
+ srl $t8,8
+ addu $t8,$t6
+ sll $t8,\shift + 5
+ or \fb,$t8
- /* blue */
+ /* blue */
.if \shift!=0
- srl $t8,\dreg,\shift
- and $t8,0x1f
+ srl $t8,\dreg,\shift
+ and $t8,0x1f
.else
- and $t8,\dreg,0x1f
+ and $t8,\dreg,0x1f
.endif
- mul $t8,$t8,$t7
- srl $t6,\src,(8+8+3)
- and $t6,0x1f
- srl $t8,8
- addu $t8,$t6
+ mul $t8,$t8,$t7
+ srl $t6,\src,(8+8+3)
+ and $t6,0x1f
+ srl $t8,8
+ addu $t8,$t6
.if \shift!=0
- sll $t8,\shift
+ sll $t8,\shift
.endif
- or \fb,$t8
-DBG .set push
-DBG .set noat
-DBG .set mips32r2
-DBG rdhwr $t8,$2
-DBG subu $t8,$at
-DBG sltu $at,$t8,$v0
-DBG movn $v0,$t8,$at
-DBG sgtu $at,$t8,$v1
-DBG movn $v1,$t8,$at
-DBG .set pop
- .endm
+ or \fb,$t8
+DBG .set push
+DBG .set noat
+DBG .set mips32r2
+DBG rdhwr $t8,$2
+DBG subu $t8,$at
+DBG sltu $at,$t8,$v0
+DBG movn $v0,$t8,$at
+DBG sgtu $at,$t8,$v1
+DBG movn $v1,$t8,$at
+DBG .set pop
+ .endm
#endif
- .text
- .align
+ .text
+ .balign 4
- .global scanline_t32cb16blend_mips
- .ent scanline_t32cb16blend_mips
+ .global scanline_t32cb16blend_mips
+ .ent scanline_t32cb16blend_mips
scanline_t32cb16blend_mips:
-DBG li $v0,0xffffffff
-DBG li $v1,0
- /* Align the destination if necessary */
- and $t0,$a0,3
- beqz $t0,aligned
+DBG li $v0,0xffffffff
+DBG li $v1,0
+ /* Align the destination if necessary */
+ and $t0,$a0,3
+ beqz $t0,aligned
- /* as long as there is at least one pixel */
- beqz $a2,done
+ /* as long as there is at least one pixel */
+ beqz $a2,done
- lw $t4,($a1)
- addu $a0,2
- addu $a1,4
- beqz $t4,1f
- lhu $t3,-2($a0)
- pixel $t3,$t4,$t1,0
- sh $t1,-2($a0)
-1: subu $a2,1
+ lw $t4,($a1)
+ addu $a0,2
+ addu $a1,4
+ beqz $t4,1f
+ lhu $t3,-2($a0)
+ pixel $t3,$t4,$t1,0
+ sh $t1,-2($a0)
+1: subu $a2,1
aligned:
- /* Check to see if its worth unrolling the loop */
- subu $a2,4
- bltz $a2,tail
+ /* Check to see if its worth unrolling the loop */
+ subu $a2,4
+ bltz $a2,tail
- /* Process 4 pixels at a time */
+ /* Process 4 pixels at a time */
fourpixels:
- /* 1st pair of pixels */
- lw $t4,0($a1)
- lw $t5,4($a1)
- addu $a0,8
- addu $a1,16
+ /* 1st pair of pixels */
+ lw $t4,0($a1)
+ lw $t5,4($a1)
+ addu $a0,8
+ addu $a1,16
- /* both are zero, skip this pair */
- or $t3,$t4,$t5
- beqz $t3,1f
+ /* both are zero, skip this pair */
+ or $t3,$t4,$t5
+ beqz $t3,1f
- /* load the destination */
- lw $t3,-8($a0)
+ /* load the destination */
+ lw $t3,-8($a0)
- pixel $t3,$t4,$t1,0
- pixel $t3,$t5,$t1,16
- sw $t1,-8($a0)
+ pixel $t3,$t4,$t1,0
+ andi $t1, 0xFFFF
+ pixel $t3,$t5,$t1,16
+ sw $t1,-8($a0)
1:
- /* 2nd pair of pixels */
- lw $t4,-8($a1)
- lw $t5,-4($a1)
+ /* 2nd pair of pixels */
+ lw $t4,-8($a1)
+ lw $t5,-4($a1)
- /* both are zero, skip this pair */
- or $t3,$t4,$t5
- beqz $t3,1f
+ /* both are zero, skip this pair */
+ or $t3,$t4,$t5
+ beqz $t3,1f
- /* load the destination */
- lw $t3,-4($a0)
+ /* load the destination */
+ lw $t3,-4($a0)
- pixel $t3,$t4,$t1,0
- pixel $t3,$t5,$t1,16
- sw $t1,-4($a0)
+ pixel $t3,$t4,$t1,0
+ andi $t1, 0xFFFF
+ pixel $t3,$t5,$t1,16
+ sw $t1,-4($a0)
-1: subu $a2,4
- bgtz $a2,fourpixels
+1: subu $a2,4
+ bgtz $a2,fourpixels
tail:
- /* the pixel count underran, restore it now */
- addu $a2,4
+ /* the pixel count underran, restore it now */
+ addu $a2,4
- /* handle the last 0..3 pixels */
- beqz $a2,done
+ /* handle the last 0..3 pixels */
+ beqz $a2,done
onepixel:
- lw $t4,($a1)
- addu $a0,2
- addu $a1,4
- beqz $t4,1f
- lhu $t3,-2($a0)
- pixel $t3,$t4,$t1,0
- sh $t1,-2($a0)
-1: subu $a2,1
- bnez $a2,onepixel
+ lw $t4,($a1)
+ addu $a0,2
+ addu $a1,4
+ beqz $t4,1f
+ lhu $t3,-2($a0)
+ pixel $t3,$t4,$t1,0
+ sh $t1,-2($a0)
+1: subu $a2,1
+ bnez $a2,onepixel
done:
-DBG .set push
-DBG .set mips32r2
-DBG rdhwr $a0,$3
-DBG mul $v0,$a0
-DBG mul $v1,$a0
-DBG .set pop
- j $ra
- .end scanline_t32cb16blend_mips
+DBG .set push
+DBG .set mips32r2
+DBG rdhwr $a0,$3
+DBG mul $v0,$a0
+DBG mul $v1,$a0
+DBG .set pop
+ j $ra
+ .end scanline_t32cb16blend_mips
diff --git a/libpixelflinger/arch-mips64/col32cb16blend.S b/libpixelflinger/arch-mips64/col32cb16blend.S
new file mode 100644
index 0000000..5baffb1
--- /dev/null
+++ b/libpixelflinger/arch-mips64/col32cb16blend.S
@@ -0,0 +1,108 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+ .macro pixel dreg src f sR sG sB shift
+
+ /* extract red */
+.if \shift < 32
+ dext $t0,\src,\shift+11,5
+.else
+ dextu $t0,\src,\shift+11,5
+.endif
+ mul $t0,$t0,\f
+
+ /* extract green */
+.if \shift < 32
+ dext $t1,\src,\shift+5,6
+.else
+ dextu $t1,\src,\shift+5,6
+.endif
+ mul $t1,$t1,\f
+
+ /* extract blue */
+.if \shift < 32
+ dext $t2,\src,\shift,5
+.else
+ dextu $t2,\src,\shift,5
+.endif
+ mul $t2,$t2,\f
+
+ srl $t0,$t0,8
+ srl $t1,$t1,8
+ srl $t2,$t2,8
+ addu $t0,$t0,\sR
+ addu $t1,$t1,\sG
+ addu \dreg,$t2,\sB
+ sll $t0,$t0,11
+ sll $t1,$t1,5
+ or \dreg,\dreg,$t0
+ or \dreg,\dreg,$t1
+ .endm
+
+ .text
+ .balign 4
+
+ .global scanline_col32cb16blend_mips64
+ .ent scanline_col32cb16blend_mips64
+scanline_col32cb16blend_mips64:
+
+ /* check if count is zero */
+ srl $v0,$a1,24 /* sA */
+ beqz $a2,done
+ li $t0, 0x100
+ srl $v1,$v0,7
+ addu $v0,$v1,$v0
+ subu $v0,$t0,$v0 /* f */
+ ext $a3,$a1,3,5 /* sR */
+ ext $a4,$a1,10,6 /* sG */
+ ext $a5,$a1,19,5 /* sB */
+
+ /* check if cnt is at least 4 */
+ addiu $a2,$a2,-4
+ bltz $a2,tail
+
+loop_4pixels:
+ ld $t3,0($a0)
+ daddiu $a0,$a0,8
+ addiu $a2,$a2,-4
+ pixel $a6 $t3 $v0 $a3 $a4 $a5 0
+ pixel $a7 $t3 $v0 $a3 $a4 $a5 16
+ pixel $t8 $t3 $v0 $a3 $a4 $a5 32
+ pixel $t9 $t3 $v0 $a3 $a4 $a5 48
+ dins $a6,$a7,16,16
+ dinsu $a6,$t8,32,16
+ dinsu $a6,$t9,48,16
+ sd $a6,-8($a0)
+ bgez $a2, loop_4pixels
+
+tail:
+ /* the pixel count underran, restore it now */
+ addiu $a2,$a2,4
+
+ /* handle the last 0..3 pixels */
+ beqz $a2,done
+
+loop_1pixel:
+ lhu $t3,0($a0)
+ daddiu $a0,$a0,2
+ addiu $a2,$a2,-1
+ pixel $a6 $t3 $v0 $a3 $a4 $a5 0
+ sh $a6, -2($a0)
+ bnez $a2,loop_1pixel
+
+done:
+ j $ra
+ .end scanline_col32cb16blend_mips64
diff --git a/libpixelflinger/arch-mips64/t32cb16blend.S b/libpixelflinger/arch-mips64/t32cb16blend.S
new file mode 100644
index 0000000..3cb5f93
--- /dev/null
+++ b/libpixelflinger/arch-mips64/t32cb16blend.S
@@ -0,0 +1,172 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifdef DEBUG
+#define DBG
+#else
+#define DBG #
+#endif
+
+/*
+ * blend one of 2 16bpp RGB pixels held in dreg selected by shift
+ * with the 32bpp ABGR pixel held in src and store the result in fb
+ *
+ * Assumes that the dreg data is little endian and that
+ * the the second pixel (shift==16) will be merged into
+ * the fb result
+ *
+ * Uses $a4,$t2,$t3,$t8
+ */
+
+ .macro pixel dreg src fb shift
+ /*
+ * sA = s >> 24
+ * f = 0x100 - (sA + (sA>>7))
+ */
+ srl $t3,\src,24
+ srl $t2,$t3,7
+ addu $t3,$t2
+ li $t2,0x100
+ subu $t3,$t2,$t3
+
+ /* red */
+ ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11]
+ mul $t2,$t8,$t3
+ ext $a4,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5]
+ ext $t8,\src,3,5 # src[7..3]
+ srl $t2,8
+ addu $t8,$t2
+.if \shift!=0
+ sll $t8,\shift+11 # dst[\shift:15..11]
+ or \fb,$t8
+.else
+ sll \fb,$t8,11
+.endif
+
+ /* green */
+ mul $t8,$a4,$t3
+ ext $a4,\dreg,\shift,5 # start blue extraction dst[\shift:4..0]
+ ext $t2,\src,2+8,6 # src[15..10]
+ srl $t8,8
+ addu $t8,$t2
+
+ /* blue */
+ mul $a4,$a4,$t3
+ sll $t8, $t8, \shift+5 # finish green insertion dst[\shift:10..5]
+ or \fb, \fb, $t8
+ ext $t2,\src,(3+8+8),5
+ srl $t8,$a4,8
+ addu $t8,$t2
+ sll $t8, $t8, \shift
+ or \fb, \fb, $t8
+ .endm
+
+ .text
+ .balign 4
+
+ .global scanline_t32cb16blend_mips64
+ .ent scanline_t32cb16blend_mips64
+scanline_t32cb16blend_mips64:
+ daddiu $sp, $sp, -40
+DBG li $v0,0xffffffff
+DBG li $v1,0
+ /* Align the destination if necessary */
+ and $a4,$a0,3
+ beqz $a4,aligned
+
+ /* as long as there is at least one pixel */
+ beqz $a2,done
+
+ lw $t0,($a1)
+ daddu $a0,2
+ daddu $a1,4
+ beqz $t0,1f
+ lhu $a7,-2($a0)
+ pixel $a7,$t0,$a5,0
+ sh $a5,-2($a0)
+1: subu $a2,1
+
+aligned:
+ /* Check to see if its worth unrolling the loop */
+ subu $a2,4
+ bltz $a2,tail
+
+ /* Process 4 pixels at a time */
+fourpixels:
+ /* 1st pair of pixels */
+ lw $t0,0($a1)
+ lw $t1,4($a1)
+ daddu $a0,8
+ daddu $a1,16
+
+ /* both are zero, skip this pair */
+ or $a7,$t0,$t1
+ beqz $a7,1f
+
+ /* load the destination */
+ lw $a7,-8($a0)
+
+ pixel $a7,$t0,$a5,0
+ andi $a5, 0xFFFF
+ pixel $a7,$t1,$a5,16
+ sw $a5,-8($a0)
+
+1:
+ /* 2nd pair of pixels */
+ lw $t0,-8($a1)
+ lw $t1,-4($a1)
+
+ /* both are zero, skip this pair */
+ or $a7,$t0,$t1
+ beqz $a7,1f
+
+ /* load the destination */
+ lw $a7,-4($a0)
+
+ pixel $a7,$t0,$a5,0
+ andi $a5, 0xFFFF
+ pixel $a7,$t1,$a5,16
+ sw $a5,-4($a0)
+
+1: subu $a2,4
+ bgtz $a2,fourpixels
+
+tail:
+ /* the pixel count underran, restore it now */
+ addu $a2,4
+
+ /* handle the last 0..3 pixels */
+ beqz $a2,done
+onepixel:
+ lw $t0,($a1)
+ daddu $a0,2
+ daddu $a1,4
+ beqz $t0,1f
+ lhu $a7,-2($a0)
+ pixel $a7,$t0,$a5,0
+ sh $a5,-2($a0)
+1: subu $a2,1
+ bnez $a2,onepixel
+done:
+DBG .set push
+DBG .set mips32r2
+DBG rdhwr $a0,$3
+DBG mul $v0,$a0
+DBG mul $v1,$a0
+DBG .set pop
+ daddiu $sp, $sp, 40
+ j $ra
+ .end scanline_t32cb16blend_mips64
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index 92243da..ac009a9 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -2,16 +2,16 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -19,13 +19,9 @@
#include <stdio.h>
#include <stdlib.h>
-#include <cutils/log.h>
+
#include <cutils/properties.h>
-
-#if defined(WITH_LIB_HARDWARE)
-#include <hardware_legacy/qemu_tracing.h>
-#endif
-
+#include <log/log.h>
#include <private/pixelflinger/ggl_context.h>
#include "ARMAssembler.h"
@@ -48,9 +44,6 @@
{
mBase = mPC = (uint32_t *)assembly->base();
mDuration = ggl_system_time();
-#if defined(WITH_LIB_HARDWARE)
- mQemuTracing = true;
-#endif
}
ARMAssembler::~ARMAssembler()
@@ -184,13 +177,6 @@
const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
-#if defined(WITH_LIB_HARDWARE)
- if (__builtin_expect(mQemuTracing, 0)) {
- int err = qemu_add_mapping(uintptr_t(base()), name);
- mQemuTracing = (err >= 0);
- }
-#endif
-
char value[PROPERTY_VALUE_MAX];
property_get("debug.pf.disasm", value, "0");
if (atoi(value) != 0) {
diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h
index c03dd9a..76acf7e 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.h
+++ b/libpixelflinger/codeflinger/ARMAssembler.h
@@ -21,9 +21,9 @@
#include <stdint.h>
#include <sys/types.h>
-#include "tinyutils/Vector.h"
-#include "tinyutils/KeyedVector.h"
#include "tinyutils/smartpointer.h"
+#include "utils/Vector.h"
+#include "utils/KeyedVector.h"
#include "ARMAssemblerInterface.h"
#include "CodeCache.h"
@@ -35,7 +35,7 @@
class ARMAssembler : public ARMAssemblerInterface
{
public:
- ARMAssembler(const sp<Assembly>& assembly);
+ explicit ARMAssembler(const sp<Assembly>& assembly);
virtual ~ARMAssembler();
uint32_t* base() const;
@@ -167,9 +167,6 @@
uint32_t* mPC;
uint32_t* mPrologPC;
int64_t mDuration;
-#if defined(WITH_LIB_HARDWARE)
- bool mQemuTracing;
-#endif
struct branch_target_t {
inline branch_target_t() : label(0), pc(0) { }
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
index 5041999..c96cf4b 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
@@ -2,26 +2,27 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
+#define LOG_TAG "pixelflinger-code"
#include <errno.h>
-#include <stdlib.h>
#include <stdint.h>
+#include <stdlib.h>
#include <sys/types.h>
-#include <cutils/log.h>
+#include <log/log.h>
+
#include "ARMAssemblerInterface.h"
namespace android {
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
index 40cbfcf..72935ac 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
@@ -63,7 +63,7 @@
};
enum {
- CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64
+ CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64, CODEGEN_ARCH_MIPS64
};
// -----------------------------------------------------------------------
@@ -115,7 +115,8 @@
// data processing...
enum {
opAND, opEOR, opSUB, opRSB, opADD, opADC, opSBC, opRSC,
- opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN
+ opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN,
+ opADD64, opSUB64
};
virtual void
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
index b852794..10d0390 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerProxy.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
@@ -34,7 +34,7 @@
// ARMAssemblerProxy take ownership of the target
ARMAssemblerProxy();
- ARMAssemblerProxy(ARMAssemblerInterface* target);
+ explicit ARMAssemblerProxy(ARMAssemblerInterface* target);
virtual ~ARMAssemblerProxy();
void setTarget(ARMAssemblerInterface* target);
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
index bd11818..bff87bb 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.cpp
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -32,14 +32,13 @@
#include <stdlib.h>
#include <string.h>
-#include <cutils/log.h>
#include <cutils/properties.h>
+#include <log/log.h>
#include <private/pixelflinger/ggl_context.h>
#include "codeflinger/Arm64Assembler.h"
-#include "codeflinger/CodeCache.h"
#include "codeflinger/Arm64Disassembler.h"
-
+#include "codeflinger/CodeCache.h"
/*
** --------------------------------------------
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.h b/libpixelflinger/codeflinger/Arm64Assembler.h
index 8479270..527c757 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.h
+++ b/libpixelflinger/codeflinger/Arm64Assembler.h
@@ -32,9 +32,9 @@
#include <stdint.h>
#include <sys/types.h>
-#include "tinyutils/Vector.h"
-#include "tinyutils/KeyedVector.h"
#include "tinyutils/smartpointer.h"
+#include "utils/Vector.h"
+#include "utils/KeyedVector.h"
#include "tinyutils/smartpointer.h"
#include "codeflinger/ARMAssemblerInterface.h"
@@ -47,8 +47,8 @@
class ArmToArm64Assembler : public ARMAssemblerInterface
{
public:
- ArmToArm64Assembler(const sp<Assembly>& assembly);
- ArmToArm64Assembler(void *base);
+ explicit ArmToArm64Assembler(const sp<Assembly>& assembly);
+ explicit ArmToArm64Assembler(void *base);
virtual ~ArmToArm64Assembler();
uint32_t* base() const;
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index d770302..8516640 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -15,18 +15,16 @@
** limitations under the License.
*/
+#define LOG_TAG "CodeCache"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <sys/mman.h>
+#include <unistd.h>
#include <cutils/ashmem.h>
-#include <cutils/atomic.h>
-#define LOG_TAG "CodeCache"
-#include <cutils/log.h>
-
+#include <log/log.h>
#include "CodeCache.h"
@@ -63,7 +61,7 @@
#define USAGE_ERROR_ACTION(m,p) \
heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p)
-#include "../../../../bionic/libc/upstream-dlmalloc/malloc.c"
+#include "../../../../external/dlmalloc/malloc.c"
static void heap_error(const char* msg, const char* function, void* p) {
ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: CODE FLINGER: %s IN %s addr=%p",
@@ -101,7 +99,7 @@
}
Assembly::Assembly(size_t size)
- : mCount(1), mSize(0)
+ : mCount(0), mSize(0)
{
mBase = (uint32_t*)mspace_malloc(getMspace(), size);
LOG_ALWAYS_FATAL_IF(mBase == NULL,
@@ -117,12 +115,12 @@
void Assembly::incStrong(const void*) const
{
- android_atomic_inc(&mCount);
+ mCount.fetch_add(1, std::memory_order_relaxed);
}
void Assembly::decStrong(const void*) const
{
- if (android_atomic_dec(&mCount) == 1) {
+ if (mCount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
delete this;
}
}
diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h
index fa67dd0..9326453 100644
--- a/libpixelflinger/codeflinger/CodeCache.h
+++ b/libpixelflinger/codeflinger/CodeCache.h
@@ -19,11 +19,12 @@
#ifndef ANDROID_CODECACHE_H
#define ANDROID_CODECACHE_H
+#include <atomic>
#include <stdint.h>
#include <pthread.h>
#include <sys/types.h>
-#include "tinyutils/KeyedVector.h"
+#include "utils/KeyedVector.h"
#include "tinyutils/smartpointer.h"
namespace android {
@@ -42,7 +43,7 @@
class AssemblyKey : public AssemblyKeyBase
{
public:
- AssemblyKey(const T& rhs) : mKey(rhs) { }
+ explicit AssemblyKey(const T& rhs) : mKey(rhs) { }
virtual int compare_type(const AssemblyKeyBase& key) const {
const T& rhs = static_cast<const AssemblyKey&>(key).mKey;
return android::compare_type(mKey, rhs);
@@ -56,7 +57,7 @@
class Assembly
{
public:
- Assembly(size_t size);
+ explicit Assembly(size_t size);
virtual ~Assembly();
ssize_t size() const;
@@ -69,7 +70,7 @@
typedef void weakref_type;
private:
- mutable int32_t mCount;
+ mutable std::atomic<int32_t> mCount;
uint32_t* mBase;
size_t mSize;
};
@@ -80,13 +81,13 @@
{
public:
// pretty simple cache API...
- CodeCache(size_t size);
- ~CodeCache();
-
- sp<Assembly> lookup(const AssemblyKeyBase& key) const;
+ explicit CodeCache(size_t size);
+ ~CodeCache();
- int cache( const AssemblyKeyBase& key,
- const sp<Assembly>& assembly);
+ sp<Assembly> lookup(const AssemblyKeyBase& key) const;
+
+ int cache(const AssemblyKeyBase& key,
+ const sp<Assembly>& assembly);
private:
// nothing to see here...
@@ -105,7 +106,7 @@
const AssemblyKeyBase* mKey;
public:
key_t() { };
- key_t(const AssemblyKeyBase& k) : mKey(&k) { }
+ explicit key_t(const AssemblyKeyBase& k) : mKey(&k) { }
};
mutable pthread_mutex_t mLock;
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
index 325caba..91fbd53 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.cpp
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -2,16 +2,16 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -19,10 +19,11 @@
#include <assert.h>
#include <stdint.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/types.h>
-#include <cutils/log.h>
+
+#include <log/log.h>
#include "GGLAssembler.h"
@@ -893,7 +894,8 @@
return;
}
- if (getCodegenArch() == CODEGEN_ARCH_MIPS) {
+ if ((getCodegenArch() == CODEGEN_ARCH_MIPS) ||
+ (getCodegenArch() == CODEGEN_ARCH_MIPS64)) {
// MIPS can do 16-bit imm in 1 instr, 32-bit in 3 instr
// the below ' while (mask)' code is buggy on mips
// since mips returns true on isValidImmediate()
@@ -1057,7 +1059,8 @@
RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch)
: mRegs(0), mTouched(0), mStatus(0), mArch(codegen_arch), mRegisterOffset(0)
{
- if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+ if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) ||
+ (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) {
mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17
}
reserve(ARMAssemblerInterface::SP);
@@ -1067,7 +1070,8 @@
RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs, int codegen_arch)
: mRegs(rhs.mRegs), mTouched(rhs.mTouched), mArch(codegen_arch), mRegisterOffset(0)
{
- if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+ if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) ||
+ (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) {
mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17
}
}
diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h
index 9db20df..47dbf3a 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.h
+++ b/libpixelflinger/codeflinger/GGLAssembler.h
@@ -49,7 +49,7 @@
public:
class RegisterFile;
- RegisterAllocator(int arch);
+ RegisterAllocator(int arch); // NOLINT, implicit
RegisterFile& registerFile();
int reserveReg(int reg);
int obtainReg();
@@ -59,7 +59,7 @@
class RegisterFile
{
public:
- RegisterFile(int arch);
+ RegisterFile(int arch); // NOLINT, implicit
RegisterFile(const RegisterFile& rhs, int arch);
~RegisterFile();
@@ -101,7 +101,7 @@
class Scratch
{
public:
- Scratch(RegisterFile& regFile)
+ explicit Scratch(RegisterFile& regFile)
: mRegFile(regFile), mScratch(0) {
}
~Scratch() {
@@ -177,8 +177,8 @@
{
public:
- GGLAssembler(ARMAssemblerInterface* target);
- virtual ~GGLAssembler();
+ explicit GGLAssembler(ARMAssemblerInterface* target);
+ virtual ~GGLAssembler();
uint32_t* base() const { return 0; } // XXX
uint32_t* pc() const { return 0; } // XXX
@@ -206,7 +206,7 @@
struct reg_t {
reg_t() : reg(-1), flags(0) {
}
- reg_t(int r, int f=0)
+ reg_t(int r, int f=0) // NOLINT, implicit
: reg(r), flags(f) {
}
void setTo(int r, int f=0) {
@@ -219,7 +219,7 @@
struct integer_t : public reg_t {
integer_t() : reg_t(), s(0) {
}
- integer_t(int r, int sz=32, int f=0)
+ integer_t(int r, int sz=32, int f=0) // NOLINT, implicit
: reg_t(r, f), s(sz) {
}
void setTo(int r, int sz=32, int f=0) {
@@ -251,7 +251,7 @@
struct component_t : public reg_t {
component_t() : reg_t(), h(0), l(0) {
}
- component_t(int r, int f=0)
+ component_t(int r, int f=0) // NOLINT, implicit
: reg_t(r, f), h(0), l(0) {
}
component_t(int r, int lo, int hi, int f=0)
@@ -288,6 +288,14 @@
private:
+ // GGLAssembler hides RegisterAllocator's and ARMAssemblerProxy's reset
+ // methods by providing a reset method with a different parameter set. The
+ // intent of GGLAssembler's reset method is to wrap the inherited reset
+ // methods, so make these methods private in order to prevent direct calls
+ // to these methods from clients.
+ using RegisterAllocator::reset;
+ using ARMAssemblerProxy::reset;
+
struct tex_coord_t {
reg_t s;
reg_t t;
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
new file mode 100644
index 0000000..d5e4cea
--- /dev/null
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -0,0 +1,1444 @@
+/* libs/pixelflinger/codeflinger/MIPS64Assembler.cpp
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+/* MIPS64 assembler and ARM->MIPS64 assembly translator
+**
+** The approach is utilize MIPSAssembler generator, using inherited MIPS64Assembler
+** that overrides just the specific MIPS64r6 instructions.
+** For now ArmToMips64Assembler is copied over from ArmToMipsAssembler class,
+** changing some MIPS64r6 related stuff.
+**
+*/
+
+#define LOG_TAG "MIPS64Assembler"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <private/pixelflinger/ggl_context.h>
+
+#include "MIPS64Assembler.h"
+#include "CodeCache.h"
+#include "mips64_disassem.h"
+
+#define NOT_IMPLEMENTED() LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__)
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark ArmToMips64Assembler...
+#endif
+
+ArmToMips64Assembler::ArmToMips64Assembler(const sp<Assembly>& assembly,
+ char *abuf, int linesz, int instr_count)
+ : ARMAssemblerInterface(),
+ mArmDisassemblyBuffer(abuf),
+ mArmLineLength(linesz),
+ mArmInstrCount(instr_count),
+ mInum(0),
+ mAssembly(assembly)
+{
+ mMips = new MIPS64Assembler(assembly, this);
+ mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *));
+ init_conditional_labels();
+}
+
+ArmToMips64Assembler::ArmToMips64Assembler(void* assembly)
+ : ARMAssemblerInterface(),
+ mArmDisassemblyBuffer(NULL),
+ mInum(0),
+ mAssembly(NULL)
+{
+ mMips = new MIPS64Assembler(assembly, this);
+ mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *));
+ init_conditional_labels();
+}
+
+ArmToMips64Assembler::~ArmToMips64Assembler()
+{
+ delete mMips;
+ free((void *) mArmPC);
+}
+
+uint32_t* ArmToMips64Assembler::pc() const
+{
+ return mMips->pc();
+}
+
+uint32_t* ArmToMips64Assembler::base() const
+{
+ return mMips->base();
+}
+
+void ArmToMips64Assembler::reset()
+{
+ cond.labelnum = 0;
+ mInum = 0;
+ mMips->reset();
+}
+
+int ArmToMips64Assembler::getCodegenArch()
+{
+ return CODEGEN_ARCH_MIPS64;
+}
+
+void ArmToMips64Assembler::comment(const char* string)
+{
+ mMips->comment(string);
+}
+
+void ArmToMips64Assembler::label(const char* theLabel)
+{
+ mMips->label(theLabel);
+}
+
+void ArmToMips64Assembler::disassemble(const char* name)
+{
+ mMips->disassemble(name);
+}
+
+void ArmToMips64Assembler::init_conditional_labels()
+{
+ int i;
+ for (i=0;i<99; ++i) {
+ sprintf(cond.label[i], "cond_%d", i);
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Prolog/Epilog & Generate...
+#endif
+
+void ArmToMips64Assembler::prolog()
+{
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+
+ mMips->DADDIU(R_sp, R_sp, -(5 * 8));
+ mMips->SD(R_s0, R_sp, 0);
+ mMips->SD(R_s1, R_sp, 8);
+ mMips->SD(R_s2, R_sp, 16);
+ mMips->SD(R_s3, R_sp, 24);
+ mMips->SD(R_s4, R_sp, 32);
+ mMips->MOVE(R_v0, R_a0); // move context * passed in a0 to v0 (arm r0)
+}
+
+void ArmToMips64Assembler::epilog(uint32_t touched)
+{
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+
+ mMips->LD(R_s0, R_sp, 0);
+ mMips->LD(R_s1, R_sp, 8);
+ mMips->LD(R_s2, R_sp, 16);
+ mMips->LD(R_s3, R_sp, 24);
+ mMips->LD(R_s4, R_sp, 32);
+ mMips->DADDIU(R_sp, R_sp, (5 * 8));
+ mMips->JR(R_ra);
+
+}
+
+int ArmToMips64Assembler::generate(const char* name)
+{
+ return mMips->generate(name);
+}
+
+void ArmToMips64Assembler::fix_branches()
+{
+ mMips->fix_branches();
+}
+
+uint32_t* ArmToMips64Assembler::pcForLabel(const char* label)
+{
+ return mMips->pcForLabel(label);
+}
+
+void ArmToMips64Assembler::set_condition(int mode, int R1, int R2) {
+ if (mode == 2) {
+ cond.type = SBIT_COND;
+ } else {
+ cond.type = CMP_COND;
+ }
+ cond.r1 = R1;
+ cond.r2 = R2;
+}
+
+//----------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Addressing modes & shifters...
+#endif
+
+
+// do not need this for MIPS, but it is in the Interface (virtual)
+int ArmToMips64Assembler::buildImmediate(
+ uint32_t immediate, uint32_t& rot, uint32_t& imm)
+{
+ // for MIPS, any 32-bit immediate is OK
+ rot = 0;
+ imm = immediate;
+ return 0;
+}
+
+// shifters...
+
+bool ArmToMips64Assembler::isValidImmediate(uint32_t immediate)
+{
+ // for MIPS, any 32-bit immediate is OK
+ return true;
+}
+
+uint32_t ArmToMips64Assembler::imm(uint32_t immediate)
+{
+ amode.value = immediate;
+ return AMODE_IMM;
+}
+
+uint32_t ArmToMips64Assembler::reg_imm(int Rm, int type, uint32_t shift)
+{
+ amode.reg = Rm;
+ amode.stype = type;
+ amode.value = shift;
+ return AMODE_REG_IMM;
+}
+
+uint32_t ArmToMips64Assembler::reg_rrx(int Rm)
+{
+ // reg_rrx mode is not used in the GLLAssember code at this time
+ return AMODE_UNSUPPORTED;
+}
+
+uint32_t ArmToMips64Assembler::reg_reg(int Rm, int type, int Rs)
+{
+ // reg_reg mode is not used in the GLLAssember code at this time
+ return AMODE_UNSUPPORTED;
+}
+
+
+// addressing modes...
+// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMips64Assembler::immed12_pre(int32_t immed12, int W)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+ "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+ immed12);
+ amode.value = immed12;
+ amode.writeback = W;
+ return AMODE_IMM_12_PRE;
+}
+
+uint32_t ArmToMips64Assembler::immed12_post(int32_t immed12)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+ "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+ immed12);
+
+ amode.value = immed12;
+ return AMODE_IMM_12_POST;
+}
+
+uint32_t ArmToMips64Assembler::reg_scale_pre(int Rm, int type,
+ uint32_t shift, int W)
+{
+ LOG_ALWAYS_FATAL_IF(W | type | shift, "reg_scale_pre adv modes not yet implemented");
+
+ amode.reg = Rm;
+ // amode.stype = type; // more advanced modes not used in GGLAssembler yet
+ // amode.value = shift;
+ // amode.writeback = W;
+ return AMODE_REG_SCALE_PRE;
+}
+
+uint32_t ArmToMips64Assembler::reg_scale_post(int Rm, int type, uint32_t shift)
+{
+ LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n");
+ return AMODE_UNSUPPORTED;
+}
+
+// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMips64Assembler::immed8_pre(int32_t immed8, int W)
+{
+ LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n");
+
+ LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+ "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+ immed8);
+ return AMODE_IMM_8_PRE;
+}
+
+uint32_t ArmToMips64Assembler::immed8_post(int32_t immed8)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+ "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+ immed8);
+ amode.value = immed8;
+ return AMODE_IMM_8_POST;
+}
+
+uint32_t ArmToMips64Assembler::reg_pre(int Rm, int W)
+{
+ LOG_ALWAYS_FATAL_IF(W, "reg_pre writeback not yet implemented");
+ amode.reg = Rm;
+ return AMODE_REG_PRE;
+}
+
+uint32_t ArmToMips64Assembler::reg_post(int Rm)
+{
+ LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n");
+ return AMODE_UNSUPPORTED;
+}
+
+
+
+// ----------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Data Processing...
+#endif
+
+
+static const char * const dpOpNames[] = {
+ "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC",
+ "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"
+};
+
+// check if the operand registers from a previous CMP or S-bit instruction
+// would be overwritten by this instruction. If so, move the value to a
+// safe register.
+// Note that we cannot tell at _this_ instruction time if a future (conditional)
+// instruction will _also_ use this value (a defect of the simple 1-pass, one-
+// instruction-at-a-time translation). Therefore we must be conservative and
+// save the value before it is overwritten. This costs an extra MOVE instr.
+
+void ArmToMips64Assembler::protectConditionalOperands(int Rd)
+{
+ if (Rd == cond.r1) {
+ mMips->MOVE(R_cmp, cond.r1);
+ cond.r1 = R_cmp;
+ }
+ if (cond.type == CMP_COND && Rd == cond.r2) {
+ mMips->MOVE(R_cmp2, cond.r2);
+ cond.r2 = R_cmp2;
+ }
+}
+
+
+// interprets the addressing mode, and generates the common code
+// used by the majority of data-processing ops. Many MIPS instructions
+// have a register-based form and a different immediate form. See
+// opAND below for an example. (this could be inlined)
+//
+// this works with the imm(), reg_imm() methods above, which are directly
+// called by the GLLAssembler.
+// note: _signed parameter defaults to false (un-signed)
+// note: tmpReg parameter defaults to 1, MIPS register AT
+int ArmToMips64Assembler::dataProcAdrModes(int op, int& source, bool _signed, int tmpReg)
+{
+ if (op < AMODE_REG) {
+ source = op;
+ return SRC_REG;
+ } else if (op == AMODE_IMM) {
+ if ((!_signed && amode.value > 0xffff)
+ || (_signed && ((int)amode.value < -32768 || (int)amode.value > 32767) )) {
+ mMips->LUI(tmpReg, (amode.value >> 16));
+ if (amode.value & 0x0000ffff) {
+ mMips->ORI(tmpReg, tmpReg, (amode.value & 0x0000ffff));
+ }
+ source = tmpReg;
+ return SRC_REG;
+ } else {
+ source = amode.value;
+ return SRC_IMM;
+ }
+ } else if (op == AMODE_REG_IMM) {
+ switch (amode.stype) {
+ case LSL: mMips->SLL(tmpReg, amode.reg, amode.value); break;
+ case LSR: mMips->SRL(tmpReg, amode.reg, amode.value); break;
+ case ASR: mMips->SRA(tmpReg, amode.reg, amode.value); break;
+ case ROR: mMips->ROTR(tmpReg, amode.reg, amode.value); break;
+ }
+ source = tmpReg;
+ return SRC_REG;
+ } else { // adr mode RRX is not used in GGL Assembler at this time
+ // we are screwed, this should be exception, assert-fail or something
+ LOG_ALWAYS_FATAL("adr mode reg_rrx not yet implemented\n");
+ return SRC_ERROR;
+ }
+}
+
+
+void ArmToMips64Assembler::dataProcessing(int opcode, int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+ int src; // src is modified by dataProcAdrModes() - passed as int&
+
+ if (cc != AL) {
+ protectConditionalOperands(Rd);
+ // the branch tests register(s) set by prev CMP or instr with 'S' bit set
+ // inverse the condition to jump past this conditional instruction
+ ArmToMips64Assembler::B(cc^1, cond.label[++cond.labelnum]);
+ } else {
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+ }
+
+ switch (opcode) {
+ case opAND:
+ if (dataProcAdrModes(Op2, src) == SRC_REG) {
+ mMips->AND(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->ANDI(Rd, Rn, src);
+ }
+ break;
+
+ case opADD:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->ADDU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->ADDIU(Rd, Rn, src);
+ }
+ break;
+
+ case opSUB:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->SUBU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->SUBIU(Rd, Rn, src);
+ }
+ break;
+
+ case opADD64:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->DADDU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->DADDIU(Rd, Rn, src);
+ }
+ break;
+
+ case opSUB64:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->DSUBU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->DSUBIU(Rd, Rn, src);
+ }
+ break;
+
+ case opEOR:
+ if (dataProcAdrModes(Op2, src) == SRC_REG) {
+ mMips->XOR(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->XORI(Rd, Rn, src);
+ }
+ break;
+
+ case opORR:
+ if (dataProcAdrModes(Op2, src) == SRC_REG) {
+ mMips->OR(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->ORI(Rd, Rn, src);
+ }
+ break;
+
+ case opBIC:
+ if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+ // if we are 16-bit imnmediate, load to AT reg
+ mMips->ORI(R_at, 0, src);
+ src = R_at;
+ }
+ mMips->NOT(R_at, src);
+ mMips->AND(Rd, Rn, R_at);
+ break;
+
+ case opRSB:
+ if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+ // if we are 16-bit imnmediate, load to AT reg
+ mMips->ORI(R_at, 0, src);
+ src = R_at;
+ }
+ mMips->SUBU(Rd, src, Rn); // subu with the parameters reversed
+ break;
+
+ case opMOV:
+ if (Op2 < AMODE_REG) { // op2 is reg # in this case
+ mMips->MOVE(Rd, Op2);
+ } else if (Op2 == AMODE_IMM) {
+ if (amode.value > 0xffff) {
+ mMips->LUI(Rd, (amode.value >> 16));
+ if (amode.value & 0x0000ffff) {
+ mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+ }
+ } else {
+ mMips->ORI(Rd, 0, amode.value);
+ }
+ } else if (Op2 == AMODE_REG_IMM) {
+ switch (amode.stype) {
+ case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+ case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+ case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+ case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break;
+ }
+ }
+ else {
+ // adr mode RRX is not used in GGL Assembler at this time
+ mMips->UNIMPL();
+ }
+ break;
+
+ case opMVN: // this is a 1's complement: NOT
+ if (Op2 < AMODE_REG) { // op2 is reg # in this case
+ mMips->NOR(Rd, Op2, 0); // NOT is NOR with 0
+ break;
+ } else if (Op2 == AMODE_IMM) {
+ if (amode.value > 0xffff) {
+ mMips->LUI(Rd, (amode.value >> 16));
+ if (amode.value & 0x0000ffff) {
+ mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+ }
+ } else {
+ mMips->ORI(Rd, 0, amode.value);
+ }
+ } else if (Op2 == AMODE_REG_IMM) {
+ switch (amode.stype) {
+ case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+ case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+ case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+ case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break;
+ }
+ }
+ else {
+ // adr mode RRX is not used in GGL Assembler at this time
+ mMips->UNIMPL();
+ }
+ mMips->NOR(Rd, Rd, 0); // NOT is NOR with 0
+ break;
+
+ case opCMP:
+ // Either operand of a CMP instr could get overwritten by a subsequent
+ // conditional instruction, which is ok, _UNLESS_ there is a _second_
+ // conditional instruction. Under MIPS, this requires doing the comparison
+ // again (SLT), and the original operands must be available. (and this
+ // pattern of multiple conditional instructions from same CMP _is_ used
+ // in GGL-Assembler)
+ //
+ // For now, if a conditional instr overwrites the operands, we will
+ // move them to dedicated temp regs. This is ugly, and inefficient,
+ // and should be optimized.
+ //
+ // WARNING: making an _Assumption_ that CMP operand regs will NOT be
+ // trashed by intervening NON-conditional instructions. In the general
+ // case this is legal, but it is NOT currently done in GGL-Assembler.
+
+ cond.type = CMP_COND;
+ cond.r1 = Rn;
+ if (dataProcAdrModes(Op2, src, false, R_cmp2) == SRC_REG) {
+ cond.r2 = src;
+ } else { // adr mode was SRC_IMM
+ mMips->ORI(R_cmp2, R_zero, src);
+ cond.r2 = R_cmp2;
+ }
+
+ break;
+
+
+ case opTST:
+ case opTEQ:
+ case opCMN:
+ case opADC:
+ case opSBC:
+ case opRSC:
+ mMips->UNIMPL(); // currently unused in GGL Assembler code
+ break;
+ }
+
+ if (cc != AL) {
+ mMips->label(cond.label[cond.labelnum]);
+ }
+ if (s && opcode != opCMP) {
+ cond.type = SBIT_COND;
+ cond.r1 = Rd;
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Multiply...
+#endif
+
+// multiply, accumulate
+void ArmToMips64Assembler::MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn) {
+
+ //ALOGW("MLA");
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+
+ mMips->MUL(R_at, Rm, Rs);
+ mMips->ADDU(Rd, R_at, Rn);
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = Rd;
+ }
+}
+
+void ArmToMips64Assembler::MUL(int cc, int s,
+ int Rd, int Rm, int Rs) {
+ mArmPC[mInum++] = pc();
+ mMips->MUL(Rd, Rm, Rs);
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = Rd;
+ }
+}
+
+void ArmToMips64Assembler::UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ mArmPC[mInum++] = pc();
+ mMips->MUH(RdHi, Rm, Rs);
+ mMips->MUL(RdLo, Rm, Rs);
+
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+ }
+}
+
+void ArmToMips64Assembler::UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ // *mPC++ = (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
+ // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+ }
+}
+
+void ArmToMips64Assembler::SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
+ // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n");
+ }
+}
+void ArmToMips64Assembler::SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
+ // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on SMUAL must be on 64-bit result\n");
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Branches...
+#endif
+
+// branches...
+
+void ArmToMips64Assembler::B(int cc, const char* label)
+{
+ mArmPC[mInum++] = pc();
+ if (cond.type == SBIT_COND) { cond.r2 = R_zero; }
+
+ switch(cc) {
+ case EQ: mMips->BEQ(cond.r1, cond.r2, label); break;
+ case NE: mMips->BNE(cond.r1, cond.r2, label); break;
+ case HS: mMips->BGEU(cond.r1, cond.r2, label); break;
+ case LO: mMips->BLTU(cond.r1, cond.r2, label); break;
+ case MI: mMips->BLT(cond.r1, cond.r2, label); break;
+ case PL: mMips->BGE(cond.r1, cond.r2, label); break;
+
+ case HI: mMips->BGTU(cond.r1, cond.r2, label); break;
+ case LS: mMips->BLEU(cond.r1, cond.r2, label); break;
+ case GE: mMips->BGE(cond.r1, cond.r2, label); break;
+ case LT: mMips->BLT(cond.r1, cond.r2, label); break;
+ case GT: mMips->BGT(cond.r1, cond.r2, label); break;
+ case LE: mMips->BLE(cond.r1, cond.r2, label); break;
+ case AL: mMips->B(label); break;
+ case NV: /* B Never - no instruction */ break;
+
+ case VS:
+ case VC:
+ default:
+ LOG_ALWAYS_FATAL("Unsupported cc: %02x\n", cc);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::BL(int cc, const char* label)
+{
+ LOG_ALWAYS_FATAL("branch-and-link not supported yet\n");
+ mArmPC[mInum++] = pc();
+}
+
+// no use for Branches with integer PC, but they're in the Interface class ....
+void ArmToMips64Assembler::B(int cc, uint32_t* to_pc)
+{
+ LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+ mArmPC[mInum++] = pc();
+}
+
+void ArmToMips64Assembler::BL(int cc, uint32_t* to_pc)
+{
+ LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+ mArmPC[mInum++] = pc();
+}
+
+void ArmToMips64Assembler::BX(int cc, int Rn)
+{
+ LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+ mArmPC[mInum++] = pc();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Data Transfer...
+#endif
+
+// data transfer...
+void ArmToMips64Assembler::LDR(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert LDR via Arm SP to LW via Mips SP
+ }
+ mMips->LW(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->DADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to STR thru Mips SP
+ }
+ mMips->LW(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->LW(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ mMips->LBU(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->DADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->LBU(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->LBU(Rd, R_at, 0);
+ break;
+ }
+
+}
+
+void ArmToMips64Assembler::STR(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to SW thru Mips SP
+ }
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ // If we will writeback, then update the index reg, then store.
+ // This correctly handles stack-push case.
+ mMips->DADDIU(Rn, Rn, amode.value);
+ mMips->SW(Rd, Rn, 0);
+ } else {
+ // No writeback so store offset by value
+ mMips->SW(Rd, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->SW(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value); // post index always writes back
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->SW(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::STRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ mMips->SB(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->DADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->SB(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->SB(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed8_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_8_PRE: // no support yet for writeback
+ mMips->LHU(Rd, Rn, amode.value);
+ break;
+ case AMODE_IMM_8_POST:
+ mMips->LHU(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_PRE:
+ // we only support simple base +/- index
+ if (amode.reg >= 0) {
+ mMips->DADDU(R_at, Rn, amode.reg);
+ } else {
+ mMips->DSUBU(R_at, Rn, abs(amode.reg));
+ }
+ mMips->LHU(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::STRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed8_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_8_PRE: // no support yet for writeback
+ mMips->SH(Rd, Rn, amode.value);
+ break;
+ case AMODE_IMM_8_POST:
+ mMips->SH(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_PRE:
+ // we only support simple base +/- index
+ if (amode.reg >= 0) {
+ mMips->DADDU(R_at, Rn, amode.reg);
+ } else {
+ mMips->DSUBU(R_at, Rn, abs(amode.reg));
+ }
+ mMips->SH(Rd, R_at, 0);
+ break;
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Block Data Transfer...
+#endif
+
+// block data transfer...
+void ArmToMips64Assembler::LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{ // ED FD EA FA IB IA DB DA
+ // const uint8_t P[8] = { 1, 0, 1, 0, 1, 0, 1, 0 };
+ // const uint8_t U[8] = { 1, 1, 0, 0, 1, 1, 0, 0 };
+ // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+ // (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{ // FA EA FD ED IB IA DB DA
+ // const uint8_t P[8] = { 0, 1, 0, 1, 1, 0, 1, 0 };
+ // const uint8_t U[8] = { 0, 0, 1, 1, 1, 1, 0, 0 };
+ // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+ // (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Special...
+#endif
+
+// special...
+void ArmToMips64Assembler::SWP(int cc, int Rn, int Rd, int Rm) {
+ // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+ // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SWI(int cc, uint32_t comment) {
+ // *mPC++ = (cc<<28) | (0xF<<24) | comment;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+
+#if 0
+#pragma mark -
+#pragma mark DSP instructions...
+#endif
+
+// DSP instructions...
+void ArmToMips64Assembler::PLD(int Rn, uint32_t offset) {
+ LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
+ "PLD only P=1, W=0");
+ // *mPC++ = 0xF550F000 | (Rn<<16) | offset;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::CLZ(int cc, int Rd, int Rm)
+{
+ mArmPC[mInum++] = pc();
+ mMips->CLZ(Rd, Rm);
+}
+
+void ArmToMips64Assembler::QADD(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QDADD(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QSUB(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QDSUB(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+// 16 x 16 signed multiply (like SMLAxx without the accumulate)
+void ArmToMips64Assembler::SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs)
+{
+ mArmPC[mInum++] = pc();
+
+ // the 16 bits may be in the top or bottom half of 32-bit source reg,
+ // as defined by the codes BB, BT, TB, TT (compressed param xy)
+ // where x corresponds to Rm and y to Rs
+
+ // select half-reg for Rm
+ if (xy & xyTB) {
+ // use top 16-bits
+ mMips->SRA(R_at, Rm, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ mMips->SEH(R_at, Rm);
+ }
+ // select half-reg for Rs
+ if (xy & xyBT) {
+ // use top 16-bits
+ mMips->SRA(R_at2, Rs, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ mMips->SEH(R_at2, Rs);
+ }
+ mMips->MUL(Rd, R_at, R_at2);
+}
+
+// signed 32b x 16b multiple, save top 32-bits of 48-bit result
+void ArmToMips64Assembler::SMULW(int cc, int y,
+ int Rd, int Rm, int Rs)
+{
+ mArmPC[mInum++] = pc();
+
+ // the selector yT or yB refers to reg Rs
+ if (y & yT) {
+ // zero the bottom 16-bits, with 2 shifts, it can affect result
+ mMips->SRL(R_at, Rs, 16);
+ mMips->SLL(R_at, R_at, 16);
+
+ } else {
+ // move low 16-bit half, to high half
+ mMips->SLL(R_at, Rs, 16);
+ }
+ mMips->MUH(Rd, Rm, R_at);
+}
+
+// 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn
+void ArmToMips64Assembler::SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn)
+{
+ mArmPC[mInum++] = pc();
+
+ // the 16 bits may be in the top or bottom half of 32-bit source reg,
+ // as defined by the codes BB, BT, TB, TT (compressed param xy)
+ // where x corresponds to Rm and y to Rs
+
+ // select half-reg for Rm
+ if (xy & xyTB) {
+ // use top 16-bits
+ mMips->SRA(R_at, Rm, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ mMips->SEH(R_at, Rm);
+ }
+ // select half-reg for Rs
+ if (xy & xyBT) {
+ // use top 16-bits
+ mMips->SRA(R_at2, Rs, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ mMips->SEH(R_at2, Rs);
+ }
+
+ mMips->MUL(R_at, R_at, R_at2);
+ mMips->ADDU(Rd, R_at, Rn);
+}
+
+void ArmToMips64Assembler::SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm)
+{
+ // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+// used by ARMv6 version of GGLAssembler::filter32
+void ArmToMips64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+{
+ mArmPC[mInum++] = pc();
+
+ //Rd[31:16] := ZeroExtend((Rm ROR (8 * sh))[23:16]),
+ //Rd[15:0] := ZeroExtend((Rm ROR (8 * sh))[7:0]). sh 0-3.
+
+ mMips->ROTR(R_at2, Rm, rotate * 8);
+ mMips->LUI(R_at, 0xFF);
+ mMips->ORI(R_at, R_at, 0xFF);
+ mMips->AND(Rd, R_at2, R_at);
+}
+
+void ArmToMips64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+{
+ /* Placeholder for UBFX */
+ mArmPC[mInum++] = pc();
+
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+// ----------------------------------------------------------------------------
+// Address Processing...
+// ----------------------------------------------------------------------------
+
+void ArmToMips64Assembler::ADDR_ADD(int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+// if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+// if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required
+ dataProcessing(opADD64, cc, s, Rd, Rn, Op2);
+}
+
+void ArmToMips64Assembler::ADDR_SUB(int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+// if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+// if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required
+ dataProcessing(opSUB64, cc, s, Rd, Rn, Op2);
+}
+
+void ArmToMips64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) {
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert LDR via Arm SP to LW via Mips SP
+ }
+ mMips->LD(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->DADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to STR thru Mips SP
+ }
+ mMips->LD(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->LD(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) {
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to SW thru Mips SP
+ }
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ // If we will writeback, then update the index reg, then store.
+ // This correctly handles stack-push case.
+ mMips->DADDIU(Rn, Rn, amode.value);
+ mMips->SD(Rd, Rn, 0);
+ } else {
+ // No writeback so store offset by value
+ mMips->SD(Rd, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->SD(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value); // post index always writes back
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->SD(Rd, R_at, 0);
+ break;
+ }
+}
+
+#if 0
+#pragma mark -
+#pragma mark MIPS Assembler...
+#endif
+
+
+//**************************************************************************
+//**************************************************************************
+//**************************************************************************
+
+
+/* MIPS64 assembler
+** this is a subset of mips64r6, targeted specifically at ARM instruction
+** replacement in the pixelflinger/codeflinger code.
+**
+** This class is extended from MIPSAssembler class and overrides only
+** MIPS64r6 specific stuff.
+*/
+
+MIPS64Assembler::MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent)
+ : mParent(parent),
+ MIPSAssembler::MIPSAssembler(assembly, NULL)
+{
+}
+
+MIPS64Assembler::MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent)
+ : mParent(parent),
+ MIPSAssembler::MIPSAssembler(assembly)
+{
+}
+
+MIPS64Assembler::~MIPS64Assembler()
+{
+}
+
+void MIPS64Assembler::reset()
+{
+ if (mAssembly != NULL) {
+ mBase = mPC = (uint32_t *)mAssembly->base();
+ } else {
+ mPC = mBase = base();
+ }
+ mBranchTargets.clear();
+ mLabels.clear();
+ mLabelsInverseMapping.clear();
+ mComments.clear();
+}
+
+
+void MIPS64Assembler::disassemble(const char* name)
+{
+ char di_buf[140];
+
+ bool arm_disasm_fmt = (mParent->mArmDisassemblyBuffer == NULL) ? false : true;
+
+ typedef char dstr[40];
+ dstr *lines = (dstr *)mParent->mArmDisassemblyBuffer;
+
+ if (mParent->mArmDisassemblyBuffer != NULL) {
+ for (int i=0; i<mParent->mArmInstrCount; ++i) {
+ string_detab(lines[i]);
+ }
+ }
+
+ // iArm is an index to Arm instructions 1...n for this assembly sequence
+ // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS
+ // instruction corresponding to that Arm instruction number
+
+ int iArm = 0;
+ size_t count = pc()-base();
+ uint32_t* mipsPC = base();
+
+ while (count--) {
+ ssize_t label = mLabelsInverseMapping.indexOfKey(mipsPC);
+ if (label >= 0) {
+ ALOGW("%s:\n", mLabelsInverseMapping.valueAt(label));
+ }
+ ssize_t comment = mComments.indexOfKey(mipsPC);
+ if (comment >= 0) {
+ ALOGW("; %s\n", mComments.valueAt(comment));
+ }
+ ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
+ string_detab(di_buf);
+ string_pad(di_buf, 30);
+ ALOGW("%08lx: %08x %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
+ mipsPC++;
+ }
+}
+
+void MIPS64Assembler::fix_branches()
+{
+ // fixup all the branches
+ size_t count = mBranchTargets.size();
+ while (count--) {
+ const branch_target_t& bt = mBranchTargets[count];
+ uint32_t* target_pc = mLabels.valueFor(bt.label);
+ LOG_ALWAYS_FATAL_IF(!target_pc,
+ "error resolving branch targets, target_pc is null");
+ int32_t offset = int32_t(target_pc - (bt.pc+1));
+ *bt.pc |= offset & 0x00FFFF;
+ }
+}
+
+void MIPS64Assembler::DADDU(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (daddu_fn<<FUNC_SHF)
+ | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF);
+}
+
+void MIPS64Assembler::DADDIU(int Rt, int Rs, int16_t imm)
+{
+ *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+void MIPS64Assembler::DSUBU(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (dsubu_fn<<FUNC_SHF) |
+ (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::DSUBIU(int Rt, int Rs, int16_t imm) // really addiu(d, s, -j)
+{
+ *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | ((-imm) & MSK_16);
+}
+
+void MIPS64Assembler::MUL(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (mul_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) |
+ (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::MUH(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (muh_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) |
+ (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::CLO(int Rd, int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (17<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF);
+}
+
+void MIPS64Assembler::CLZ(int Rd, int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (16<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF);
+}
+
+void MIPS64Assembler::LD(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (ld_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPS64Assembler::SD(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (sd_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPS64Assembler::LUI(int Rt, int16_t offset)
+{
+ *mPC++ = (aui_op<<OP_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+
+void MIPS64Assembler::JR(int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (Rs<<RS_SHF) | (jalr_fn << FUNC_SHF);
+ MIPS64Assembler::NOP();
+}
+
+}; // namespace android:
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.h b/libpixelflinger/codeflinger/MIPS64Assembler.h
new file mode 100644
index 0000000..b43e5da
--- /dev/null
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.h
@@ -0,0 +1,404 @@
+/* libs/pixelflinger/codeflinger/MIPS64Assembler.h
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_MIPS64ASSEMBLER_H
+#define ANDROID_MIPS64ASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "utils/KeyedVector.h"
+#include "utils/Vector.h"
+#include "tinyutils/smartpointer.h"
+
+#include "ARMAssemblerInterface.h"
+#include "MIPSAssembler.h"
+#include "CodeCache.h"
+
+namespace android {
+
+class MIPS64Assembler; // forward reference
+
+// this class mimics ARMAssembler interface
+// intent is to translate each ARM instruction to 1 or more MIPS instr
+// implementation calls MIPS64Assembler class to generate mips code
+class ArmToMips64Assembler : public ARMAssemblerInterface
+{
+public:
+ ArmToMips64Assembler(const sp<Assembly>& assembly,
+ char *abuf = 0, int linesz = 0, int instr_count = 0);
+ ArmToMips64Assembler(void* assembly);
+ virtual ~ArmToMips64Assembler();
+
+ uint32_t* base() const;
+ uint32_t* pc() const;
+ void disassemble(const char* name);
+
+ virtual void reset();
+
+ virtual int generate(const char* name);
+ virtual int getCodegenArch();
+
+ virtual void prolog();
+ virtual void epilog(uint32_t touched);
+ virtual void comment(const char* string);
+ // for testing purposes
+ void fix_branches();
+ void set_condition(int mode, int R1, int R2);
+
+
+ // -----------------------------------------------------------------------
+ // shifters and addressing modes
+ // -----------------------------------------------------------------------
+
+ // shifters...
+ virtual bool isValidImmediate(uint32_t immed);
+ virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+ virtual uint32_t imm(uint32_t immediate);
+ virtual uint32_t reg_imm(int Rm, int type, uint32_t shift);
+ virtual uint32_t reg_rrx(int Rm);
+ virtual uint32_t reg_reg(int Rm, int type, int Rs);
+
+ // addressing modes...
+ // LDR(B)/STR(B)/PLD
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed12_pre(int32_t immed12, int W=0);
+ virtual uint32_t immed12_post(int32_t immed12);
+ virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+ virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+
+ // LDRH/LDRSB/LDRSH/STRH
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed8_pre(int32_t immed8, int W=0);
+ virtual uint32_t immed8_post(int32_t immed8);
+ virtual uint32_t reg_pre(int Rm, int W=0);
+ virtual uint32_t reg_post(int Rm);
+
+
+
+
+ virtual void dataProcessing(int opcode, int cc, int s,
+ int Rd, int Rn,
+ uint32_t Op2);
+ virtual void MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void MUL(int cc, int s,
+ int Rd, int Rm, int Rs);
+ virtual void UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+
+ virtual void B(int cc, uint32_t* pc);
+ virtual void BL(int cc, uint32_t* pc);
+ virtual void BX(int cc, int Rn);
+ virtual void label(const char* theLabel);
+ virtual void B(int cc, const char* label);
+ virtual void BL(int cc, const char* label);
+
+ virtual uint32_t* pcForLabel(const char* label);
+
+ virtual void LDR (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STR (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STRB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRH (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRSB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRSH(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STRH (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+
+ virtual void LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+ virtual void STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+
+ virtual void SWP(int cc, int Rn, int Rd, int Rm);
+ virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+ virtual void SWI(int cc, uint32_t comment);
+
+ virtual void PLD(int Rn, uint32_t offset);
+ virtual void CLZ(int cc, int Rd, int Rm);
+ virtual void QADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs);
+ virtual void SMULW(int cc, int y,
+ int Rd, int Rm, int Rs);
+ virtual void SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm);
+ virtual void SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn);
+
+ // byte/half word extract...
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+
+ // bit manipulation...
+ virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
+
+ // Address loading/storing/manipulation
+ virtual void ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void ADDR_STR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void ADDR_ADD(int cc, int s, int Rd, int Rn, uint32_t Op2);
+ virtual void ADDR_SUB(int cc, int s, int Rd, int Rn, uint32_t Op2);
+
+ // this is some crap to share is MIPS64Assembler class for debug
+ char * mArmDisassemblyBuffer;
+ int mArmLineLength;
+ int mArmInstrCount;
+
+ int mInum; // current arm instuction number (0..n)
+ uint32_t** mArmPC; // array: PC for 1st mips instr of
+ // each translated ARM instr
+
+
+private:
+ ArmToMips64Assembler(const ArmToMips64Assembler& rhs);
+ ArmToMips64Assembler& operator = (const ArmToMips64Assembler& rhs);
+
+ void init_conditional_labels(void);
+
+ void protectConditionalOperands(int Rd);
+
+ // reg__tmp set to MIPS AT, reg 1
+ int dataProcAdrModes(int op, int& source, bool sign = false, int reg_tmp = 1);
+
+ sp<Assembly> mAssembly;
+ MIPS64Assembler* mMips;
+
+
+ enum misc_constants_t {
+ ARM_MAX_INSTUCTIONS = 512 // based on ASSEMBLY_SCRATCH_SIZE
+ };
+
+ enum {
+ SRC_REG = 0,
+ SRC_IMM,
+ SRC_ERROR = -1
+ };
+
+ enum addr_modes {
+ // start above the range of legal mips reg #'s (0-31)
+ AMODE_REG = 0x20,
+ AMODE_IMM, AMODE_REG_IMM, // for data processing
+ AMODE_IMM_12_PRE, AMODE_IMM_12_POST, // for load/store
+ AMODE_REG_SCALE_PRE, AMODE_IMM_8_PRE,
+ AMODE_IMM_8_POST, AMODE_REG_PRE,
+ AMODE_UNSUPPORTED
+ };
+
+ struct addr_mode_t { // address modes for current ARM instruction
+ int reg;
+ int stype;
+ uint32_t value;
+ bool writeback; // writeback the adr reg after modification
+ } amode;
+
+ enum cond_types {
+ CMP_COND = 1,
+ SBIT_COND
+ };
+
+ struct cond_mode_t { // conditional-execution info for current ARM instruction
+ cond_types type;
+ int r1;
+ int r2;
+ int labelnum;
+ char label[100][10];
+ } cond;
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+// This is the basic MIPS64 assembler, which just creates the opcodes in memory.
+// All the more complicated work is done in ArmToMips64Assember above.
+// Inherits MIPSAssembler class, and overrides only MIPS64r6 specific stuff
+
+class MIPS64Assembler : public MIPSAssembler
+{
+public:
+ MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent);
+ MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent);
+ virtual ~MIPS64Assembler();
+
+ virtual void reset();
+ virtual void disassemble(const char* name);
+
+ void fix_branches();
+
+ // ------------------------------------------------------------------------
+ // MIPS64AssemblerInterface...
+ // ------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Arithmetic...
+#endif
+
+ void DADDU(int Rd, int Rs, int Rt);
+ void DADDIU(int Rt, int Rs, int16_t imm);
+ void DSUBU(int Rd, int Rs, int Rt);
+ void DSUBIU(int Rt, int Rs, int16_t imm);
+ virtual void MUL(int Rd, int Rs, int Rt);
+ void MUH(int Rd, int Rs, int Rt);
+
+#if 0
+#pragma mark -
+#pragma mark Logical...
+#endif
+
+ virtual void CLO(int Rd, int Rs);
+ virtual void CLZ(int Rd, int Rs);
+
+#if 0
+#pragma mark -
+#pragma mark Load/store...
+#endif
+
+ void LD(int Rt, int Rbase, int16_t offset);
+ void SD(int Rt, int Rbase, int16_t offset);
+ virtual void LUI(int Rt, int16_t offset);
+
+#if 0
+#pragma mark -
+#pragma mark Branch...
+#endif
+
+ void JR(int Rs);
+
+
+protected:
+ ArmToMips64Assembler *mParent;
+
+ // opcode field of all instructions
+ enum opcode_field {
+ spec_op, regimm_op, j_op, jal_op, // 0x00 - 0x03
+ beq_op, bne_op, pop06_op, pop07_op, // 0x04 - 0x07
+ pop10_op, addiu_op, slti_op, sltiu_op, // 0x08 - 0x0b
+ andi_op, ori_op, xori_op, aui_op, // 0x0c - 0x0f
+ cop0_op, cop1_op, cop2_op, rsrv_opc_0, // 0x10 - 0x13
+ rsrv_opc_1, rsrv_opc_2, pop26_op, pop27_op, // 0x14 - 0x17
+ pop30_op, daddiu_op, rsrv_opc_3, rsrv_opc_4, // 0x18 - 0x1b
+ rsrv_opc_5, daui_op, msa_op, spec3_op, // 0x1c - 0x1f
+ lb_op, lh_op, rsrv_opc_6, lw_op, // 0x20 - 0x23
+ lbu_op, lhu_op, rsrv_opc_7, lwu_op, // 0x24 - 0x27
+ sb_op, sh_op, rsrv_opc_8, sw_op, // 0x28 - 0x2b
+ rsrv_opc_9, rsrv_opc_10, rsrv_opc_11, rsrv_opc_12, // 0x2c - 0x2f
+ rsrv_opc_13, lwc1_op, bc_op, rsrv_opc_14, // 0x2c - 0x2f
+ rsrv_opc_15, ldc1_op, pop66_op, ld_op, // 0x30 - 0x33
+ rsrv_opc_16, swc1_op, balc_op, pcrel_op, // 0x34 - 0x37
+ rsrv_opc_17, sdc1_op, pop76_op, sd_op // 0x38 - 0x3b
+ };
+
+
+ // func field for special opcode
+ enum func_spec_op {
+ sll_fn, rsrv_spec_0, srl_fn, sra_fn,
+ sllv_fn, lsa_fn, srlv_fn, srav_fn,
+ rsrv_spec_1, jalr_fn, rsrv_spec_2, rsrv_spec_3,
+ syscall_fn, break_fn, sdbbp_fn, sync_fn,
+ clz_fn, clo_fn, dclz_fn, dclo_fn,
+ dsllv_fn, dlsa_fn, dsrlv_fn, dsrav_fn,
+ sop30_fn, sop31_fn, sop32_fn, sop33_fn,
+ sop34_fn, sop35_fn, sop36_fn, sop37_fn,
+ add_fn, addu_fn, sub_fn, subu_fn,
+ and_fn, or_fn, xor_fn, nor_fn,
+ rsrv_spec_4, rsrv_spec_5, slt_fn, sltu_fn,
+ dadd_fn, daddu_fn, dsub_fn, dsubu_fn,
+ tge_fn, tgeu_fn, tlt_fn, tltu_fn,
+ teq_fn, seleqz_fn, tne_fn, selnez_fn,
+ dsll_fn, rsrv_spec_6, dsrl_fn, dsra_fn,
+ dsll32_fn, rsrv_spec_7, dsrl32_fn, dsra32_fn
+ };
+
+ // func field for spec3 opcode
+ enum func_spec3_op {
+ ext_fn, dextm_fn, dextu_fn, dext_fn,
+ ins_fn, dinsm_fn, dinsu_fn, dins_fn,
+ cachee_fn = 0x1b, sbe_fn, she_fn, sce_fn, swe_fn,
+ bshfl_fn, prefe_fn = 0x23, dbshfl_fn, cache_fn, sc_fn, scd_fn,
+ lbue_fn, lhue_fn, lbe_fn = 0x2c, lhe_fn, lle_fn, lwe_fn,
+ pref_fn = 0x35, ll_fn, lld_fn, rdhwr_fn = 0x3b
+ };
+
+ // sa field for spec3 opcodes, with BSHFL function
+ enum func_spec3_bshfl {
+ bitswap_fn,
+ wsbh_fn = 0x02,
+ dshd_fn = 0x05,
+ seb_fn = 0x10,
+ seh_fn = 0x18
+ };
+
+ // rt field of regimm opcodes.
+ enum regimm_fn {
+ bltz_fn, bgez_fn,
+ dahi_fn = 0x6,
+ nal_fn = 0x10, bal_fn, bltzall_fn, bgezall_fn,
+ sigrie_fn = 0x17,
+ dati_fn = 0x1e, synci_fn
+ };
+
+ enum muldiv_fn {
+ mul_fn = 0x02, muh_fn
+ };
+
+ enum mips_inst_shifts {
+ OP_SHF = 26,
+ JTARGET_SHF = 0,
+ RS_SHF = 21,
+ RT_SHF = 16,
+ RD_SHF = 11,
+ RE_SHF = 6,
+ SA_SHF = RE_SHF, // synonym
+ IMM_SHF = 0,
+ FUNC_SHF = 0,
+
+ // mask values
+ MSK_16 = 0xffff,
+
+
+ CACHEOP_SHF = 18,
+ CACHESEL_SHF = 16,
+ };
+};
+
+
+}; // namespace android
+
+#endif //ANDROID_MIPS64ASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index a88d2fe..865a568 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -47,22 +47,17 @@
** functions in ARMAssemblerInterface.cpp so they could be used as static initializers).
*/
-
#define LOG_TAG "MIPSAssembler"
#include <stdio.h>
#include <stdlib.h>
-#include <cutils/log.h>
+
#include <cutils/properties.h>
-
-#if defined(WITH_LIB_HARDWARE)
-#include <hardware_legacy/qemu_tracing.h>
-#endif
-
+#include <log/log.h>
#include <private/pixelflinger/ggl_context.h>
-#include "MIPSAssembler.h"
#include "CodeCache.h"
+#include "MIPSAssembler.h"
#include "mips_disassem.h"
// Choose MIPS arch variant following gcc flags
@@ -1256,6 +1251,12 @@
mDuration = ggl_system_time();
}
+MIPSAssembler::MIPSAssembler(void* assembly)
+ : mParent(NULL), mAssembly(NULL)
+{
+ mBase = mPC = (uint32_t *)assembly;
+}
+
MIPSAssembler::~MIPSAssembler()
{
}
@@ -1358,7 +1359,7 @@
::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
string_detab(di_buf);
string_pad(di_buf, 30);
- ALOGW("%08x: %08x %s", uint32_t(mipsPC), uint32_t(*mipsPC), di_buf);
+ ALOGW("%08x: %08x %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
mipsPC++;
}
}
@@ -1405,13 +1406,6 @@
const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
-#if defined(WITH_LIB_HARDWARE)
- if (__builtin_expect(mQemuTracing, 0)) {
- int err = qemu_add_mapping(int(base()), name);
- mQemuTracing = (err >= 0);
- }
-#endif
-
char value[PROPERTY_VALUE_MAX];
value[0] = '\0';
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h
index 430ab06..c1178b6 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.h
+++ b/libpixelflinger/codeflinger/MIPSAssembler.h
@@ -21,9 +21,9 @@
#include <stdint.h>
#include <sys/types.h>
-#include "tinyutils/KeyedVector.h"
-#include "tinyutils/Vector.h"
#include "tinyutils/smartpointer.h"
+#include "utils/KeyedVector.h"
+#include "utils/Vector.h"
#include "ARMAssemblerInterface.h"
#include "CodeCache.h"
@@ -242,22 +242,23 @@
{
public:
MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent);
+ MIPSAssembler(void* assembly);
virtual ~MIPSAssembler();
- uint32_t* base() const;
- uint32_t* pc() const;
- void reset();
+ virtual uint32_t* base() const;
+ virtual uint32_t* pc() const;
+ virtual void reset();
- void disassemble(const char* name);
+ virtual void disassemble(const char* name);
- void prolog();
- void epilog(uint32_t touched);
- int generate(const char* name);
- void comment(const char* string);
- void label(const char* string);
+ virtual void prolog();
+ virtual void epilog(uint32_t touched);
+ virtual int generate(const char* name);
+ virtual void comment(const char* string);
+ virtual void label(const char* string);
// valid only after generate() has been called
- uint32_t* pcForLabel(const char* label);
+ virtual uint32_t* pcForLabel(const char* label);
// ------------------------------------------------------------------------
@@ -399,9 +400,9 @@
-private:
- void string_detab(char *s);
- void string_pad(char *s, int padded_len);
+protected:
+ virtual void string_detab(char *s);
+ virtual void string_pad(char *s, int padded_len);
ArmToMipsAssembler *mParent;
sp<Assembly> mAssembly;
@@ -409,9 +410,6 @@
uint32_t* mPC;
uint32_t* mPrologPC;
int64_t mDuration;
-#if defined(WITH_LIB_HARDWARE)
- bool mQemuTracing;
-#endif
struct branch_target_t {
inline branch_target_t() : label(0), pc(0) { }
@@ -537,7 +535,11 @@
enum mips_regnames {
R_zero = 0,
R_at, R_v0, R_v1, R_a0, R_a1, R_a2, R_a3,
+#if __mips_isa_rev < 6
R_t0, R_t1, R_t2, R_t3, R_t4, R_t5, R_t6, R_t7,
+#else
+ R_a4, R_a5, R_a6, R_a7, R_t0, R_t1, R_t2, R_t3,
+#endif
R_s0, R_s1, R_s2, R_s3, R_s4, R_s5, R_s6, R_s7,
R_t8, R_t9, R_k0, R_k1, R_gp, R_sp, R_s8, R_ra,
R_lr = R_s8,
diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp
index b20219c..a55dfe3 100644
--- a/libpixelflinger/codeflinger/blending.cpp
+++ b/libpixelflinger/codeflinger/blending.cpp
@@ -2,30 +2,31 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
+#define LOG_TAG "pixelflinger-code"
+
#include <assert.h>
#include <stdint.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/types.h>
-#include <cutils/log.h>
+#include <log/log.h>
#include "GGLAssembler.h"
-
namespace android {
void GGLAssembler::build_fog(
diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c
index 39dd614..5cbd63d 100644
--- a/libpixelflinger/codeflinger/disassem.c
+++ b/libpixelflinger/codeflinger/disassem.c
@@ -279,14 +279,14 @@
"4.0", "5.0", "0.5", "10.0"
};
-#define insn_condition(x) arm32_insn_conditions[(x >> 28) & 0x0f]
-#define insn_blktrans(x) insn_block_transfers[(x >> 23) & 3]
-#define insn_stkblktrans(x) insn_stack_block_transfers[(3*((x >> 20)&1))^((x >> 23)&3)]
-#define op2_shift(x) op_shifts[(x >> 5) & 3]
-#define insn_fparnd(x) insn_fpa_rounding[(x >> 5) & 0x03]
-#define insn_fpaprec(x) insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1]
-#define insn_fpaprect(x) insn_fpa_precision[(((x >> 21) & 2)|(x >> 15)) & 1]
-#define insn_fpaimm(x) insn_fpaconstants[x & 0x07]
+#define insn_condition(x) arm32_insn_conditions[((x) >> 28) & 0x0f]
+#define insn_blktrans(x) insn_block_transfers[((x) >> 23) & 3]
+#define insn_stkblktrans(x) insn_stack_block_transfers[(3*(((x) >> 20)&1))^(((x) >> 23)&3)]
+#define op2_shift(x) op_shifts[((x) >> 5) & 3]
+#define insn_fparnd(x) insn_fpa_rounding[((x) >> 5) & 0x03]
+#define insn_fpaprec(x) insn_fpa_precision[((((x) >> 18) & 2)|((x) >> 7)) & 1]
+#define insn_fpaprect(x) insn_fpa_precision[((((x) >> 21) & 2)|((x) >> 15)) & 1]
+#define insn_fpaimm(x) insn_fpaconstants[(x) & 0x07]
/* Local prototypes */
static void disasm_register_shift(const disasm_interface_t *di, u_int insn);
diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp
index e5a1ae0..da21e1d 100644
--- a/libpixelflinger/codeflinger/load_store.cpp
+++ b/libpixelflinger/codeflinger/load_store.cpp
@@ -2,22 +2,26 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
+#define LOG_TAG "pixelflinger-code"
+
#include <assert.h>
#include <stdio.h>
-#include <cutils/log.h>
+
+#include <log/log.h>
+
#include "GGLAssembler.h"
namespace android {
@@ -25,7 +29,7 @@
// ----------------------------------------------------------------------------
void GGLAssembler::store(const pointer_t& addr, const pixel_t& s, uint32_t flags)
-{
+{
const int bits = addr.size;
const int inc = (flags & WRITE_BACK)?1:0;
switch (bits) {
@@ -59,8 +63,8 @@
}
void GGLAssembler::load(const pointer_t& addr, const pixel_t& s, uint32_t flags)
-{
- Scratch scratches(registerFile());
+{
+ Scratch scratches(registerFile());
int s0;
const int bits = addr.size;
@@ -72,7 +76,7 @@
break;
case 24:
// 24 bits formats are a little special and used only for RGB
- // R,G,B is packed as 0x00BBGGRR
+ // R,G,B is packed as 0x00BBGGRR
s0 = scratches.obtain();
if (s.reg != addr.reg) {
LDRB(AL, s.reg, addr.reg, immed12_pre(0)); // R
@@ -90,7 +94,7 @@
}
if (inc)
ADD(AL, 0, addr.reg, addr.reg, imm(3));
- break;
+ break;
case 16:
if (inc) LDRH(AL, s.reg, addr.reg, immed8_post(2));
else LDRH(AL, s.reg, addr.reg);
@@ -112,7 +116,7 @@
assert(maskLen<=8);
#endif
assert(h);
-
+
if (h != bits) {
const int mask = ((1<<maskLen)-1) << l;
if (isValidImmediate(mask)) {
@@ -126,12 +130,12 @@
}
s = d.reg;
}
-
+
if (l) {
MOV(AL, 0, d.reg, reg_imm(s, LSR, l)); // component = packed >> l;
s = d.reg;
}
-
+
if (s != d.reg) {
MOV(AL, 0, d.reg, s);
}
@@ -212,12 +216,12 @@
} while(dbits>0);
return;
}
-
+
dbits -= sbits;
do {
ORR(AL, 0, d, s, reg_imm(s, LSL, sbits));
// d |= d<<sbits;
- s = d;
+ s = d;
dbits -= sbits;
if (sbits*2 < dbits) {
sbits *= 2;
@@ -241,14 +245,14 @@
int dl = d.format.c[component].l;
int dbits = dh - dl;
int dithering = 0;
-
+
ALOGE_IF(sbits<dbits, "sbits (%d) < dbits (%d) in downshift", sbits, dbits);
if (sbits>dbits) {
// see if we need to dither
dithering = mDithering;
}
-
+
int ireg = d.reg;
if (!(d.flags & FIRST)) {
if (s.flags & CORRUPTIBLE) {
@@ -271,7 +275,7 @@
if (isValidImmediate(mask) || isValidImmediate(~mask)) {
build_and_immediate(ireg, s.reg, mask, 32);
sl = offset;
- s.reg = ireg;
+ s.reg = ireg;
sbits = dbits;
maskLoBits = maskHiBits = 0;
}
@@ -281,7 +285,7 @@
const uint32_t mask = ((1<<sbits)-1) << sl;
if (isValidImmediate(mask) || isValidImmediate(~mask)) {
build_and_immediate(ireg, s.reg, mask, 32);
- s.reg = ireg;
+ s.reg = ireg;
maskLoBits = maskHiBits = 0;
}
}
@@ -325,7 +329,7 @@
MOV(AL, 0, ireg, reg_imm(s.reg, LSR, sl));
sh -= sl;
sl = 0;
- s.reg = ireg;
+ s.reg = ireg;
}
// scaling (V-V>>dbits)
SUB(AL, 0, ireg, s.reg, reg_imm(s.reg, LSR, dbits));
@@ -333,7 +337,7 @@
if (shift>0) ADD(AL, 0, ireg, ireg, reg_imm(dither.reg, LSR, shift));
else if (shift<0) ADD(AL, 0, ireg, ireg, reg_imm(dither.reg, LSL,-shift));
else ADD(AL, 0, ireg, ireg, dither.reg);
- s.reg = ireg;
+ s.reg = ireg;
}
if ((maskLoBits|dithering) && (sh > dbits)) {
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
new file mode 100644
index 0000000..f28d726
--- /dev/null
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -0,0 +1,581 @@
+/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <android/log.h>
+
+#include "mips_opcode.h"
+
+static char *sprintf_buffer;
+static int sprintf_buf_len;
+
+typedef uint64_t db_addr_t;
+static void db_printf(const char* fmt, ...);
+
+static const char * const op_name[64] = {
+/* 0 */ "spec", "bcond", "j", "jal", "beq", "bne", "blez", "bgtz",
+/* 8 */ "pop10", "addiu", "slti", "sltiu", "andi", "ori", "xori", "aui",
+/*16 */ "cop0", "cop1", "cop2", "?", "?", "?", "pop26", "pop27",
+/*24 */ "pop30", "daddiu", "?", "?", "?", "daui", "msa", "op37",
+/*32 */ "lb", "lh", "?", "lw", "lbu", "lhu", "?", "lwu",
+/*40 */ "sb", "sh", "?", "sw", "?", "?", "?", "?",
+/*48 */ "?", "lwc1", "bc", "?", "?", "ldc1", "pop66", "ld",
+/*56 */ "?", "swc1", "balc", "pcrel", "?", "sdc1", "pop76", "sd"
+};
+
+static const char * const spec_name[64] = {
+/* 0 */ "sll", "?", "srl", "sra", "sllv", "?", "srlv", "srav",
+/* 8 */ "?", "jalr", "?", "?", "syscall", "break", "sdbpp", "sync",
+/*16 */ "clz", "clo", "dclz", "dclo", "dsllv", "dlsa", "dsrlv", "dsrav",
+/*24 */ "sop30", "sop31", "sop32", "sop33", "sop34", "sop35", "sop36", "sop37",
+/*32 */ "add", "addu", "sub", "subu", "and", "or", "xor", "nor",
+/*40 */ "?", "?", "slt", "sltu", "dadd", "daddu", "dsub", "dsubu",
+/*48 */ "tge", "tgeu", "tlt", "tltu", "teq", "seleqz", "tne", "selnez",
+/*56 */ "dsll", "?", "dsrl", "dsra", "dsll32", "?", "dsrl32", "dsra32"
+};
+
+static const char * const bcond_name[32] = {
+/* 0 */ "bltz", "bgez", "?", "?", "?", "?", "dahi", "?",
+/* 8 */ "?", "?", "?", "?", "?", "?", "?", "?",
+/*16 */ "nal", "bal", "?", "?", "?", "?", "?", "sigrie",
+/*24 */ "?", "?", "?", "?", "?", "?", "dati", "synci",
+};
+
+static const char * const cop1_name[64] = {
+/* 0 */ "fadd", "fsub", "fmpy", "fdiv", "fsqrt","fabs", "fmov", "fneg",
+/* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f",
+/*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17",
+/*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f",
+/*32 */ "fcvts","fcvtd","fcvte","fop23","fcvtw","fop25","fop26","fop27",
+/*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f",
+/*48 */ "fcmp.f","fcmp.un","fcmp.eq","fcmp.ueq","fcmp.olt","fcmp.ult",
+ "fcmp.ole","fcmp.ule",
+/*56 */ "fcmp.sf","fcmp.ngle","fcmp.seq","fcmp.ngl","fcmp.lt","fcmp.nge",
+ "fcmp.le","fcmp.ngt"
+};
+
+static const char * const fmt_name[16] = {
+ "s", "d", "e", "fmt3",
+ "w", "fmt5", "fmt6", "fmt7",
+ "fmt8", "fmt9", "fmta", "fmtb",
+ "fmtc", "fmtd", "fmte", "fmtf"
+};
+
+static char * const mips_reg_name[32] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static char * alt_arm_reg_name[32] = { // hacked names for comparison with ARM code
+ "zero", "at", "r0", "r1", "r2", "r3", "r4", "r5",
+ "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13",
+ "r14", "r15", "at2", "cmp", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static char ** reg_name = &mips_reg_name[0];
+
+static const char * const c0_opname[64] = {
+ "c0op00","tlbr", "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07",
+ "tlbp", "c0op11","c0op12","c0op13","c0op14","c0op15","c0op16","c0op17",
+ "rfe", "c0op21","c0op22","c0op23","c0op24","c0op25","c0op26","c0op27",
+ "eret", "c0op31","c0op32","c0op33","c0op34","c0op35","c0op36","c0op37",
+ "c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47",
+ "c0op50","c0op51","c0op52","c0op53","c0op54","c0op55","c0op56","c0op57",
+ "c0op60","c0op61","c0op62","c0op63","c0op64","c0op65","c0op66","c0op67",
+ "c0op70","c0op71","c0op72","c0op73","c0op74","c0op75","c0op77","c0op77",
+};
+
+static const char * const c0_reg[32] = {
+ "index", "random", "tlblo0", "tlblo1",
+ "context", "pagemask", "wired", "cp0r7",
+ "badvaddr", "count", "tlbhi", "compare",
+ "status", "cause", "epc", "prid",
+ "config", "lladdr", "watchlo", "watchhi",
+ "xcontext", "cp0r21", "cp0r22", "debug",
+ "depc", "perfcnt", "ecc", "cacheerr",
+ "taglo", "taghi", "errepc", "desave"
+};
+
+static void print_addr(db_addr_t);
+db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format);
+
+
+/*
+ * Disassemble instruction 'insn' nominally at 'loc'.
+ * 'loc' may in fact contain a breakpoint instruction.
+ */
+static db_addr_t
+db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
+{
+ bool bdslot = false;
+ InstFmt i;
+
+ i.word = insn;
+
+ switch (i.JType.op) {
+ case OP_SPECIAL:
+ if (i.word == 0) {
+ db_printf("nop");
+ break;
+ }
+ if (i.word == 0x0080) {
+ db_printf("NIY");
+ break;
+ }
+ if (i.word == 0x00c0) {
+ db_printf("NOT IMPL");
+ break;
+ }
+ /* Special cases --------------------------------------------------
+ * "addu" is a "move" only in 32-bit mode. What's the correct
+ * answer - never decode addu/daddu as "move"?
+ */
+ if ( (i.RType.func == OP_ADDU && i.RType.rt == 0) ||
+ (i.RType.func == OP_OR && i.RType.rt == 0) ) {
+ db_printf("move\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rs]);
+ break;
+ }
+
+ if (i.RType.func == OP_SRL && (i.RType.rs & 1) == 1) {
+ db_printf("rotr\t%s,%s,%d", reg_name[i.RType.rd],
+ reg_name[i.RType.rt], i.RType.shamt);
+ break;
+ }
+ if (i.RType.func == OP_SRLV && (i.RType.shamt & 1) == 1) {
+ db_printf("rotrv\t%s,%s,%s", reg_name[i.RType.rd],
+ reg_name[i.RType.rt], reg_name[i.RType.rs]);
+ break;
+ }
+
+ if (i.RType.func == OP_SOP30) {
+ if (i.RType.shamt == OP_MUL) {
+ db_printf("mul");
+ } else if (i.RType.shamt == OP_MUH) {
+ db_printf("muh");
+ }
+ db_printf("\t%s,%s,%s", reg_name[i.RType.rd],
+ reg_name[i.RType.rs], reg_name[i.RType.rt]);
+ break;
+ }
+ if (i.RType.func == OP_SOP31) {
+ if (i.RType.shamt == OP_MUL) {
+ db_printf("mulu");
+ } else if (i.RType.shamt == OP_MUH) {
+ db_printf("muhu");
+ }
+ db_printf("\t%s,%s,%s", reg_name[i.RType.rd],
+ reg_name[i.RType.rs], reg_name[i.RType.rt]);
+ break;
+ }
+
+ if (i.RType.func == OP_JALR && i.RType.rd == 0) {
+ db_printf("jr\t%s", reg_name[i.RType.rs]);
+ bdslot = true;
+ break;
+ }
+
+ db_printf("%s", spec_name[i.RType.func]);
+ switch (i.RType.func) {
+ case OP_SLL:
+ case OP_SRL:
+ case OP_SRA:
+ case OP_DSLL:
+
+ case OP_DSRL:
+ case OP_DSRA:
+ case OP_DSLL32:
+ case OP_DSRL32:
+ case OP_DSRA32:
+ db_printf("\t%s,%s,%d",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt],
+ i.RType.shamt);
+ break;
+
+ case OP_SLLV:
+ case OP_SRLV:
+ case OP_SRAV:
+ case OP_DSLLV:
+ case OP_DSRLV:
+ case OP_DSRAV:
+ db_printf("\t%s,%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs]);
+ break;
+
+ case OP_CLZ:
+ case OP_CLO:
+ case OP_DCLZ:
+ case OP_DCLO:
+ db_printf("\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rs]);
+ break;
+
+ case OP_JALR:
+ db_printf("\t");
+ if (i.RType.rd != 31) {
+ db_printf("%s,", reg_name[i.RType.rd]);
+ }
+ db_printf("%s", reg_name[i.RType.rs]);
+ bdslot = true;
+ break;
+
+ case OP_SYSCALL:
+ case OP_SYNC:
+ break;
+
+ case OP_BREAK:
+ db_printf("\t%d", (i.RType.rs << 5) | i.RType.rt);
+ break;
+
+ default:
+ db_printf("\t%s,%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rs],
+ reg_name[i.RType.rt]);
+ }
+ break;
+
+ case OP_SPECIAL3:
+ if (i.RType.func == OP_EXT)
+ db_printf("ext\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd+1);
+ else if (i.RType.func == OP_DEXT)
+ db_printf("dext\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd+1);
+ else if (i.RType.func == OP_DEXTM)
+ db_printf("dextm\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd+33);
+ else if (i.RType.func == OP_DEXTU)
+ db_printf("dextu\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt+32,
+ i.RType.rd+1);
+ else if (i.RType.func == OP_INS)
+ db_printf("ins\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd-i.RType.shamt+1);
+ else if (i.RType.func == OP_DINS)
+ db_printf("dins\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd-i.RType.shamt+1);
+ else if (i.RType.func == OP_DINSM)
+ db_printf("dinsm\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd-i.RType.shamt+33);
+ else if (i.RType.func == OP_DINSU)
+ db_printf("dinsu\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt+32,
+ i.RType.rd-i.RType.shamt+1);
+ else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH)
+ db_printf("wsbh\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEB)
+ db_printf("seb\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEH)
+ db_printf("seh\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else if (i.RType.func == OP_RDHWR)
+ db_printf("rdhwr\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else
+ db_printf("Unknown");
+ break;
+
+ case OP_BCOND:
+ db_printf("%s\t%s,", bcond_name[i.IType.rt],
+ reg_name[i.IType.rs]);
+ goto pr_displ;
+
+ case OP_BLEZ:
+ case OP_BGTZ:
+ db_printf("%s\t%s,", op_name[i.IType.op],
+ reg_name[i.IType.rs]);
+ goto pr_displ;
+
+ case OP_BEQ:
+ if (i.IType.rs == 0 && i.IType.rt == 0) {
+ db_printf("b\t");
+ goto pr_displ;
+ }
+ /* FALLTHROUGH */
+ case OP_BNE:
+ db_printf("%s\t%s,%s,", op_name[i.IType.op],
+ reg_name[i.IType.rs],
+ reg_name[i.IType.rt]);
+ pr_displ:
+ print_addr(loc + 4 + ((short)i.IType.imm << 2));
+ bdslot = true;
+ break;
+
+ case OP_COP0:
+ switch (i.RType.rs) {
+ case OP_BCx:
+ case OP_BCy:
+
+ db_printf("bc0%c\t",
+ "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+ goto pr_displ;
+
+ case OP_MT:
+ db_printf("mtc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ case OP_DMT:
+ db_printf("dmtc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ case OP_MF:
+ db_printf("mfc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ case OP_DMF:
+ db_printf("dmfc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ default:
+ db_printf("%s", c0_opname[i.FRType.func]);
+ }
+ break;
+
+ case OP_COP1:
+ switch (i.RType.rs) {
+ case OP_BCx:
+ case OP_BCy:
+ db_printf("bc1%c\t",
+ "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+ goto pr_displ;
+
+ case OP_MT:
+ db_printf("mtc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ case OP_MF:
+ db_printf("mfc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ case OP_CT:
+ db_printf("ctc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ case OP_CF:
+ db_printf("cfc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ default:
+ db_printf("%s.%s\tf%d,f%d,f%d",
+ cop1_name[i.FRType.func],
+ fmt_name[i.FRType.fmt],
+ i.FRType.fd, i.FRType.fs, i.FRType.ft);
+ }
+ break;
+
+ case OP_J:
+ case OP_JAL:
+ db_printf("%s\t", op_name[i.JType.op]);
+ print_addr((loc & 0xFFFFFFFFF0000000) | (i.JType.target << 2));
+ bdslot = true;
+ break;
+
+ case OP_LWC1:
+ case OP_SWC1:
+ db_printf("%s\tf%d,", op_name[i.IType.op],
+ i.IType.rt);
+ goto loadstore;
+
+ case OP_LB:
+ case OP_LH:
+ case OP_LW:
+ case OP_LD:
+ case OP_LBU:
+ case OP_LHU:
+ case OP_LWU:
+ case OP_SB:
+ case OP_SH:
+ case OP_SW:
+ case OP_SD:
+ db_printf("%s\t%s,", op_name[i.IType.op],
+ reg_name[i.IType.rt]);
+ loadstore:
+ db_printf("%d(%s)", (short)i.IType.imm,
+ reg_name[i.IType.rs]);
+ break;
+
+ case OP_ORI:
+ case OP_XORI:
+ if (i.IType.rs == 0) {
+ db_printf("li\t%s,0x%x",
+ reg_name[i.IType.rt],
+ i.IType.imm);
+ break;
+ }
+ /* FALLTHROUGH */
+ case OP_ANDI:
+ db_printf("%s\t%s,%s,0x%x", op_name[i.IType.op],
+ reg_name[i.IType.rt],
+ reg_name[i.IType.rs],
+ i.IType.imm);
+ break;
+
+ case OP_AUI:
+ if (i.IType.rs == 0) {
+ db_printf("lui\t%s,0x%x", reg_name[i.IType.rt],
+ i.IType.imm);
+ } else {
+ db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
+ reg_name[i.IType.rt], reg_name[i.IType.rs],
+ (short)i.IType.imm);
+ }
+ break;
+
+ case OP_ADDIU:
+ case OP_DADDIU:
+ if (i.IType.rs == 0) {
+ db_printf("li\t%s,%d",
+ reg_name[i.IType.rt],
+ (short)i.IType.imm);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
+ reg_name[i.IType.rt],
+ reg_name[i.IType.rs],
+ (short)i.IType.imm);
+ }
+ // db_printf("\n");
+ // if (bdslot) {
+ // db_printf(" bd: ");
+ // mips_disassem(loc+4);
+ // return (loc + 8);
+ // }
+ return (loc + 4);
+}
+
+static void
+print_addr(db_addr_t loc)
+{
+ db_printf("0x%08lx", loc);
+}
+
+static void db_printf(const char* fmt, ...)
+{
+ int cnt;
+ va_list argp;
+ va_start(argp, fmt);
+ if (sprintf_buffer) {
+ cnt = vsnprintf(sprintf_buffer, sprintf_buf_len, fmt, argp);
+ sprintf_buffer += cnt;
+ sprintf_buf_len -= cnt;
+ } else {
+ vprintf(fmt, argp);
+ }
+}
+
+/*
+ * Disassemble instruction at 'loc'.
+ * Return address of start of next instruction.
+ * Since this function is used by 'examine' and by 'step'
+ * "next instruction" does NOT mean the next instruction to
+ * be executed but the 'linear' next instruction.
+ */
+db_addr_t
+mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format)
+{
+ u_int32_t instr;
+
+ if (alt_dis_format) { // use ARM register names for disassembly
+ reg_name = &alt_arm_reg_name[0];
+ }
+
+ sprintf_buffer = di_buffer; // quick 'n' dirty printf() vs sprintf()
+ sprintf_buf_len = 39; // should be passed in
+
+ instr = *(u_int32_t *)loc;
+ return (db_disasm_insn(instr, loc, false));
+}
diff --git a/libpixelflinger/codeflinger/mips64_disassem.h b/libpixelflinger/codeflinger/mips64_disassem.h
new file mode 100644
index 0000000..c94f04f
--- /dev/null
+++ b/libpixelflinger/codeflinger/mips64_disassem.h
@@ -0,0 +1,56 @@
+/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93
+ */
+
+
+
+#ifndef ANDROID_MIPS_DISASSEM_H
+#define ANDROID_MIPS_DISASSEM_H
+
+#include <sys/types.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+/* Prototypes for callable functions */
+
+void mips_disassem(uint32_t *location, char *di_buffer, int alt_fmt);
+
+#if __cplusplus
+}
+#endif
+
+#endif /* !ANDROID_MIPS_DISASSEM_H */
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 4ab9bd3..3007b15 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -323,14 +323,14 @@
db_printf("ext\t%s,%s,%d,%d",
reg_name[i.RType.rt],
reg_name[i.RType.rs],
- i.RType.rd+1,
- i.RType.shamt);
+ i.RType.shamt,
+ i.RType.rd+1);
else if (i.RType.func == OP_INS)
db_printf("ins\t%s,%s,%d,%d",
reg_name[i.RType.rt],
reg_name[i.RType.rs],
- i.RType.rd+1,
- i.RType.shamt);
+ i.RType.shamt,
+ i.RType.rd-i.RType.shamt+1);
else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH)
db_printf("wsbh\t%s,%s",
reg_name[i.RType.rd],
diff --git a/libpixelflinger/codeflinger/mips_opcode.h b/libpixelflinger/codeflinger/mips_opcode.h
index 7ed5ef5..45bb19e 100644
--- a/libpixelflinger/codeflinger/mips_opcode.h
+++ b/libpixelflinger/codeflinger/mips_opcode.h
@@ -125,69 +125,118 @@
#define OP_BLEZ 006
#define OP_BGTZ 007
+#if __mips_isa_rev < 6
#define OP_ADDI 010
+#else
+#define OP_POP10 010
+#endif
+
#define OP_ADDIU 011
#define OP_SLTI 012
#define OP_SLTIU 013
#define OP_ANDI 014
#define OP_ORI 015
#define OP_XORI 016
+
+#if __mips_isa_rev < 6
#define OP_LUI 017
+#else
+#define OP_AUI 017
+#endif
#define OP_COP0 020
#define OP_COP1 021
#define OP_COP2 022
+
+#if __mips_isa_rev < 6
#define OP_COP3 023
-#define OP_BEQL 024 /* MIPS-II, for r4000 port */
-#define OP_BNEL 025 /* MIPS-II, for r4000 port */
-#define OP_BLEZL 026 /* MIPS-II, for r4000 port */
-#define OP_BGTZL 027 /* MIPS-II, for r4000 port */
+#define OP_BEQL 024
+#define OP_BNEL 025
+#define OP_BLEZL 026
+#define OP_BGTZL 027
+#define OP_DADDI 030
+#else
+#define OP_POP26 026
+#define OP_POP27 027
+#define OP_POP30 030
+#endif
-#define OP_DADDI 030 /* MIPS-II, for r4000 port */
-#define OP_DADDIU 031 /* MIPS-II, for r4000 port */
-#define OP_LDL 032 /* MIPS-II, for r4000 port */
-#define OP_LDR 033 /* MIPS-II, for r4000 port */
+#define OP_DADDIU 031
-#define OP_SPECIAL2 034 /* QED opcodes */
-#define OP_SPECIAL3 037 /* mips32r2 opcodes */
+#if __mips_isa_rev < 6
+#define OP_LDL 032
+#define OP_LDR 033
+#define OP_SPECIAL2 034
+#else
+#define OP_DAUI 035
+#endif
+
+#define OP_SPECIAL3 037
#define OP_LB 040
#define OP_LH 041
+
+#if __mips_isa_rev < 6
#define OP_LWL 042
+#endif
+
#define OP_LW 043
#define OP_LBU 044
#define OP_LHU 045
#define OP_LWR 046
#define OP_LHU 045
+
+#if __mips_isa_rev < 6
#define OP_LWR 046
-#define OP_LWU 047 /* MIPS-II, for r4000 port */
+#endif
+
+#define OP_LWU 047
#define OP_SB 050
#define OP_SH 051
-#define OP_SWL 052
-#define OP_SW 053
-#define OP_SDL 054 /* MIPS-II, for r4000 port */
-#define OP_SDR 055 /* MIPS-II, for r4000 port */
-#define OP_SWR 056
-#define OP_CACHE 057 /* MIPS-II, for r4000 port */
+#if __mips_isa_rev < 6
+#define OP_SWL 052
+#endif
+
+#define OP_SW 053
+
+#if __mips_isa_rev < 6
+#define OP_SDL 054
+#define OP_SDR 055
+#define OP_SWR 056
+#define OP_CACHE 057
#define OP_LL 060
-#define OP_LWC0 OP_LL /* backwards source compatibility */
+#define OP_LWC0 OP_LL
#define OP_LWC1 061
#define OP_LWC2 062
#define OP_LWC3 063
-#define OP_LLD 064 /* MIPS-II, for r4000 port */
-#define OP_LDC1 065
-#define OP_LD 067 /* MIPS-II, for r4000 port */
+#define OP_LLD 064
+#else
+#define OP_LWC1 061
+#define OP_BC 062
+#endif
+#define OP_LDC1 065
+#define OP_LD 067
+
+#if __mips_isa_rev < 6
#define OP_SC 070
-#define OP_SWC0 OP_SC /* backwards source compatibility */
+#define OP_SWC0 OP_SC
+#endif
+
#define OP_SWC1 071
+
+#if __mips_isa_rev < 6
#define OP_SWC2 072
#define OP_SWC3 073
-#define OP_SCD 074 /* MIPS-II, for r4000 port */
+#define OP_SCD 074
+#else
+#define OP_BALC 072
+#endif
+
#define OP_SDC1 075
-#define OP_SD 077 /* MIPS-II, for r4000 port */
+#define OP_SD 077
/*
* Values for the 'func' field when 'op' == OP_SPECIAL.
@@ -199,28 +248,50 @@
#define OP_SRLV 006
#define OP_SRAV 007
+#if __mips_isa_rev < 6
#define OP_JR 010
+#endif
+
#define OP_JALR 011
#define OP_SYSCALL 014
#define OP_BREAK 015
-#define OP_SYNC 017 /* MIPS-II, for r4000 port */
+#define OP_SYNC 017
+#if __mips_isa_rev < 6
#define OP_MFHI 020
#define OP_MTHI 021
#define OP_MFLO 022
#define OP_MTLO 023
-#define OP_DSLLV 024 /* MIPS-II, for r4000 port */
-#define OP_DSRLV 026 /* MIPS-II, for r4000 port */
-#define OP_DSRAV 027 /* MIPS-II, for r4000 port */
+#else
+#define OP_CLZ 020
+#define OP_CLO 021
+#define OP_DCLZ 022
+#define OP_DCLO 023
+#endif
+#define OP_DSLLV 024
+#define OP_DSRLV 026
+#define OP_DSRAV 027
+
+#if __mips_isa_rev < 6
#define OP_MULT 030
#define OP_MULTU 031
#define OP_DIV 032
#define OP_DIVU 033
-#define OP_DMULT 034 /* MIPS-II, for r4000 port */
-#define OP_DMULTU 035 /* MIPS-II, for r4000 port */
-#define OP_DDIV 036 /* MIPS-II, for r4000 port */
-#define OP_DDIVU 037 /* MIPS-II, for r4000 port */
+#define OP_DMULT 034
+#define OP_DMULTU 035
+#define OP_DDIV 036
+#define OP_DDIVU 037
+#else
+#define OP_SOP30 030
+#define OP_SOP31 031
+#define OP_SOP32 032
+#define OP_SOP33 033
+#define OP_SOP34 034
+#define OP_SOP35 035
+#define OP_SOP36 036
+#define OP_SOP37 037
+#endif
#define OP_ADD 040
#define OP_ADDU 041
@@ -233,73 +304,96 @@
#define OP_SLT 052
#define OP_SLTU 053
-#define OP_DADD 054 /* MIPS-II, for r4000 port */
-#define OP_DADDU 055 /* MIPS-II, for r4000 port */
-#define OP_DSUB 056 /* MIPS-II, for r4000 port */
-#define OP_DSUBU 057 /* MIPS-II, for r4000 port */
+#define OP_DADD 054
+#define OP_DADDU 055
+#define OP_DSUB 056
+#define OP_DSUBU 057
-#define OP_TGE 060 /* MIPS-II, for r4000 port */
-#define OP_TGEU 061 /* MIPS-II, for r4000 port */
-#define OP_TLT 062 /* MIPS-II, for r4000 port */
-#define OP_TLTU 063 /* MIPS-II, for r4000 port */
-#define OP_TEQ 064 /* MIPS-II, for r4000 port */
-#define OP_TNE 066 /* MIPS-II, for r4000 port */
+#define OP_TGE 060
+#define OP_TGEU 061
+#define OP_TLT 062
+#define OP_TLTU 063
+#define OP_TEQ 064
+#define OP_TNE 066
-#define OP_DSLL 070 /* MIPS-II, for r4000 port */
-#define OP_DSRL 072 /* MIPS-II, for r4000 port */
-#define OP_DSRA 073 /* MIPS-II, for r4000 port */
-#define OP_DSLL32 074 /* MIPS-II, for r4000 port */
-#define OP_DSRL32 076 /* MIPS-II, for r4000 port */
-#define OP_DSRA32 077 /* MIPS-II, for r4000 port */
+#define OP_DSLL 070
+#define OP_DSRL 072
+#define OP_DSRA 073
+#define OP_DSLL32 074
+#define OP_DSRL32 076
+#define OP_DSRA32 077
+#if __mips_isa_rev < 6
/*
* Values for the 'func' field when 'op' == OP_SPECIAL2.
+ * OP_SPECIAL2 opcodes are removed in mips32r6
*/
#define OP_MAD 000 /* QED */
#define OP_MADU 001 /* QED */
#define OP_MUL 002 /* QED */
+#endif
/*
* Values for the 'func' field when 'op' == OP_SPECIAL3.
*/
#define OP_EXT 000
+#define OP_DEXTM 001
+#define OP_DEXTU 002
+#define OP_DEXT 003
#define OP_INS 004
+#define OP_DINSM 005
+#define OP_DINSU 006
+#define OP_DINS 007
#define OP_BSHFL 040
+#define OP_RDHWR 073
/*
* Values for the 'shamt' field when OP_SPECIAL3 && func OP_BSHFL.
*/
+
#define OP_WSBH 002
#define OP_SEB 020
#define OP_SEH 030
+#if __mips_isa_rev == 6
+/*
+ * Values for the 'shamt' field when OP_SOP30.
+ */
+#define OP_MUL 002
+#define OP_MUH 003
+#endif
+
/*
* Values for the 'func' field when 'op' == OP_BCOND.
*/
#define OP_BLTZ 000
#define OP_BGEZ 001
-#define OP_BLTZL 002 /* MIPS-II, for r4000 port */
-#define OP_BGEZL 003 /* MIPS-II, for r4000 port */
-#define OP_TGEI 010 /* MIPS-II, for r4000 port */
-#define OP_TGEIU 011 /* MIPS-II, for r4000 port */
-#define OP_TLTI 012 /* MIPS-II, for r4000 port */
-#define OP_TLTIU 013 /* MIPS-II, for r4000 port */
-#define OP_TEQI 014 /* MIPS-II, for r4000 port */
-#define OP_TNEI 016 /* MIPS-II, for r4000 port */
-
-#define OP_BLTZAL 020 /* MIPS-II, for r4000 port */
+#if __mips_isa_rev < 6
+#define OP_BLTZL 002
+#define OP_BGEZL 003
+#define OP_TGEI 010
+#define OP_TGEIU 011
+#define OP_TLTI 012
+#define OP_TLTIU 013
+#define OP_TEQI 014
+#define OP_TNEI 016
+#define OP_BLTZAL 020
#define OP_BGEZAL 021
#define OP_BLTZALL 022
#define OP_BGEZALL 023
+#else
+#define OP_NAL 020
+#define OP_BAL 021
+#endif
/*
* Values for the 'rs' field when 'op' == OP_COPz.
*/
#define OP_MF 000
-#define OP_DMF 001 /* MIPS-II, for r4000 port */
+#define OP_DMF 001
#define OP_MT 004
-#define OP_DMT 005 /* MIPS-II, for r4000 port */
+#define OP_DMT 005
#define OP_BCx 010
#define OP_BCy 014
#define OP_CF 002
@@ -311,6 +405,6 @@
#define COPz_BC_TF_MASK 0x01
#define COPz_BC_TRUE 0x01
#define COPz_BC_FALSE 0x00
-#define COPz_BCL_TF_MASK 0x02 /* MIPS-II, for r4000 port */
-#define COPz_BCL_TRUE 0x02 /* MIPS-II, for r4000 port */
-#define COPz_BCL_FALSE 0x00 /* MIPS-II, for r4000 port */
+#define COPz_BCL_TF_MASK 0x02
+#define COPz_BCL_TRUE 0x02
+#define COPz_BCL_FALSE 0x00
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
index 29a3742..4c357af 100644
--- a/libpixelflinger/codeflinger/texturing.cpp
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -2,26 +2,28 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
+#define LOG_TAG "pixelflinger-code"
+
#include <assert.h>
#include <stdint.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/types.h>
-#include <cutils/log.h>
+#include <log/log.h>
#include "GGLAssembler.h"
diff --git a/libpixelflinger/codeflinger/tinyutils/Errors.h b/libpixelflinger/codeflinger/tinyutils/Errors.h
deleted file mode 100644
index 47ae9d7..0000000
--- a/libpixelflinger/codeflinger/tinyutils/Errors.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_PIXELFLINGER_ERRORS_H
-#define ANDROID_PIXELFLINGER_ERRORS_H
-
-#include <sys/types.h>
-#include <errno.h>
-
-namespace android {
-namespace tinyutils {
-
-// use this type to return error codes
-typedef int32_t status_t;
-
-/*
- * Error codes.
- * All error codes are negative values.
- */
-
-enum {
- NO_ERROR = 0, // No errors.
- NO_MEMORY = -ENOMEM,
- BAD_VALUE = -EINVAL,
- BAD_INDEX = -EOVERFLOW,
- NAME_NOT_FOUND = -ENOENT,
-};
-
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_ERRORS_H
diff --git a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
deleted file mode 100644
index 9d8668b..0000000
--- a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_PIXELFLINGER_KEYED_VECTOR_H
-#define ANDROID_PIXELFLINGER_KEYED_VECTOR_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "Errors.h"
-#include "SortedVector.h"
-#include "TypeHelpers.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-template <typename KEY, typename VALUE>
-class KeyedVector
-{
-public:
- typedef KEY key_type;
- typedef VALUE value_type;
-
- inline KeyedVector();
-
- /*
- * empty the vector
- */
-
- inline void clear() { mVector.clear(); }
-
- /*!
- * vector stats
- */
-
- //! returns number of items in the vector
- inline size_t size() const { return mVector.size(); }
- //! returns wether or not the vector is empty
- inline bool isEmpty() const { return mVector.isEmpty(); }
- //! returns how many items can be stored without reallocating the backing store
- inline size_t capacity() const { return mVector.capacity(); }
- //! setst the capacity. capacity can never be reduced less than size()
- inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); }
-
- /*!
- * accessors
- */
- const VALUE& valueFor(const KEY& key) const;
- const VALUE& valueAt(size_t index) const;
- const KEY& keyAt(size_t index) const;
- ssize_t indexOfKey(const KEY& key) const;
-
- /*!
- * modifing the array
- */
-
- VALUE& editValueFor(const KEY& key);
- VALUE& editValueAt(size_t index);
-
- /*!
- * add/insert/replace items
- */
-
- ssize_t add(const KEY& key, const VALUE& item);
- ssize_t replaceValueFor(const KEY& key, const VALUE& item);
- ssize_t replaceValueAt(size_t index, const VALUE& item);
-
- /*!
- * remove items
- */
-
- ssize_t removeItem(const KEY& key);
- ssize_t removeItemsAt(size_t index, size_t count = 1);
-
-private:
- SortedVector< key_value_pair_t<KEY, VALUE> > mVector;
-};
-
-// ---------------------------------------------------------------------------
-
-/**
- * Variation of KeyedVector that holds a default value to return when
- * valueFor() is called with a key that doesn't exist.
- */
-template <typename KEY, typename VALUE>
-class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
-{
-public:
- inline DefaultKeyedVector(const VALUE& defValue = VALUE());
- const VALUE& valueFor(const KEY& key) const;
-
-private:
- VALUE mDefault;
-};
-
-// ---------------------------------------------------------------------------
-
-template<typename KEY, typename VALUE> inline
-KeyedVector<KEY,VALUE>::KeyedVector()
-{
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
- return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
- ssize_t i = indexOfKey(key);
- assert(i>=0);
- return mVector.itemAt(i).value;
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
- return mVector.itemAt(index).value;
-}
-
-template<typename KEY, typename VALUE> inline
-const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
- return mVector.itemAt(index).key;
-}
-
-template<typename KEY, typename VALUE> inline
-VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
- ssize_t i = indexOfKey(key);
- assert(i>=0);
- return mVector.editItemAt(i).value;
-}
-
-template<typename KEY, typename VALUE> inline
-VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
- return mVector.editItemAt(index).value;
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
- return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
- key_value_pair_t<KEY,VALUE> pair(key, value);
- mVector.remove(pair);
- return mVector.add(pair);
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
- if (index<size()) {
- mVector.editValueAt(index).value = item;
- return index;
- }
- return BAD_INDEX;
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
- return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
- return mVector.removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<typename KEY, typename VALUE> inline
-DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
- : mDefault(defValue)
-{
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
- ssize_t i = indexOfKey(key);
- return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
-}
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_KEYED_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp
deleted file mode 100644
index ef453fa..0000000
--- a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <cutils/atomic.h>
-
-#include "SharedBuffer.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-SharedBuffer* SharedBuffer::alloc(size_t size)
-{
- SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
- if (sb) {
- sb->mRefs = 1;
- sb->mSize = size;
- }
- return sb;
-}
-
-
-ssize_t SharedBuffer::dealloc(const SharedBuffer* released)
-{
- if (released->mRefs != 0) return -1; // XXX: invalid operation
- free(const_cast<SharedBuffer*>(released));
- return 0;
-}
-
-SharedBuffer* SharedBuffer::edit() const
-{
- if (onlyOwner()) {
- return const_cast<SharedBuffer*>(this);
- }
- SharedBuffer* sb = alloc(mSize);
- if (sb) {
- memcpy(sb->data(), data(), size());
- release();
- }
- return sb;
-}
-
-SharedBuffer* SharedBuffer::editResize(size_t newSize) const
-{
- if (onlyOwner()) {
- SharedBuffer* buf = const_cast<SharedBuffer*>(this);
- if (buf->mSize == newSize) return buf;
- buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
- if (buf != NULL) {
- buf->mSize = newSize;
- return buf;
- }
- }
- SharedBuffer* sb = alloc(newSize);
- if (sb) {
- const size_t mySize = mSize;
- memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
- release();
- }
- return sb;
-}
-
-SharedBuffer* SharedBuffer::attemptEdit() const
-{
- if (onlyOwner()) {
- return const_cast<SharedBuffer*>(this);
- }
- return 0;
-}
-
-SharedBuffer* SharedBuffer::reset(size_t new_size) const
-{
- // cheap-o-reset.
- SharedBuffer* sb = alloc(new_size);
- if (sb) {
- release();
- }
- return sb;
-}
-
-void SharedBuffer::acquire() const {
- android_atomic_inc(&mRefs);
-}
-
-int32_t SharedBuffer::release(uint32_t flags) const
-{
- int32_t prev = 1;
- if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) {
- mRefs = 0;
- if ((flags & eKeepStorage) == 0) {
- free(const_cast<SharedBuffer*>(this));
- }
- }
- return prev;
-}
-
-} // namespace tinyutils
-} // namespace android
diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h
deleted file mode 100644
index d69b417..0000000
--- a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_PIXELFLINGER_SHARED_BUFFER_H
-#define ANDROID_PIXELFLINGER_SHARED_BUFFER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-class SharedBuffer
-{
-public:
-
- /* flags to use with release() */
- enum {
- eKeepStorage = 0x00000001
- };
-
- /*! allocate a buffer of size 'size' and acquire() it.
- * call release() to free it.
- */
- static SharedBuffer* alloc(size_t size);
-
- /*! free the memory associated with the SharedBuffer.
- * Fails if there are any users associated with this SharedBuffer.
- * In other words, the buffer must have been release by all its
- * users.
- */
- static ssize_t dealloc(const SharedBuffer* released);
-
- //! get the SharedBuffer from the data pointer
- static inline const SharedBuffer* sharedBuffer(const void* data);
-
- //! access the data for read
- inline const void* data() const;
-
- //! access the data for read/write
- inline void* data();
-
- //! get size of the buffer
- inline size_t size() const;
-
- //! get back a SharedBuffer object from its data
- static inline SharedBuffer* bufferFromData(void* data);
-
- //! get back a SharedBuffer object from its data
- static inline const SharedBuffer* bufferFromData(const void* data);
-
- //! get the size of a SharedBuffer object from its data
- static inline size_t sizeFromData(const void* data);
-
- //! edit the buffer (get a writtable, or non-const, version of it)
- SharedBuffer* edit() const;
-
- //! edit the buffer, resizing if needed
- SharedBuffer* editResize(size_t size) const;
-
- //! like edit() but fails if a copy is required
- SharedBuffer* attemptEdit() const;
-
- //! resize and edit the buffer, loose it's content.
- SharedBuffer* reset(size_t size) const;
-
- //! acquire/release a reference on this buffer
- void acquire() const;
-
- /*! release a reference on this buffer, with the option of not
- * freeing the memory associated with it if it was the last reference
- * returns the previous reference count
- */
- int32_t release(uint32_t flags = 0) const;
-
- //! returns wether or not we're the only owner
- inline bool onlyOwner() const;
-
-
-private:
- inline SharedBuffer() { }
- inline ~SharedBuffer() { }
- inline SharedBuffer(const SharedBuffer&);
-
- // 16 bytes. must be sized to preserve correct alingment.
- mutable int32_t mRefs;
- size_t mSize;
- uint32_t mReserved[2];
-};
-
-// ---------------------------------------------------------------------------
-
-const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) {
- return data ? reinterpret_cast<const SharedBuffer *>(data)-1 : 0;
-}
-
-const void* SharedBuffer::data() const {
- return this + 1;
-}
-
-void* SharedBuffer::data() {
- return this + 1;
-}
-
-size_t SharedBuffer::size() const {
- return mSize;
-}
-
-SharedBuffer* SharedBuffer::bufferFromData(void* data)
-{
- return ((SharedBuffer*)data)-1;
-}
-
-const SharedBuffer* SharedBuffer::bufferFromData(const void* data)
-{
- return ((const SharedBuffer*)data)-1;
-}
-
-size_t SharedBuffer::sizeFromData(const void* data)
-{
- return (((const SharedBuffer*)data)-1)->mSize;
-}
-
-bool SharedBuffer::onlyOwner() const {
- return (mRefs == 1);
-}
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_SHARED_BUFFER_H
diff --git a/libpixelflinger/codeflinger/tinyutils/SortedVector.h b/libpixelflinger/codeflinger/tinyutils/SortedVector.h
deleted file mode 100644
index a2b7005..0000000
--- a/libpixelflinger/codeflinger/tinyutils/SortedVector.h
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_PIXELFLINGER_SORTED_VECTOR_H
-#define ANDROID_PIXELFLINGER_SORTED_VECTOR_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "Vector.h"
-#include "VectorImpl.h"
-#include "TypeHelpers.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-template <class TYPE>
-class SortedVector : private SortedVectorImpl
-{
-public:
- typedef TYPE value_type;
-
- /*!
- * Constructors and destructors
- */
-
- SortedVector();
- SortedVector(const SortedVector<TYPE>& rhs);
- virtual ~SortedVector();
-
- /*! copy operator */
- const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
- SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
-
- /*
- * empty the vector
- */
-
- inline void clear() { VectorImpl::clear(); }
-
- /*!
- * vector stats
- */
-
- //! returns number of items in the vector
- inline size_t size() const { return VectorImpl::size(); }
- //! returns wether or not the vector is empty
- inline bool isEmpty() const { return VectorImpl::isEmpty(); }
- //! returns how many items can be stored without reallocating the backing store
- inline size_t capacity() const { return VectorImpl::capacity(); }
- //! setst the capacity. capacity can never be reduced less than size()
- inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
-
- /*!
- * C-style array access
- */
-
- //! read-only C-style access
- inline const TYPE* array() const;
-
- //! read-write C-style access. BE VERY CAREFUL when modifying the array
- //! you ust keep it sorted! You usually don't use this function.
- TYPE* editArray();
-
- //! finds the index of an item
- ssize_t indexOf(const TYPE& item) const;
-
- //! finds where this item should be inserted
- size_t orderOf(const TYPE& item) const;
-
-
- /*!
- * accessors
- */
-
- //! read-only access to an item at a given index
- inline const TYPE& operator [] (size_t index) const;
- //! alternate name for operator []
- inline const TYPE& itemAt(size_t index) const;
- //! stack-usage of the vector. returns the top of the stack (last element)
- const TYPE& top() const;
- //! same as operator [], but allows to access the vector backward (from the end) with a negative index
- const TYPE& mirrorItemAt(ssize_t index) const;
-
- /*!
- * modifing the array
- */
-
- //! add an item in the right place (and replace the one that is there)
- ssize_t add(const TYPE& item);
-
- //! editItemAt() MUST NOT change the order of this item
- TYPE& editItemAt(size_t index) {
- return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
- }
-
- //! merges a vector into this one
- ssize_t merge(const Vector<TYPE>& vector);
- ssize_t merge(const SortedVector<TYPE>& vector);
-
- //! removes an item
- ssize_t remove(const TYPE&);
-
- //! remove several items
- inline ssize_t removeItemsAt(size_t index, size_t count = 1);
- //! remove one item
- inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
-
-protected:
- virtual void do_construct(void* storage, size_t num) const;
- virtual void do_destroy(void* storage, size_t num) const;
- virtual void do_copy(void* dest, const void* from, size_t num) const;
- virtual void do_splat(void* dest, const void* item, size_t num) const;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const;
- virtual int do_compare(const void* lhs, const void* rhs) const;
-};
-
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts from here...
-// ---------------------------------------------------------------------------
-
-template<class TYPE> inline
-SortedVector<TYPE>::SortedVector()
- : SortedVectorImpl(sizeof(TYPE),
- ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
- |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
- |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
- |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
- )
-{
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
- : SortedVectorImpl(rhs) {
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>::~SortedVector() {
- finish_vector();
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
- SortedVectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
- SortedVectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const TYPE* SortedVector<TYPE>::array() const {
- return static_cast<const TYPE *>(arrayImpl());
-}
-
-template<class TYPE> inline
-TYPE* SortedVector<TYPE>::editArray() {
- return static_cast<TYPE *>(editArrayImpl());
-}
-
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
- assert( index<size() );
- return *(array() + index);
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
- return operator[](index);
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::mirrorItemAt(ssize_t index) const {
- assert( (index>0 ? index : -index)<size() );
- return *(array() + ((index<0) ? (size()-index) : index));
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::top() const {
- return *(array() + size() - 1);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::add(const TYPE& item) {
- return SortedVectorImpl::add(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
- return SortedVectorImpl::indexOf(&item);
-}
-
-template<class TYPE> inline
-size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
- return SortedVectorImpl::orderOf(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
- return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
- return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
- return SortedVectorImpl::remove(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
- return VectorImpl::removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<class TYPE>
-void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
- construct_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
- destroy_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
- copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
- splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
- move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
- move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
- return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
-}
-
-} // namespace tinyutils
-} // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_SORTED_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h b/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h
deleted file mode 100644
index 7abff07..0000000
--- a/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_PIXELFLINGER_TYPE_HELPERS_H
-#define ANDROID_PIXELFLINGER_TYPE_HELPERS_H
-
-#include <new>
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-/*
- * Types traits
- */
-
-template <typename T> struct trait_trivial_ctor { enum { value = false }; };
-template <typename T> struct trait_trivial_dtor { enum { value = false }; };
-template <typename T> struct trait_trivial_copy { enum { value = false }; };
-template <typename T> struct trait_trivial_assign{ enum { value = false }; };
-
-template <typename T> struct trait_pointer { enum { value = false }; };
-template <typename T> struct trait_pointer<T*> { enum { value = true }; };
-
-#define ANDROID_BASIC_TYPES_TRAITS( T ) \
- template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \
- template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \
- template<> struct trait_trivial_copy< T > { enum { value = true }; }; \
- template<> struct trait_trivial_assign< T >{ enum { value = true }; };
-
-#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \
- template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \
- template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \
- template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \
- template<> struct trait_trivial_assign< T >{ enum { value = assign }; };
-
-template <typename TYPE>
-struct traits {
- enum {
- is_pointer = trait_pointer<TYPE>::value,
- has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
- has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
- has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
- has_trivial_assign = is_pointer || trait_trivial_assign<TYPE>::value
- };
-};
-
-template <typename T, typename U>
-struct aggregate_traits {
- enum {
- is_pointer = false,
- has_trivial_ctor = traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
- has_trivial_dtor = traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
- has_trivial_copy = traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
- has_trivial_assign = traits<T>::has_trivial_assign && traits<U>::has_trivial_assign
- };
-};
-
-// ---------------------------------------------------------------------------
-
-/*
- * basic types traits
- */
-
-ANDROID_BASIC_TYPES_TRAITS( void );
-ANDROID_BASIC_TYPES_TRAITS( bool );
-ANDROID_BASIC_TYPES_TRAITS( char );
-ANDROID_BASIC_TYPES_TRAITS( unsigned char );
-ANDROID_BASIC_TYPES_TRAITS( short );
-ANDROID_BASIC_TYPES_TRAITS( unsigned short );
-ANDROID_BASIC_TYPES_TRAITS( int );
-ANDROID_BASIC_TYPES_TRAITS( unsigned int );
-ANDROID_BASIC_TYPES_TRAITS( long );
-ANDROID_BASIC_TYPES_TRAITS( unsigned long );
-ANDROID_BASIC_TYPES_TRAITS( long long );
-ANDROID_BASIC_TYPES_TRAITS( unsigned long long );
-ANDROID_BASIC_TYPES_TRAITS( float );
-ANDROID_BASIC_TYPES_TRAITS( double );
-
-// ---------------------------------------------------------------------------
-
-
-/*
- * compare and order types
- */
-
-template<typename TYPE> inline
-int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
- return (lhs < rhs) ? 1 : 0;
-}
-
-template<typename TYPE> inline
-int compare_type(const TYPE& lhs, const TYPE& rhs) {
- return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
-}
-
-/*
- * create, destroy, copy and assign types...
- */
-
-template<typename TYPE> inline
-void construct_type(TYPE* p, size_t n) {
- if (!traits<TYPE>::has_trivial_ctor) {
- while (n--) {
- new(p++) TYPE;
- }
- }
-}
-
-template<typename TYPE> inline
-void destroy_type(TYPE* p, size_t n) {
- if (!traits<TYPE>::has_trivial_dtor) {
- while (n--) {
- p->~TYPE();
- p++;
- }
- }
-}
-
-template<typename TYPE> inline
-void copy_type(TYPE* d, const TYPE* s, size_t n) {
- if (!traits<TYPE>::has_trivial_copy) {
- while (n--) {
- new(d) TYPE(*s);
- d++, s++;
- }
- } else {
- memcpy(d,s,n*sizeof(TYPE));
- }
-}
-
-template<typename TYPE> inline
-void assign_type(TYPE* d, const TYPE* s, size_t n) {
- if (!traits<TYPE>::has_trivial_assign) {
- while (n--) {
- *d++ = *s++;
- }
- } else {
- memcpy(d,s,n*sizeof(TYPE));
- }
-}
-
-template<typename TYPE> inline
-void splat_type(TYPE* where, const TYPE* what, size_t n) {
- if (!traits<TYPE>::has_trivial_copy) {
- while (n--) {
- new(where) TYPE(*what);
- where++;
- }
- } else {
- while (n--) {
- *where++ = *what;
- }
- }
-}
-
-template<typename TYPE> inline
-void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
- d += n;
- s += n;
- while (n--) {
- --d, --s;
- if (!traits<TYPE>::has_trivial_copy) {
- new(d) TYPE(*s);
- } else {
- *d = *s;
- }
- if (!traits<TYPE>::has_trivial_dtor) {
- s->~TYPE();
- }
- }
- } else {
- memmove(d,s,n*sizeof(TYPE));
- }
-}
-
-template<typename TYPE> inline
-void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
- while (n--) {
- if (!traits<TYPE>::has_trivial_copy) {
- new(d) TYPE(*s);
- } else {
- *d = *s;
- }
- if (!traits<TYPE>::has_trivial_dtor) {
- s->~TYPE();
- }
- d++, s++;
- }
- } else {
- memmove(d,s,n*sizeof(TYPE));
- }
-}
-// ---------------------------------------------------------------------------
-
-/*
- * a key/value pair
- */
-
-template <typename KEY, typename VALUE>
-struct key_value_pair_t {
- KEY key;
- VALUE value;
- key_value_pair_t() { }
- key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
- key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
- key_value_pair_t(const KEY& k) : key(k) { }
- inline bool operator < (const key_value_pair_t& o) const {
- return strictly_order_type(key, o.key);
- }
-};
-
-template<>
-template <typename K, typename V>
-struct trait_trivial_ctor< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
-template<>
-template <typename K, typename V>
-struct trait_trivial_dtor< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
-template<>
-template <typename K, typename V>
-struct trait_trivial_copy< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
-template<>
-template <typename K, typename V>
-struct trait_trivial_assign< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_assign};};
-
-// ---------------------------------------------------------------------------
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_TYPE_HELPERS_H
diff --git a/libpixelflinger/codeflinger/tinyutils/Vector.h b/libpixelflinger/codeflinger/tinyutils/Vector.h
deleted file mode 100644
index c07a17a..0000000
--- a/libpixelflinger/codeflinger/tinyutils/Vector.h
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_PIXELFLINGER_VECTOR_H
-#define ANDROID_PIXELFLINGER_VECTOR_H
-
-#include <new>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <cutils/log.h>
-
-#include "Errors.h"
-#include "VectorImpl.h"
-#include "TypeHelpers.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-/*!
- * The main templated vector class ensuring type safety
- * while making use of VectorImpl.
- * This is the class users want to use.
- */
-
-template <class TYPE>
-class Vector : private VectorImpl
-{
-public:
- typedef TYPE value_type;
-
- /*!
- * Constructors and destructors
- */
-
- Vector();
- Vector(const Vector<TYPE>& rhs);
- virtual ~Vector();
-
- /*! copy operator */
- const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
- Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
-
- /*
- * empty the vector
- */
-
- inline void clear() { VectorImpl::clear(); }
-
- /*!
- * vector stats
- */
-
- //! returns number of items in the vector
- inline size_t size() const { return VectorImpl::size(); }
- //! returns wether or not the vector is empty
- inline bool isEmpty() const { return VectorImpl::isEmpty(); }
- //! returns how many items can be stored without reallocating the backing store
- inline size_t capacity() const { return VectorImpl::capacity(); }
- //! setst the capacity. capacity can never be reduced less than size()
- inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
-
- /*!
- * C-style array access
- */
-
- //! read-only C-style access
- inline const TYPE* array() const;
- //! read-write C-style access
- TYPE* editArray();
-
- /*!
- * accessors
- */
-
- //! read-only access to an item at a given index
- inline const TYPE& operator [] (size_t index) const;
- //! alternate name for operator []
- inline const TYPE& itemAt(size_t index) const;
- //! stack-usage of the vector. returns the top of the stack (last element)
- const TYPE& top() const;
- //! same as operator [], but allows to access the vector backward (from the end) with a negative index
- const TYPE& mirrorItemAt(ssize_t index) const;
-
- /*!
- * modifing the array
- */
-
- //! copy-on write support, grants write access to an item
- TYPE& editItemAt(size_t index);
- //! grants right acces to the top of the stack (last element)
- TYPE& editTop();
-
- /*!
- * append/insert another vector
- */
-
- //! insert another vector at a given index
- ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index);
-
- //! append another vector at the end of this one
- ssize_t appendVector(const Vector<TYPE>& vector);
-
-
- /*!
- * add/insert/replace items
- */
-
- //! insert one or several items initialized with their default constructor
- inline ssize_t insertAt(size_t index, size_t numItems = 1);
- //! insert on onr several items initialized from a prototype item
- ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
- //! pop the top of the stack (removes the last element). No-op if the stack's empty
- inline void pop();
- //! pushes an item initialized with its default constructor
- inline void push();
- //! pushes an item on the top of the stack
- void push(const TYPE& item);
- //! same as push() but returns the index the item was added at (or an error)
- inline ssize_t add();
- //! same as push() but returns the index the item was added at (or an error)
- ssize_t add(const TYPE& item);
- //! replace an item with a new one initialized with its default constructor
- inline ssize_t replaceAt(size_t index);
- //! replace an item with a new one
- ssize_t replaceAt(const TYPE& item, size_t index);
-
- /*!
- * remove items
- */
-
- //! remove several items
- inline ssize_t removeItemsAt(size_t index, size_t count = 1);
- //! remove one item
- inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
-
- /*!
- * sort (stable) the array
- */
-
- typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
- typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
-
- inline status_t sort(compar_t cmp);
- inline status_t sort(compar_r_t cmp, void* state);
-
-protected:
- virtual void do_construct(void* storage, size_t num) const;
- virtual void do_destroy(void* storage, size_t num) const;
- virtual void do_copy(void* dest, const void* from, size_t num) const;
- virtual void do_splat(void* dest, const void* item, size_t num) const;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const;
-};
-
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts from here...
-// ---------------------------------------------------------------------------
-
-template<class TYPE> inline
-Vector<TYPE>::Vector()
- : VectorImpl(sizeof(TYPE),
- ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
- |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
- |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
- |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
- )
-{
-}
-
-template<class TYPE> inline
-Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
- : VectorImpl(rhs) {
-}
-
-template<class TYPE> inline
-Vector<TYPE>::~Vector() {
- finish_vector();
-}
-
-template<class TYPE> inline
-Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
- VectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
- VectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const TYPE* Vector<TYPE>::array() const {
- return static_cast<const TYPE *>(arrayImpl());
-}
-
-template<class TYPE> inline
-TYPE* Vector<TYPE>::editArray() {
- return static_cast<TYPE *>(editArrayImpl());
-}
-
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::operator[](size_t index) const {
- LOG_FATAL_IF( index>=size(),
- "itemAt: index %d is past size %d", (int)index, (int)size() );
- return *(array() + index);
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::itemAt(size_t index) const {
- return operator[](index);
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::mirrorItemAt(ssize_t index) const {
- LOG_FATAL_IF( (index>0 ? index : -index)>=size(),
- "mirrorItemAt: index %d is past size %d",
- (int)index, (int)size() );
- return *(array() + ((index<0) ? (size()-index) : index));
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::top() const {
- return *(array() + size() - 1);
-}
-
-template<class TYPE> inline
-TYPE& Vector<TYPE>::editItemAt(size_t index) {
- return *( static_cast<TYPE *>(editItemLocation(index)) );
-}
-
-template<class TYPE> inline
-TYPE& Vector<TYPE>::editTop() {
- return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
- return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
- return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
- return VectorImpl::insertAt(&item, index, numItems);
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::push(const TYPE& item) {
- return VectorImpl::push(&item);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::add(const TYPE& item) {
- return VectorImpl::add(&item);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
- return VectorImpl::replaceAt(&item, index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
- return VectorImpl::insertAt(index, numItems);
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::pop() {
- VectorImpl::pop();
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::push() {
- VectorImpl::push();
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::add() {
- return VectorImpl::add();
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::replaceAt(size_t index) {
- return VectorImpl::replaceAt(index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
- return VectorImpl::removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<class TYPE>
-void Vector<TYPE>::do_construct(void* storage, size_t num) const {
- construct_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
- destroy_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
- copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
- splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
- move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
- move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-} // namespace tinyutils
-} // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp b/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp
deleted file mode 100644
index 689129a..0000000
--- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Vector"
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <cutils/log.h>
-
-#include "Errors.h"
-#include "SharedBuffer.h"
-#include "VectorImpl.h"
-
-/*****************************************************************************/
-
-
-namespace android {
-namespace tinyutils {
-
-// ----------------------------------------------------------------------------
-
-const size_t kMinVectorCapacity = 4;
-
-static inline size_t max(size_t a, size_t b) {
- return a>b ? a : b;
-}
-
-// ----------------------------------------------------------------------------
-
-VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
- : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
-{
-}
-
-VectorImpl::VectorImpl(const VectorImpl& rhs)
- : mStorage(rhs.mStorage), mCount(rhs.mCount),
- mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
-{
- if (mStorage) {
- SharedBuffer::sharedBuffer(mStorage)->acquire();
- }
-}
-
-VectorImpl::~VectorImpl()
-{
- ALOG_ASSERT(!mCount,
- "[%p] "
- "subclasses of VectorImpl must call finish_vector()"
- " in their destructor. Leaking %d bytes.",
- this, (int)(mCount*mItemSize));
- // We can't call _do_destroy() here because the vtable is already gone.
-}
-
-VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
-{
- ALOG_ASSERT(mItemSize == rhs.mItemSize,
- "Vector<> have different types (this=%p, rhs=%p)", this, &rhs);
- if (this != &rhs) {
- release_storage();
- if (rhs.mCount) {
- mStorage = rhs.mStorage;
- mCount = rhs.mCount;
- SharedBuffer::sharedBuffer(mStorage)->acquire();
- } else {
- mStorage = 0;
- mCount = 0;
- }
- }
- return *this;
-}
-
-void* VectorImpl::editArrayImpl()
-{
- if (mStorage) {
- SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit();
- if (sb == 0) {
- sb = SharedBuffer::alloc(capacity() * mItemSize);
- if (sb) {
- _do_copy(sb->data(), mStorage, mCount);
- release_storage();
- mStorage = sb->data();
- }
- }
- }
- return mStorage;
-}
-
-size_t VectorImpl::capacity() const
-{
- if (mStorage) {
- return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize;
- }
- return 0;
-}
-
-ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
-{
- if (index > size())
- return BAD_INDEX;
- void* where = _grow(index, vector.size());
- if (where) {
- _do_copy(where, vector.arrayImpl(), vector.size());
- }
- return where ? index : (ssize_t)NO_MEMORY;
-}
-
-ssize_t VectorImpl::appendVector(const VectorImpl& vector)
-{
- return insertVectorAt(vector, size());
-}
-
-ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
-{
- return insertAt(0, index, numItems);
-}
-
-ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
-{
- if (index > size())
- return BAD_INDEX;
- void* where = _grow(index, numItems);
- if (where) {
- if (item) {
- _do_splat(where, item, numItems);
- } else {
- _do_construct(where, numItems);
- }
- }
- return where ? index : (ssize_t)NO_MEMORY;
-}
-
-void VectorImpl::pop()
-{
- if (size())
- removeItemsAt(size()-1, 1);
-}
-
-void VectorImpl::push()
-{
- push(0);
-}
-
-void VectorImpl::push(const void* item)
-{
- insertAt(item, size());
-}
-
-ssize_t VectorImpl::add()
-{
- return add(0);
-}
-
-ssize_t VectorImpl::add(const void* item)
-{
- return insertAt(item, size());
-}
-
-ssize_t VectorImpl::replaceAt(size_t index)
-{
- return replaceAt(0, index);
-}
-
-ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
-{
- ALOG_ASSERT(index<size(),
- "[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
-
- void* item = editItemLocation(index);
- if (item == 0)
- return NO_MEMORY;
- _do_destroy(item, 1);
- if (prototype == 0) {
- _do_construct(item, 1);
- } else {
- _do_copy(item, prototype, 1);
- }
- return ssize_t(index);
-}
-
-ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
-{
- ALOG_ASSERT((index+count)<=size(),
- "[%p] remove: index=%d, count=%d, size=%d",
- this, (int)index, (int)count, (int)size());
-
- if ((index+count) > size())
- return BAD_VALUE;
- _shrink(index, count);
- return index;
-}
-
-void VectorImpl::finish_vector()
-{
- release_storage();
- mStorage = 0;
- mCount = 0;
-}
-
-void VectorImpl::clear()
-{
- _shrink(0, mCount);
-}
-
-void* VectorImpl::editItemLocation(size_t index)
-{
- ALOG_ASSERT(index<capacity(),
- "[%p] itemLocation: index=%d, capacity=%d, count=%d",
- this, (int)index, (int)capacity(), (int)mCount);
-
- void* buffer = editArrayImpl();
- if (buffer)
- return reinterpret_cast<char*>(buffer) + index*mItemSize;
- return 0;
-}
-
-const void* VectorImpl::itemLocation(size_t index) const
-{
- ALOG_ASSERT(index<capacity(),
- "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
- this, (int)index, (int)capacity(), (int)mCount);
-
- const void* buffer = arrayImpl();
- if (buffer)
- return reinterpret_cast<const char*>(buffer) + index*mItemSize;
- return 0;
-}
-
-ssize_t VectorImpl::setCapacity(size_t new_capacity)
-{
- size_t current_capacity = capacity();
- ssize_t amount = new_capacity - size();
- if (amount <= 0) {
- // we can't reduce the capacity
- return current_capacity;
- }
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
- if (sb) {
- void* array = sb->data();
- _do_copy(array, mStorage, size());
- release_storage();
- mStorage = const_cast<void*>(array);
- } else {
- return NO_MEMORY;
- }
- return new_capacity;
-}
-
-void VectorImpl::release_storage()
-{
- if (mStorage) {
- const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage);
- if (sb->release(SharedBuffer::eKeepStorage) == 1) {
- _do_destroy(mStorage, mCount);
- SharedBuffer::dealloc(sb);
- }
- }
-}
-
-void* VectorImpl::_grow(size_t where, size_t amount)
-{
-// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
-// this, (int)where, (int)amount, (int)mCount, (int)capacity());
-
- if (where > mCount)
- where = mCount;
-
- const size_t new_size = mCount + amount;
- if (capacity() < new_size) {
- const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
-// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
- if ((mStorage) &&
- (mCount==where) &&
- (mFlags & HAS_TRIVIAL_COPY) &&
- (mFlags & HAS_TRIVIAL_DTOR))
- {
- const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
- SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
- mStorage = sb->data();
- } else {
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
- if (sb) {
- void* array = sb->data();
- if (where>0) {
- _do_copy(array, mStorage, where);
- }
- if (mCount>where) {
- const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
- void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
- _do_copy(dest, from, mCount-where);
- }
- release_storage();
- mStorage = const_cast<void*>(array);
- }
- }
- } else {
- ssize_t s = mCount-where;
- if (s>0) {
- void* array = editArrayImpl();
- void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
- const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
- _do_move_forward(to, from, s);
- }
- }
- mCount += amount;
- void* free_space = const_cast<void*>(itemLocation(where));
- return free_space;
-}
-
-void VectorImpl::_shrink(size_t where, size_t amount)
-{
- if (!mStorage)
- return;
-
-// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
-// this, (int)where, (int)amount, (int)mCount, (int)capacity());
-
- if (where >= mCount)
- where = mCount - amount;
-
- const size_t new_size = mCount - amount;
- if (new_size*3 < capacity()) {
- const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
-// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
- if ((where == mCount-amount) &&
- (mFlags & HAS_TRIVIAL_COPY) &&
- (mFlags & HAS_TRIVIAL_DTOR))
- {
- const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
- SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
- mStorage = sb->data();
- } else {
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
- if (sb) {
- void* array = sb->data();
- if (where>0) {
- _do_copy(array, mStorage, where);
- }
- if (mCount > where+amount) {
- const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
- void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
- _do_copy(dest, from, mCount-(where+amount));
- }
- release_storage();
- mStorage = const_cast<void*>(array);
- }
- }
- } else {
- void* array = editArrayImpl();
- void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
- _do_destroy(to, amount);
- ssize_t s = mCount-(where+amount);
- if (s>0) {
- const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
- _do_move_backward(to, from, s);
- }
- }
-
- // adjust the number of items...
- mCount -= amount;
-}
-
-size_t VectorImpl::itemSize() const {
- return mItemSize;
-}
-
-void VectorImpl::_do_construct(void* storage, size_t num) const
-{
- if (!(mFlags & HAS_TRIVIAL_CTOR)) {
- do_construct(storage, num);
- }
-}
-
-void VectorImpl::_do_destroy(void* storage, size_t num) const
-{
- if (!(mFlags & HAS_TRIVIAL_DTOR)) {
- do_destroy(storage, num);
- }
-}
-
-void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const
-{
- if (!(mFlags & HAS_TRIVIAL_COPY)) {
- do_copy(dest, from, num);
- } else {
- memcpy(dest, from, num*itemSize());
- }
-}
-
-void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {
- do_splat(dest, item, num);
-}
-
-void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {
- do_move_forward(dest, from, num);
-}
-
-void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {
- do_move_backward(dest, from, num);
-}
-
-void VectorImpl::reservedVectorImpl1() { }
-void VectorImpl::reservedVectorImpl2() { }
-void VectorImpl::reservedVectorImpl3() { }
-void VectorImpl::reservedVectorImpl4() { }
-void VectorImpl::reservedVectorImpl5() { }
-void VectorImpl::reservedVectorImpl6() { }
-void VectorImpl::reservedVectorImpl7() { }
-void VectorImpl::reservedVectorImpl8() { }
-
-/*****************************************************************************/
-
-SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)
- : VectorImpl(itemSize, flags)
-{
-}
-
-SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)
-: VectorImpl(rhs)
-{
-}
-
-SortedVectorImpl::~SortedVectorImpl()
-{
-}
-
-SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)
-{
- return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );
-}
-
-ssize_t SortedVectorImpl::indexOf(const void* item) const
-{
- return _indexOrderOf(item);
-}
-
-size_t SortedVectorImpl::orderOf(const void* item) const
-{
- size_t o;
- _indexOrderOf(item, &o);
- return o;
-}
-
-ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
-{
- // binary search
- ssize_t err = NAME_NOT_FOUND;
- ssize_t l = 0;
- ssize_t h = size()-1;
- ssize_t mid;
- const void* a = arrayImpl();
- const size_t s = itemSize();
- while (l <= h) {
- mid = l + (h - l)/2;
- const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);
- const int c = do_compare(curr, item);
- if (c == 0) {
- err = l = mid;
- break;
- } else if (c < 0) {
- l = mid + 1;
- } else {
- h = mid - 1;
- }
- }
- if (order) *order = l;
- return err;
-}
-
-ssize_t SortedVectorImpl::add(const void* item)
-{
- size_t order;
- ssize_t index = _indexOrderOf(item, &order);
- if (index < 0) {
- index = VectorImpl::insertAt(item, order, 1);
- } else {
- index = VectorImpl::replaceAt(item, index);
- }
- return index;
-}
-
-ssize_t SortedVectorImpl::merge(const VectorImpl& vector)
-{
- // naive merge...
- if (!vector.isEmpty()) {
- const void* buffer = vector.arrayImpl();
- const size_t is = itemSize();
- size_t s = vector.size();
- for (size_t i=0 ; i<s ; i++) {
- ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );
- if (err<0) {
- return err;
- }
- }
- }
- return NO_ERROR;
-}
-
-ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
-{
- // we've merging a sorted vector... nice!
- ssize_t err = NO_ERROR;
- if (!vector.isEmpty()) {
- // first take care of the case where the vectors are sorted together
- if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
- err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);
- } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {
- err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));
- } else {
- // this could be made a little better
- err = merge(static_cast<const VectorImpl&>(vector));
- }
- }
- return err;
-}
-
-ssize_t SortedVectorImpl::remove(const void* item)
-{
- ssize_t i = indexOf(item);
- if (i>=0) {
- VectorImpl::removeItemsAt(i, 1);
- }
- return i;
-}
-
-void SortedVectorImpl::reservedSortedVectorImpl1() { };
-void SortedVectorImpl::reservedSortedVectorImpl2() { };
-void SortedVectorImpl::reservedSortedVectorImpl3() { };
-void SortedVectorImpl::reservedSortedVectorImpl4() { };
-void SortedVectorImpl::reservedSortedVectorImpl5() { };
-void SortedVectorImpl::reservedSortedVectorImpl6() { };
-void SortedVectorImpl::reservedSortedVectorImpl7() { };
-void SortedVectorImpl::reservedSortedVectorImpl8() { };
-
-
-/*****************************************************************************/
-
-} // namespace tinyutils
-} // namespace android
-
diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
deleted file mode 100644
index 56089b3..0000000
--- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_PIXELFLINGER_VECTOR_IMPL_H
-#define ANDROID_PIXELFLINGER_VECTOR_IMPL_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts in here...
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-/*!
- * Implementation of the guts of the vector<> class
- * this ensures backward binary compatibility and
- * reduces code size.
- * For performance reasons, we expose mStorage and mCount
- * so these fields are set in stone.
- *
- */
-
-class VectorImpl
-{
-public:
- enum { // flags passed to the ctor
- HAS_TRIVIAL_CTOR = 0x00000001,
- HAS_TRIVIAL_DTOR = 0x00000002,
- HAS_TRIVIAL_COPY = 0x00000004,
- HAS_TRIVIAL_ASSIGN = 0x00000008
- };
-
- VectorImpl(size_t itemSize, uint32_t flags);
- VectorImpl(const VectorImpl& rhs);
- virtual ~VectorImpl();
-
- /*! must be called from subclasses destructor */
- void finish_vector();
-
- VectorImpl& operator = (const VectorImpl& rhs);
-
- /*! C-style array access */
- inline const void* arrayImpl() const { return mStorage; }
- void* editArrayImpl();
-
- /*! vector stats */
- inline size_t size() const { return mCount; }
- inline bool isEmpty() const { return mCount == 0; }
- size_t capacity() const;
- ssize_t setCapacity(size_t size);
-
- /*! append/insert another vector */
- ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
- ssize_t appendVector(const VectorImpl& vector);
-
- /*! add/insert/replace items */
- ssize_t insertAt(size_t where, size_t numItems = 1);
- ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
- void pop();
- void push();
- void push(const void* item);
- ssize_t add();
- ssize_t add(const void* item);
- ssize_t replaceAt(size_t index);
- ssize_t replaceAt(const void* item, size_t index);
-
- /*! remove items */
- ssize_t removeItemsAt(size_t index, size_t count = 1);
- void clear();
-
- const void* itemLocation(size_t index) const;
- void* editItemLocation(size_t index);
-
-protected:
- size_t itemSize() const;
- void release_storage();
-
- virtual void do_construct(void* storage, size_t num) const = 0;
- virtual void do_destroy(void* storage, size_t num) const = 0;
- virtual void do_copy(void* dest, const void* from, size_t num) const = 0;
- virtual void do_splat(void* dest, const void* item, size_t num) const = 0;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0;
-
- // take care of FBC...
- virtual void reservedVectorImpl1();
- virtual void reservedVectorImpl2();
- virtual void reservedVectorImpl3();
- virtual void reservedVectorImpl4();
- virtual void reservedVectorImpl5();
- virtual void reservedVectorImpl6();
- virtual void reservedVectorImpl7();
- virtual void reservedVectorImpl8();
-
-private:
- void* _grow(size_t where, size_t amount);
- void _shrink(size_t where, size_t amount);
-
- inline void _do_construct(void* storage, size_t num) const;
- inline void _do_destroy(void* storage, size_t num) const;
- inline void _do_copy(void* dest, const void* from, size_t num) const;
- inline void _do_splat(void* dest, const void* item, size_t num) const;
- inline void _do_move_forward(void* dest, const void* from, size_t num) const;
- inline void _do_move_backward(void* dest, const void* from, size_t num) const;
-
- // These 2 fields are exposed in the inlines below,
- // so they're set in stone.
- void * mStorage; // base address of the vector
- size_t mCount; // number of items
-
- const uint32_t mFlags;
- const size_t mItemSize;
-};
-
-
-
-class SortedVectorImpl : public VectorImpl
-{
-public:
- SortedVectorImpl(size_t itemSize, uint32_t flags);
- SortedVectorImpl(const VectorImpl& rhs);
- virtual ~SortedVectorImpl();
-
- SortedVectorImpl& operator = (const SortedVectorImpl& rhs);
-
- //! finds the index of an item
- ssize_t indexOf(const void* item) const;
-
- //! finds where this item should be inserted
- size_t orderOf(const void* item) const;
-
- //! add an item in the right place (or replaces it if there is one)
- ssize_t add(const void* item);
-
- //! merges a vector into this one
- ssize_t merge(const VectorImpl& vector);
- ssize_t merge(const SortedVectorImpl& vector);
-
- //! removes an item
- ssize_t remove(const void* item);
-
-protected:
- virtual int do_compare(const void* lhs, const void* rhs) const = 0;
-
- // take care of FBC...
- virtual void reservedSortedVectorImpl1();
- virtual void reservedSortedVectorImpl2();
- virtual void reservedSortedVectorImpl3();
- virtual void reservedSortedVectorImpl4();
- virtual void reservedSortedVectorImpl5();
- virtual void reservedSortedVectorImpl6();
- virtual void reservedSortedVectorImpl7();
- virtual void reservedSortedVectorImpl8();
-
-private:
- ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
-
- // these are made private, because they can't be used on a SortedVector
- // (they don't have an implementation either)
- ssize_t add();
- void pop();
- void push();
- void push(const void* item);
- ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
- ssize_t appendVector(const VectorImpl& vector);
- ssize_t insertAt(size_t where, size_t numItems = 1);
- ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
- ssize_t replaceAt(size_t index);
- ssize_t replaceAt(const void* item, size_t index);
-};
-
-} // namespace tinyutils
-} // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_VECTOR_IMPL_H
diff --git a/libpixelflinger/codeflinger/tinyutils/smartpointer.h b/libpixelflinger/codeflinger/tinyutils/smartpointer.h
index 9d0a16e..23a5f7e 100644
--- a/libpixelflinger/codeflinger/tinyutils/smartpointer.h
+++ b/libpixelflinger/codeflinger/tinyutils/smartpointer.h
@@ -51,10 +51,10 @@
public:
inline sp() : m_ptr(0) { }
- sp(T* other);
+ sp(T* other); // NOLINT, implicit
sp(const sp<T>& other);
- template<typename U> sp(U* other);
- template<typename U> sp(const sp<U>& other);
+ template<typename U> sp(U* other); // NOLINT, implicit
+ template<typename U> sp(const sp<U>& other); // NOLINT, implicit
~sp();
diff --git a/libpixelflinger/col32cb16blend.S b/libpixelflinger/col32cb16blend.S
index 1831255..39f94e1 100644
--- a/libpixelflinger/col32cb16blend.S
+++ b/libpixelflinger/col32cb16blend.S
@@ -16,7 +16,7 @@
*/
.text
- .align
+ .balign 4
.global scanline_col32cb16blend_arm
diff --git a/libpixelflinger/col32cb16blend_neon.S b/libpixelflinger/col32cb16blend_neon.S
index cbd54d1..7ad34b0 100644
--- a/libpixelflinger/col32cb16blend_neon.S
+++ b/libpixelflinger/col32cb16blend_neon.S
@@ -17,7 +17,7 @@
.text
- .align
+ .balign 4
.global scanline_col32cb16blend_neon
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_context.h b/libpixelflinger/include/private/pixelflinger/ggl_context.h
index d43655c..563b0f1 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_context.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_context.h
@@ -42,7 +42,7 @@
#else
inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
-#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+#if defined(__mips__) && __mips_isa_rev>=2
uint32_t r;
__asm__("wsbh %0, %1;"
"rotr %0, %0, 16"
@@ -55,7 +55,7 @@
#endif
}
inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
-#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+#if defined(__mips__) && __mips_isa_rev>=2
uint32_t r;
__asm__("wsbh %0, %1;"
"rotr %0, %0, 16"
@@ -120,7 +120,7 @@
template<bool> struct CTA;
template<> struct CTA<true> { };
-#define GGL_CONTEXT(con, c) context_t *con = static_cast<context_t *>(c)
+#define GGL_CONTEXT(con, c) context_t *(con) = static_cast<context_t *>(c) /* NOLINT */
#define GGL_OFFSETOF(field) uintptr_t(&(((context_t*)0)->field))
#define GGL_INIT_PROC(p, f) p.f = ggl_ ## f;
#define GGL_BETWEEN(x, L, H) (uint32_t((x)-(L)) <= ((H)-(L)))
@@ -136,14 +136,14 @@
// ----------------------------------------------------------------------------
#define GGL_RESERVE_NEEDS(name, l, s) \
- const uint32_t GGL_NEEDS_##name##_MASK = (((1LU<<(s))-1)<<l); \
+ const uint32_t GGL_NEEDS_##name##_MASK = (((1LU<<(s))-1)<<(l)); \
const uint32_t GGL_NEEDS_##name##_SHIFT = (l);
#define GGL_BUILD_NEEDS(val, name) \
(((val)<<(GGL_NEEDS_##name##_SHIFT)) & GGL_NEEDS_##name##_MASK)
#define GGL_READ_NEEDS(name, n) \
- (uint32_t(n & GGL_NEEDS_##name##_MASK) >> GGL_NEEDS_##name##_SHIFT)
+ (uint32_t((n) & GGL_NEEDS_##name##_MASK) >> GGL_NEEDS_##name##_SHIFT)
#define GGL_NEED_MASK(name) (uint32_t(GGL_NEEDS_##name##_MASK))
#define GGL_NEED(name, val) GGL_BUILD_NEEDS(val, name)
@@ -234,7 +234,7 @@
// ----------------------------------------------------------------------------
-class needs_filter_t;
+struct needs_filter_t;
struct needs_t {
inline int match(const needs_filter_t& filter);
inline bool operator == (const needs_t& rhs) const {
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 787f620..17b85dd 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -520,6 +520,252 @@
return res;
}
+#elif defined(__mips__) && __mips_isa_rev == 6
+
+/*inline MIPS implementations*/
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) {
+ GGLfixed result,tmp,tmp1,tmp2;
+
+ if (__builtin_constant_p(shift)) {
+ if (shift == 0) {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ : [res]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b)
+ );
+ } else if (shift == 32)
+ {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "li %[tmp],1\t\n"
+ "sll %[tmp],%[tmp],0x1f\t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "muh %[res], %[a], %[b] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp]\t\n" /*obit*/
+ "sra %[tmp],%[tmp],0x1f \t\n"
+ "addu %[res],%[res],%[tmp]\t\n"
+ "addu %[res],%[res],%[tmp1]\t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1)
+ : [a]"r"(a),[b]"r"(b),[shift]"I"(shift)
+ );
+ } else if ((shift >0) && (shift < 32))
+ {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "li %[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[shiftm1] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/
+ "addu %[res],%[res],%[tmp] \t\n"
+ "muh %[tmp], %[a], %[b] \t\n"
+ "addu %[tmp],%[tmp],%[tmp1] \t\n"
+ "sll %[tmp],%[tmp],%[lshift] \t\n"
+ "srl %[res],%[res],%[rshift] \t\n"
+ "or %[res],%[res],%[tmp] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[lshift]"I"(32-shift),[rshift]"I"(shift),[shiftm1]"I"(shift-1)
+ );
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "li %[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[shiftm1] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/
+ "sra %[tmp2],%[tmp],0x1f \t\n"
+ "addu %[res],%[res],%[tmp] \t\n"
+ "muh %[tmp], %[a], %[b] \t\n"
+ "addu %[tmp],%[tmp],%[tmp2] \t\n"
+ "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/
+ "srl %[tmp2],%[res],%[rshift] \t\n"
+ "srav %[res], %[tmp],%[rshift]\t\n"
+ "sll %[tmp],%[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[norbits] \t\n"
+ "or %[tmp],%[tmp],%[tmp2] \t\n"
+ "seleqz %[tmp],%[tmp],%[bit5] \t\n"
+ "selnez %[res],%[res],%[bit5] \t\n"
+ "or %[res],%[res],%[tmp] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[norbits]"I"(~(shift)),[rshift]"I"(shift),[shiftm1] "I"(shift-1),[bit5]"I"(shift & 0x20)
+ );
+ }
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "li %[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[shiftm1] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/
+ "sra %[tmp2],%[tmp],0x1f \t\n"
+ "addu %[res],%[res],%[tmp] \t\n"
+ "muh %[tmp], %[a], %[b] \t\n"
+ "addu %[tmp],%[tmp],%[tmp2] \t\n"
+ "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/
+ "srl %[tmp2],%[res],%[rshift] \t\n"
+ "srav %[res], %[tmp],%[rshift]\t\n"
+ "sll %[tmp],%[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[norbits] \t\n"
+ "or %[tmp],%[tmp],%[tmp2] \t\n"
+ "seleqz %[tmp],%[tmp],%[bit5] \t\n"
+ "selnez %[res],%[res],%[bit5] \t\n"
+ "or %[res],%[res],%[tmp] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[norbits]"r"(~(shift)),[rshift] "r"(shift),[shiftm1]"r"(shift-1),[bit5] "r"(shift & 0x20)
+ );
+ }
+ return result;
+}
+
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+ GGLfixed result,t,tmp1,tmp2;
+
+ if (__builtin_constant_p(shift)) {
+ if (shift == 0) {
+ asm ("mul %[lo], %[a], %[b] \t\n"
+ "addu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ );
+ } else if (shift == 32) {
+ asm ("muh %[lo], %[a], %[b] \t\n"
+ "addu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ );
+ } else if ((shift>0) && (shift<32)) {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "srl %[res],%[res],%[rshift] \t\n"
+ "sll %[t],%[t],%[lshift] \t\n"
+ "or %[res],%[res],%[t] \t\n"
+ "addu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+ );
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+ "selnez %[res],%[res],%[tmp2]\t\n"
+ "or %[res],%[res],%[tmp1]\t\n"
+ "addu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+ );
+ }
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+ "selnez %[res],%[res],%[tmp2]\t\n"
+ "or %[res],%[res],%[tmp1]\t\n"
+ "addu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+ );
+ }
+ return result;
+}
+
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+ GGLfixed result,t,tmp1,tmp2;
+
+ if (__builtin_constant_p(shift)) {
+ if (shift == 0) {
+ asm ("mul %[lo], %[a], %[b] \t\n"
+ "subu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ );
+ } else if (shift == 32) {
+ asm ("muh %[lo], %[a], %[b] \t\n"
+ "subu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ );
+ } else if ((shift>0) && (shift<32)) {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "srl %[res],%[res],%[rshift] \t\n"
+ "sll %[t],%[t],%[lshift] \t\n"
+ "or %[res],%[res],%[t] \t\n"
+ "subu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+ );
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+ "selnez %[res],%[res],%[tmp2]\t\n"
+ "or %[res],%[res],%[tmp1]\t\n"
+ "subu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+ );
+ }
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+ "selnez %[res],%[res],%[tmp2]\t\n"
+ "or %[res],%[res],%[tmp1]\t\n"
+ "subu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+ );
+ }
+ return result;
+}
+
+inline int64_t gglMulii(int32_t x, int32_t y) CONST;
+inline int64_t gglMulii(int32_t x, int32_t y) {
+ union {
+ struct {
+#if defined(__MIPSEL__)
+ int32_t lo;
+ int32_t hi;
+#elif defined(__MIPSEB__)
+ int32_t hi;
+ int32_t lo;
+#endif
+ } s;
+ int64_t res;
+ }u;
+ asm("mul %0, %2, %3 \t\n"
+ "muh %1, %2, %3 \t\n"
+ : "=r"(u.s.lo), "=&r"(u.s.hi)
+ : "%r"(x), "r"(y)
+ );
+ return u.res;
+}
+
#else // ----------------------------------------------------------------------
inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
diff --git a/libpixelflinger/rotate90CW_4x4_16v6.S b/libpixelflinger/rotate90CW_4x4_16v6.S
deleted file mode 100644
index 8e3e142..0000000
--- a/libpixelflinger/rotate90CW_4x4_16v6.S
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-
- .text
- .align
-
- .global rotate90CW_4x4_16v6
-
-// Rotates 90deg CW a 4x4 block of 16bpp pixels using ARMv6
-// src and dst must be 4 pixels-aligned (2-pixels aligned might
-// actually work)
-//
-// The code below is complicated by ARM's little endianness.
-
-rotate90CW_4x4_16v6:
- // r0 = dst
- // r1 = src
- // r2 = dst stride in pixels
- // r3 = src stride in pixels
-
- stmfd sp!, {r4,r5, r6,r7, r8,r9, r10,r11, lr}
- add r14, r3, r3
- add r12, r2, r2
-
- ldrd r2, r3, [r1], r14
- ldrd r4, r5, [r1], r14
- ldrd r6, r7, [r1], r14
- ldrd r8, r9, [r1]
-
- pkhbt r10, r8, r6, lsl #16
- pkhbt r11, r4, r2, lsl #16
- strd r10, r11, [r0], r12
-
- pkhtb r10, r6, r8, asr #16
- pkhtb r11, r2, r4, asr #16
-
- strd r10, r11, [r0], r12
- pkhbt r10, r9, r7, lsl #16
- pkhbt r11, r5, r3, lsl #16
-
- strd r10, r11, [r0], r12
-
- pkhtb r10, r7, r9, asr #16
- pkhtb r11, r3, r5, asr #16
- strd r10, r11, [r0]
-
- ldmfd sp!, {r4,r5, r6,r7, r8,r9, r10,r11, pc}
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index 3d14531..c6cf5bf 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -2,33 +2,28 @@
**
** Copyright 2006-2011, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
#define LOG_TAG "pixelflinger"
#include <assert.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <cutils/memory.h>
-#include <cutils/log.h>
-
-#ifdef __arm__
-#include <machine/cpu-features.h>
-#endif
+#include <log/log.h>
#include "buffer.h"
#include "scanline.h"
@@ -41,6 +36,8 @@
#include "codeflinger/Arm64Assembler.h"
#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
#include "codeflinger/MIPSAssembler.h"
+#elif defined(__mips__) && defined(__LP64__)
+#include "codeflinger/MIPS64Assembler.h"
#endif
//#include "codeflinger/ARMAssemblerOptimizer.h"
@@ -59,7 +56,7 @@
# define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED
#endif
-#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))) || defined(__aarch64__)
# define ANDROID_ARM_CODEGEN 1
#else
# define ANDROID_ARM_CODEGEN 0
@@ -73,7 +70,7 @@
*/
#define DEBUG_NEEDS 0
-#if defined( __mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined( __mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))
#define ASSEMBLY_SCRATCH_SIZE 4096
#elif defined(__aarch64__)
#define ASSEMBLY_SCRATCH_SIZE 8192
@@ -136,6 +133,9 @@
extern "C" void scanline_col32cb16blend_arm64(uint16_t *dst, uint32_t col, size_t ct);
#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
+#elif defined(__mips__) && defined(__LP64__)
+extern "C" void scanline_t32cb16blend_mips64(uint16_t*, uint32_t*, size_t);
+extern "C" void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t col, size_t ct);
#endif
// ----------------------------------------------------------------------------
@@ -286,7 +286,7 @@
#if ANDROID_ARM_CODEGEN
-#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))
static CodeCache gCodeCache(32 * 1024);
#elif defined(__aarch64__)
static CodeCache gCodeCache(48 * 1024);
@@ -406,8 +406,10 @@
//GGLAssembler assembler(
// new ARMAssemblerOptimizer(new ARMAssembler(a)) );
#endif
-#if defined(__mips__)
+#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
GGLAssembler assembler( new ArmToMipsAssembler(a) );
+#elif defined(__mips__) && defined(__LP64__)
+ GGLAssembler assembler( new ArmToMips64Assembler(a) );
#elif defined(__aarch64__)
GGLAssembler assembler( new ArmToArm64Assembler(a) );
#endif
@@ -958,7 +960,7 @@
* Use only for one-to-one texture mapping.
*/
struct horz_iterator32 {
- horz_iterator32(context_t* c) {
+ explicit horz_iterator32(context_t* c) {
const int x = c->iterators.xl;
const int y = c->iterators.y;
texture_t& tx = c->state.texture[0];
@@ -975,7 +977,7 @@
/* A variant for 16-bit source textures. */
struct horz_iterator16 {
- horz_iterator16(context_t* c) {
+ explicit horz_iterator16(context_t* c) {
const int x = c->iterators.xl;
const int y = c->iterators.y;
texture_t& tx = c->state.texture[0];
@@ -995,7 +997,7 @@
* texture pixel value.
*/
struct clamp_iterator {
- clamp_iterator(context_t* c) {
+ explicit clamp_iterator(context_t* c) {
const int xs = c->iterators.xl;
texture_t& tx = c->state.texture[0];
texture_iterators_t& ti = tx.iterators;
@@ -1105,13 +1107,13 @@
}
struct horz_clamp_iterator16 : horz_clamp_iterator {
- horz_clamp_iterator16(const context_t* c) {
+ explicit horz_clamp_iterator16(const context_t* c) {
init(c,1);
};
};
struct horz_clamp_iterator32 : horz_clamp_iterator {
- horz_clamp_iterator32(context_t* c) {
+ explicit horz_clamp_iterator32(context_t* c) {
init(c,2);
};
};
@@ -1119,7 +1121,7 @@
/* This is used to perform dithering operations.
*/
struct ditherer {
- ditherer(const context_t* c) {
+ explicit ditherer(const context_t* c) {
const int x = c->iterators.xl;
const int y = c->iterators.y;
m_line = &c->ditherMatrix[ ((y & GGL_DITHER_MASK)<<GGL_DITHER_ORDER_SHIFT) ];
@@ -1165,7 +1167,7 @@
* blender.blend(<32-bit-src-pixel-value>,<ptr-to-16-bit-dest-pixel>)
*/
struct blender_32to16 {
- blender_32to16(context_t* /*c*/) { }
+ explicit blender_32to16(context_t* /*c*/) { }
void write(uint32_t s, uint16_t* dst) {
if (s == 0)
return;
@@ -1222,7 +1224,7 @@
* where dstFactor=srcA*(1-srcA) srcFactor=srcA
*/
struct blender_32to16_srcA {
- blender_32to16_srcA(const context_t* /*c*/) { }
+ explicit blender_32to16_srcA(const context_t* /*c*/) { }
void write(uint32_t s, uint16_t* dst) {
if (!s) {
return;
@@ -1264,7 +1266,7 @@
/* This blender does a normal blend after modulation.
*/
struct blender_32to16_modulate : blender_modulate {
- blender_32to16_modulate(const context_t* c) {
+ explicit blender_32to16_modulate(const context_t* c) {
init(c);
}
void write(uint32_t s, uint16_t* dst) {
@@ -1336,7 +1338,7 @@
/* same as 32to16_modulate, except that the input is xRGB, instead of ARGB */
struct blender_x32to16_modulate : blender_modulate {
- blender_x32to16_modulate(const context_t* c) {
+ explicit blender_x32to16_modulate(const context_t* c) {
init(c);
}
void write(uint32_t s, uint16_t* dst) {
@@ -1391,7 +1393,7 @@
/* Same as above, but source is 16bit rgb565 */
struct blender_16to16_modulate : blender_modulate {
- blender_16to16_modulate(const context_t* c) {
+ explicit blender_16to16_modulate(const context_t* c) {
init(c);
}
void write(uint16_t s16, uint16_t* dst) {
@@ -1427,7 +1429,7 @@
* }
*/
struct dst_iterator16 {
- dst_iterator16(const context_t* c) {
+ explicit dst_iterator16(const context_t* c) {
const int x = c->iterators.xl;
const int width = c->iterators.xr - x;
const int32_t y = c->iterators.y;
@@ -2103,6 +2105,8 @@
#endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__aarch64__))
scanline_col32cb16blend_arm64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
+#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__mips__) && defined(__LP64__)))
+ scanline_col32cb16blend_mips64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
#else
uint32_t s = GGL_RGBA_TO_HOST(c->packed8888);
int sA = (s>>24);
@@ -2175,7 +2179,8 @@
void scanline_t32cb16blend(context_t* c)
{
-#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)))
+#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__aarch64__) || \
+ (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__)))))
int32_t x = c->iterators.xl;
size_t ct = c->iterators.xr - x;
int32_t y = c->iterators.y;
@@ -2191,8 +2196,10 @@
scanline_t32cb16blend_arm(dst, src, ct);
#elif defined(__aarch64__)
scanline_t32cb16blend_arm64(dst, src, ct);
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
scanline_t32cb16blend_mips(dst, src, ct);
+#elif defined(__mips__) && defined(__LP64__)
+ scanline_t32cb16blend_mips64(dst, src, ct);
#endif
#else
dst_iterator16 di(c);
diff --git a/libpixelflinger/t32cb16blend.S b/libpixelflinger/t32cb16blend.S
index caf9eb7..5e4995a 100644
--- a/libpixelflinger/t32cb16blend.S
+++ b/libpixelflinger/t32cb16blend.S
@@ -17,7 +17,8 @@
.text
- .align
+ .syntax unified
+ .balign 4
.global scanline_t32cb16blend_arm
@@ -146,7 +147,7 @@
tst r0, #0x3
beq aligned
subs r2, r2, #1
- ldmlofd sp!, {r4-r7, lr} // return
+ ldmfdlo sp!, {r4-r7, lr} // return
bxlo lr
last:
@@ -197,6 +198,6 @@
mov r4, r5
9: adds r2, r2, #1
- ldmlofd sp!, {r4-r7, lr} // return
+ ldmfdlo sp!, {r4-r7, lr} // return
bxlo lr
b last
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
index 448d298..bd0f24b 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
@@ -5,9 +5,6 @@
arm64_assembler_test.cpp\
asm_test_jacket.S
-# asm_test_jacket.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
-
LOCAL_SHARED_LIBRARIES := \
libcutils \
libpixelflinger
diff --git a/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp
index 5f58797..63642c4 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp
+++ b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp
@@ -34,7 +34,6 @@
#include <sys/mman.h>
#include <cutils/ashmem.h>
-#include <cutils/atomic.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
diff --git a/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
index a1392c2..3f900f8 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
+++ b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
@@ -27,7 +27,7 @@
*/
.text
- .align
+ .balign 0
.global asm_test_jacket
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
index 5d69203..3368eb0 100644
--- a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
@@ -5,8 +5,6 @@
col32cb16blend_test.c \
../../../arch-arm64/col32cb16blend.S
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
-
LOCAL_SHARED_LIBRARIES :=
LOCAL_C_INCLUDES :=
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
index 2c1379b..8e5ec5e 100644
--- a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
@@ -5,8 +5,6 @@
t32cb16blend_test.c \
../../../arch-arm64/t32cb16blend.S
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
-
LOCAL_SHARED_LIBRARIES :=
LOCAL_C_INCLUDES :=
diff --git a/libpixelflinger/tests/arch-mips/Android.mk b/libpixelflinger/tests/arch-mips/Android.mk
new file mode 100644
index 0000000..fe6979e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/Android.mk
@@ -0,0 +1,6 @@
+ifeq ($(TARGET_ARCH),mips)
+include $(all-subdir-makefiles)
+endif
+ifeq ($(TARGET_ARCH),mipsel)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
new file mode 100644
index 0000000..40f197f
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ col32cb16blend_test.c \
+ ../../../arch-mips/col32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 32
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c
new file mode 100644
index 0000000..dd0e60f
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX 0xFFFFFFFF
+#define ARGB_8888_MIN 0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+ char name[256];
+ uint32_t src_color;
+ uint16_t dst_color;
+ size_t count;
+};
+
+struct test_t tests[] =
+{
+ {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+ {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+ {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+ {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+ {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+ {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+ {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+ {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+ {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+ {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_mips(uint16_t *dst, uint32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
+{
+ uint32_t srcAlpha = (src>>24);
+ uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
+
+ while (count--)
+ {
+ uint16_t d = *dst;
+ int dstR = (d>>11)&0x1f;
+ int dstG = (d>>5)&0x3f;
+ int dstB = (d)&0x1f;
+ int srcR = (src >> ( 3))&0x1F;
+ int srcG = (src >> ( 8+2))&0x3F;
+ int srcB = (src >> (16+3))&0x1F;
+ srcR += (f*dstR)>>8;
+ srcG += (f*dstG)>>8;
+ srcB += (f*dstB)>>8;
+ *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+ }
+}
+
+void scanline_col32cb16blend_test()
+{
+ uint16_t dst_c[16], dst_asm[16];
+ uint32_t i, j;
+
+ for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+ {
+ struct test_t test = tests[i];
+
+ printf("Testing - %s:",test.name);
+
+ memset(dst_c, 0, sizeof(dst_c));
+ memset(dst_asm, 0, sizeof(dst_asm));
+
+ for(j = 0; j < test.count; ++j)
+ {
+ dst_c[j] = test.dst_color;
+ dst_asm[j] = test.dst_color;
+ }
+
+
+ scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+ scanline_col32cb16blend_mips(dst_asm, test.src_color, test.count);
+
+ if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+
+ for(j = 0; j < test.count; ++j)
+ {
+ printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+ }
+ }
+}
+
+int main()
+{
+ scanline_col32cb16blend_test();
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
new file mode 100644
index 0000000..d0c0ae4
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ t32cb16blend_test.c \
+ ../../../arch-mips/t32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips-t32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 32
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c
new file mode 100644
index 0000000..c6d6937
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define ARGB_8888_MAX 0xFFFFFFFF
+#define ARGB_8888_MIN 0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+ char name[256];
+ uint32_t src_color;
+ uint16_t dst_color;
+ size_t count;
+};
+
+struct test_t tests[] =
+{
+ {"Count 0", 0, 0, 0},
+ {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+ {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+ {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+ {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+ {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+ {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+ {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+ {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+ {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+ {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+
+};
+
+void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
+void scanline_t32cb16blend_c(uint16_t * dst, uint32_t* src, size_t count)
+{
+ while (count--)
+ {
+ uint16_t d = *dst;
+ uint32_t s = *src++;
+ int dstR = (d>>11)&0x1f;
+ int dstG = (d>>5)&0x3f;
+ int dstB = (d)&0x1f;
+ int srcR = (s >> ( 3))&0x1F;
+ int srcG = (s >> ( 8+2))&0x3F;
+ int srcB = (s >> (16+3))&0x1F;
+ int srcAlpha = (s>>24) & 0xFF;
+
+
+ int f = 0x100 - (srcAlpha + ((srcAlpha>>7) & 0x1));
+ srcR += (f*dstR)>>8;
+ srcG += (f*dstG)>>8;
+ srcB += (f*dstB)>>8;
+ // srcR = srcR > 0x1F? 0x1F: srcR;
+ // srcG = srcG > 0x3F? 0x3F: srcG;
+ // srcB = srcB > 0x1F? 0x1F: srcB;
+ *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+ }
+}
+
+void scanline_t32cb16blend_test()
+{
+ uint16_t dst_c[16], dst_asm[16];
+ uint32_t src[16];
+ uint32_t i;
+ uint32_t j;
+
+ for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+ {
+ struct test_t test = tests[i];
+
+ printf("Testing - %s:",test.name);
+
+ memset(dst_c, 0, sizeof(dst_c));
+ memset(dst_asm, 0, sizeof(dst_asm));
+
+ for(j = 0; j < test.count; ++j)
+ {
+ dst_c[j] = test.dst_color;
+ dst_asm[j] = test.dst_color;
+ src[j] = test.src_color;
+ }
+
+ scanline_t32cb16blend_c(dst_c,src,test.count);
+ scanline_t32cb16blend_mips(dst_asm,src,test.count);
+
+
+ if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+
+ for(j = 0; j < test.count; ++j)
+ {
+ printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+ }
+ }
+}
+
+int main()
+{
+ scanline_t32cb16blend_test();
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/Android.mk b/libpixelflinger/tests/arch-mips64/Android.mk
new file mode 100644
index 0000000..3b1c64e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/Android.mk
@@ -0,0 +1,3 @@
+ifeq ($(TARGET_ARCH),mips64)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.mk b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
new file mode 100644
index 0000000..4699961
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ mips64_assembler_test.cpp\
+ asm_mips_test_jacket.S
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libpixelflinger
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/../../..
+
+LOCAL_MODULE:= test-pixelflinger-mips64-assembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
new file mode 100644
index 0000000..705ee9b
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
@@ -0,0 +1,93 @@
+# /*
+# * Copyright (C) 2015 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.
+# */
+
+ .text
+ .balign 8
+
+ .global asm_mips_test_jacket
+
+ # // Set the register
+ # // Calls the asm function
+ # // Reads the register values to output register
+
+ # // Parameters
+ # // a0 - Function to jump
+ # // a1 - register values array
+ # // a2 - flag values array
+asm_mips_test_jacket:
+ # // Save registers to stack
+ daddiu $sp, $sp, -96
+ sd $s0, 64($sp)
+ sd $s1, 72($sp)
+ sd $s2, 80($sp)
+ sd $ra, 88($sp)
+
+ move $s0, $a0
+ move $s1, $a1
+ move $s2, $a2
+
+ ld $v0, 16($s1)
+ ld $v1, 24($s1)
+ ld $a0, 32($s1)
+ ld $a1, 40($s1)
+ ld $a2, 48($s1)
+ ld $a3, 56($s1)
+ ld $a4, 64($s1)
+ ld $a5, 72($s1)
+ ld $a6, 80($s1)
+ ld $a7, 88($s1)
+ ld $t0, 96($s1)
+ ld $t1, 104($s1)
+ ld $t2, 112($s1)
+ ld $t3, 120($s1)
+
+ jal $s0
+
+ sd $v0, 16($s1)
+ sd $v1, 24($s1)
+ sd $a0, 32($s1)
+ sd $a1, 40($s1)
+ sd $a2, 48($s1)
+ sd $a3, 56($s1)
+ sd $a4, 64($s1)
+ sd $a5, 72($s1)
+ sd $a6, 80($s1)
+ sd $a7, 88($s1)
+ sd $t0, 96($s1)
+ sd $t1, 104($s1)
+ sd $t2, 112($s1)
+ sd $t3, 120($s1)
+
+ ld $s0, 64($sp)
+ ld $s1, 72($sp)
+ ld $s2, 80($sp)
+ ld $ra, 88($sp)
+
+ daddiu $sp, $sp, 96
+
+ j $ra
diff --git a/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp
new file mode 100644
index 0000000..9fb0a28
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) 2015 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 <errno.h>
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <cutils/ashmem.h>
+
+#include "codeflinger/ARMAssemblerInterface.h"
+#include "codeflinger/MIPS64Assembler.h"
+
+using namespace android;
+
+#define TESTS_DATAOP_ENABLE 1
+#define TESTS_DATATRANSFER_ENABLE 1
+#define ASSEMBLY_SCRATCH_SIZE 4096
+
+void *instrMem;
+uint32_t instrMemSize = 128 * 1024;
+char dataMem[8192];
+
+typedef void (*asm_function_t)();
+extern "C" void asm_mips_test_jacket(asm_function_t function,
+ int64_t regs[], int32_t flags[]);
+
+#define MAX_32BIT (uint32_t)(((uint64_t)1 << 32) - 1)
+#define MAX_64BIT ((uint64_t)0xFFFFFFFFFFFFFFFF)
+const uint32_t NA = 0;
+const uint32_t NUM_REGS = 32;
+const uint32_t NUM_FLAGS = 16;
+
+enum instr_t
+{
+ INSTR_ADD,
+ INSTR_SUB,
+ INSTR_AND,
+ INSTR_ORR,
+ INSTR_RSB,
+ INSTR_BIC,
+ INSTR_CMP,
+ INSTR_MOV,
+ INSTR_MVN,
+ INSTR_MUL,
+ INSTR_MLA,
+ INSTR_SMULBB,
+ INSTR_SMULBT,
+ INSTR_SMULTB,
+ INSTR_SMULTT,
+ INSTR_SMULWB,
+ INSTR_SMULWT,
+ INSTR_SMLABB,
+ INSTR_UXTB16,
+ INSTR_UBFX,
+ INSTR_ADDR_ADD,
+ INSTR_ADDR_SUB,
+ INSTR_LDR,
+ INSTR_LDRB,
+ INSTR_LDRH,
+ INSTR_ADDR_LDR,
+ INSTR_LDM,
+ INSTR_STR,
+ INSTR_STRB,
+ INSTR_STRH,
+ INSTR_ADDR_STR,
+ INSTR_STM
+};
+
+enum shift_t
+{
+ SHIFT_LSL,
+ SHIFT_LSR,
+ SHIFT_ASR,
+ SHIFT_ROR,
+ SHIFT_NONE
+};
+
+enum offset_t
+{
+ REG_SCALE_OFFSET,
+ REG_OFFSET,
+ IMM8_OFFSET,
+ IMM12_OFFSET,
+ NO_OFFSET
+};
+
+enum cond_t
+{
+ EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
+ HS = CS,
+ LO = CC
+};
+
+const char * cc_code[] =
+{
+ "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
+ "HI", "LS","GE","LT", "GT", "LE", "AL", "NV"
+};
+
+struct condTest_t
+{
+ int mode;
+ int32_t Rcond1;
+ int32_t Rcond2;
+ uint64_t Rcond1Value;
+ uint64_t Rcond2Value;
+};
+
+
+struct dataOpTest_t
+{
+ uint32_t id;
+ instr_t op;
+ condTest_t preCond;
+ cond_t cond;
+ bool setFlags;
+ uint64_t RnValue;
+ uint64_t RsValue;
+ bool immediate;
+ uint32_t immValue;
+ uint64_t RmValue;
+ uint32_t shiftMode;
+ uint32_t shiftAmount;
+ uint64_t RdValue;
+ bool checkRd;
+ uint64_t postRdValue;
+};
+
+struct dataTransferTest_t
+{
+ uint32_t id;
+ instr_t op;
+ uint32_t preFlag;
+ cond_t cond;
+ bool setMem;
+ uint64_t memOffset;
+ uint64_t memValue;
+ uint64_t RnValue;
+ offset_t offsetType;
+ uint64_t RmValue;
+ uint32_t immValue;
+ bool writeBack;
+ bool preIndex;
+ bool postIndex;
+ uint64_t RdValue;
+ uint64_t postRdValue;
+ uint64_t postRnValue;
+ bool checkMem;
+ uint64_t postMemOffset;
+ uint32_t postMemLength;
+ uint64_t postMemValue;
+};
+
+
+dataOpTest_t dataOpTests [] =
+{
+ {0xA000,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
+ {0xA001,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,MAX_64BIT},
+ {0xA002,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,NA,NA,NA,1,0},
+ {0xA003,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT-1,NA,NA,NA,1,MAX_64BIT},
+ {0xA004,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL, 0,NA,1,0},
+ {0xA005,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
+ {0xA006,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,2},
+ {0xA007,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,2},
+ {0xA008,INSTR_ADD,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+ {0xA009,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
+ {0xA010,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,0,0,0,NA,1,1},
+ {0xA011,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,0,0,0,NA,1,0},
+ {0xA012,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,1},
+ {0xA013,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,0},
+ {0xA014,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,1},
+ {0xA015,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0},
+ {0xA016,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+ {0xA017,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+ {0xA018,INSTR_AND,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,0},
+ {0xA019,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_ASR,31,NA,1,1},
+ {0xA020,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,1,MAX_32BIT,0,0,0,NA,1,MAX_64BIT},
+ {0xA021,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,1,MAX_32BIT-1,0,0,0,NA,1,MAX_64BIT-1},
+ {0xA022,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,0,0,MAX_32BIT,0,0,NA,1,MAX_64BIT},
+ {0xA023,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,0,0,MAX_32BIT-1,0,0,NA,1,MAX_64BIT-1},
+ {0xA024,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,MAX_64BIT},
+ {0xA025,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
+ {0xA026,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+ {0xA027,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+ {0xA028,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+ {0xA029,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,MAX_64BIT},
+ {0xA030,INSTR_CMP,{0,0,0,0,0},AL,1,0x10000,NA,1,0x10000,0,0,0,NA,0,0},
+ {0xA031,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x10000,0,0,0x10000,0,0,NA,1,0},
+ {0xA032,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x1000,0,0,0x10000,0,0,NA,1,0x10000000},
+ {0xA033,INSTR_MUL,{0,0,0,0,0},AL,0,0,MAX_32BIT,0,0,1,0,0,NA,1,MAX_64BIT},
+ {0xA034,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x10000,0,0,0x10000,0,0,NA,1,0x10000},
+ {0xA035,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x1000,0,0,0x10000,0,0,NA,1,0x10010000},
+ {0xA036,INSTR_SUB,{1,R_v1,R_a6,2,4},MI,0,2,NA,0,NA,1,NA,NA,2,1,1},
+ {0xA037,INSTR_SUB,{2,R_v1,R_a6,2,0},MI,0,2,NA,0,NA,1,NA,NA,2,1,2},
+ {0xA038,INSTR_SUB,{1,R_v1,R_a6,4,2},GE,0,2,NA,1,1,NA,NA,NA,2,1,1},
+ {0xA039,INSTR_SUB,{1,R_a5,R_a6,2,7},GE,0,2,NA,1,1,NA,NA,NA,2,1,2},
+ {0xA040,INSTR_SUB,{1,R_a5,R_a6,1,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,1},
+ {0xA041,INSTR_SUB,{1,R_a5,R_a6,0,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,2},
+ {0xA042,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1<< 16,0,0,0,NA,1,UINT64_C(1) -(1<<16)},
+ {0xA043,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,0,0,0,NA,1,MAX_64BIT-1},
+ {0xA044,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1,0,0,0,NA,1,0},
+ {0xA045,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,UINT64_C(1) -(1<<16)},
+ {0xA046,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,MAX_64BIT-1},
+ {0xA047,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
+ {0xA048,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,UINT64_C(1) -(1<<16)},
+ {0xA049,INSTR_SUB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT,SHIFT_LSL,31,NA,1,1},
+ {0xA050,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
+ {0xA051,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
+ {0xA052,INSTR_RSB,{1,R_a5,R_a6,4,1},GE,0,2,NA,1,0,NA,NA,NA,2,1,UINT64_C(-2)},
+ {0xA053,INSTR_RSB,{1,R_a5,R_a6,UINT64_C(-1),1},GE,0,2,NA,1,0,NA,NA,NA,2,1,2},
+ {0xA054,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1<<16,NA,NA,NA,NA,1,(1<<16)-1},
+ {0xA055,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,NA,NA,NA,NA,1,UINT64_C(1)-MAX_64BIT},
+ {0xA056,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1,NA,NA,NA,NA,1,0},
+ {0xA057,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,(1<<16)-1},
+ {0xA058,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,UINT64_C(1)-MAX_64BIT},
+ {0xA059,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
+ {0xA060,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(1<<16)-1},
+ {0xA061,INSTR_RSB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,UINT64_C(-1)},
+ {0xA062,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
+ {0xA063,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
+ {0xA064,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,1,0x80000001,NA,NA,NA,NA,1,0xFFFFFFFF80000001},
+ {0xA065,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,0,0,NA,1,0xFFFFFFFF80000001},
+ {0xA066,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,NA,1,MAX_64BIT-1},
+ {0xA067,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000000},
+ {0xA068,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+ {0xA069,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+ {0xA070,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+ {0xA071,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_64BIT ,SHIFT_ASR,31,NA,1,MAX_64BIT},
+ {0xA072,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ROR,1,NA,1,0xFFFFFFFF80000001},
+ {0xA073,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,SHIFT_ROR,31,NA,1,3},
+ {0xA074,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,MAX_64BIT -1,SHIFT_ASR,1,NA,1,MAX_64BIT},
+ {0xA075,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+ {0xA076,INSTR_MOV,{2,R_a5,R_a6,6,8},MI,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA077,INSTR_MOV,{2,R_a5,R_a6,UINT64_C(-4),UINT64_C(-8)},MI,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
+ {0xA078,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-1)},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA079,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+ {0xA080,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-5)},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
+ {0xA081,INSTR_MOV,{1,R_a5,R_a6,5,5},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
+ {0xA082,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,2},
+ {0xA083,INSTR_MOV,{1,R_a5,R_a6,4,1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
+ {0xA084,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-1)},LE,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+ {0xA085,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
+ {0xA086,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA087,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-3)},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+ {0xA088,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),0},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA089,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-1)},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
+ {0xA090,INSTR_MOV,{1,R_a5,R_a6,6,1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
+ {0xA091,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
+ {0xA092,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
+ {0xA093,INSTR_MOV,{1,R_a5,R_a6,4,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
+ {0xA094,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2},
+ {0xA095,INSTR_MOV,{1,R_a5,R_a6,1,UINT64_C(-1)},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA096,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+ {0xA097,INSTR_MVN,{1,R_a5,R_a6,1,4},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,2},
+ {0xA098,INSTR_MVN,{1,R_a5,R_a6,UINT64_C(-1),1},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,1},
+ {0xA099,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,1,0,NA,NA,NA,2,1,MAX_64BIT},
+ {0xA100,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,MAX_32BIT-1,NA,0,2,1,1},
+ {0xA101,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,0x80000001,NA,0,2,1,0x7FFFFFFE},
+ {0xA102,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
+ {0xA103,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,1},
+ {0xA104,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,0},
+ {0xA105,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,1},
+ {0xA106,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,3,SHIFT_ASR,1,NA,1,0xF0},
+ {0xA107,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
+ {0xA108,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA109,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
+ {0xA110,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA111,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
+ {0xA112,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA113,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
+ {0xA114,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA115,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
+ {0xA116,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA117,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+ {0xA118,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA119,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
+ {0xA120,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA121,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+ {0xA122,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA123,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
+ {0xA124,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+ {0xA125,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+ {0xA126,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA127,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
+ {0xA128,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+ {0xA129,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+ {0xA130,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA131,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
+ {0xA132,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0},
+ {0xA133,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00001000},
+ {0xA134,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+ {0xA135,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,0},
+ {0xA136,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,0,NA,1,0x00CD0001},
+ {0xA137,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,1,NA,1,0x00AB00EF},
+ {0xA138,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,2,NA,1,0x000100CD},
+ {0xA139,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,3,NA,1,0x00EF00AB},
+ {0xA140,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,SHIFT_LSL,1,NA,1,0xD00000001},
+ {0xA141,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0x01,NA,0,NA,0x1,SHIFT_LSL,2,NA,1,0x5},
+ {0xA142,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,NA,0,NA,1,0xD00000000},
+ {0xA143,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xD00000001,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,0xCFFFFFFFF},
+ {0xA144,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x020000,SHIFT_LSR,15,NA,1,0xCFFFFFFFB},
+ {0xA145,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,3,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,1},
+};
+
+dataTransferTest_t dataTransferTests [] =
+{
+ {0xB000,INSTR_LDR,AL,AL,1,24,0xABCDEF0123456789,0,REG_SCALE_OFFSET,24,NA,NA,NA,NA,NA,0x23456789,0,0,NA,NA,NA},
+ {0xB001,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4,1,0,1,NA,0x23456789,4,0,NA,NA,NA},
+ {0xB002,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x23456789,0,0,NA,NA,NA},
+ {0xB003,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,REG_SCALE_OFFSET,4064,NA,NA,NA,NA,NA,0x89,0,0,NA,NA,NA},
+ {0xB004,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,0,0,1,0,NA,0x67,4065,0,NA,NA,NA},
+ {0xB005,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,0,1,0,NA,0x45,4065,0,NA,NA,NA},
+ {0xB006,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,2,0,1,0,NA,0x23,4065,0,NA,NA,NA},
+ {0xB007,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,1,0,1,NA,0x67,4066,0,NA,NA,NA},
+ {0xB008,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x89,0,0,NA,NA,NA},
+ {0xB009,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,IMM8_OFFSET,NA,2,1,0,1,NA,0x6789,2,0,NA,NA,NA},
+ {0xB010,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4064,0,0,1,0,NA,0x6789,0,0,NA,NA,NA},
+ {0xB011,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4066,0,0,1,0,NA,0x2345,0,0,NA,NA,NA},
+ {0xB012,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,0,0,0,0,NA,0x6789,0,0,NA,NA,NA},
+ {0xB013,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,2,NO_OFFSET,NA,0,0,0,0,NA,0x2345,2,0,NA,NA,NA},
+ {0xB014,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,8,1,2,8,0xDEAD23456789BEEF},
+ {0xB015,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,2,8,0xDEAD23456789BEEF},
+ {0xB016,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,0,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+ {0xB017,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,1,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDE89BEEF},
+ {0xB018,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,2,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEF89ADBEEF},
+ {0xB019,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,5,1,0,8,0xDEADBEEFDEAD89EF},
+ {0xB020,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+ {0xB021,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,IMM8_OFFSET,NA,2,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,4072,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB022,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+};
+
+
+void flushcache()
+{
+ const long base = long(instrMem);
+ const long curr = base + long(instrMemSize);
+ __builtin___clear_cache((char*)base, (char*)curr);
+}
+
+void dataOpTest(dataOpTest_t test, ArmToMips64Assembler *a64asm, uint32_t Rd = R_v1,
+ uint32_t Rn = R_t0, uint32_t Rm = R_t1, uint32_t Rs = R_t2)
+{
+ int64_t regs[NUM_REGS] = {0};
+ int32_t flags[NUM_FLAGS] = {0};
+ int64_t savedRegs[NUM_REGS] = {0};
+ uint32_t i;
+ uint32_t op2;
+
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ regs[i] = i;
+ }
+
+ regs[Rd] = test.RdValue;
+ regs[Rn] = test.RnValue;
+ regs[Rs] = test.RsValue;
+ a64asm->reset();
+ if (test.preCond.mode) {
+ a64asm->set_condition(test.preCond.mode, test.preCond.Rcond1, test.preCond.Rcond2);
+ regs[test.preCond.Rcond1] = test.preCond.Rcond1Value;
+ regs[test.preCond.Rcond2] = test.preCond.Rcond2Value;
+ }
+ a64asm->prolog();
+ if(test.immediate == true)
+ {
+ op2 = a64asm->imm(test.immValue);
+ }
+ else if(test.immediate == false && test.shiftAmount == 0)
+ {
+ op2 = Rm;
+ regs[Rm] = (int64_t)((int32_t)(test.RmValue));
+ }
+ else
+ {
+ op2 = a64asm->reg_imm(Rm, test.shiftMode, test.shiftAmount);
+ regs[Rm] = (int64_t)((int32_t)(test.RmValue));
+ }
+ switch(test.op)
+ {
+ case INSTR_ADD: a64asm->ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_SUB: a64asm->SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_RSB: a64asm->RSB(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_AND: a64asm->AND(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_ORR: a64asm->ORR(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_BIC: a64asm->BIC(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_MUL: a64asm->MUL(test.cond, test.setFlags, Rd,Rm,Rs); break;
+ case INSTR_MLA: a64asm->MLA(test.cond, test.setFlags, Rd,Rm,Rs,Rn); break;
+ case INSTR_CMP: a64asm->CMP(test.cond, Rn,op2); break;
+ case INSTR_MOV: a64asm->MOV(test.cond, test.setFlags,Rd,op2); break;
+ case INSTR_MVN: a64asm->MVN(test.cond, test.setFlags,Rd,op2); break;
+ case INSTR_SMULBB:a64asm->SMULBB(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULBT:a64asm->SMULBT(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULTB:a64asm->SMULTB(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULTT:a64asm->SMULTT(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULWB:a64asm->SMULWB(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULWT:a64asm->SMULWT(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMLABB:a64asm->SMLABB(test.cond, Rd,Rm,Rs,Rn); break;
+ case INSTR_UXTB16:a64asm->UXTB16(test.cond, Rd,Rm,test.shiftAmount); break;
+ case INSTR_ADDR_ADD: a64asm->ADDR_ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_ADDR_SUB: a64asm->ADDR_SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+ default: printf("Error"); return;
+ }
+ a64asm->epilog(0);
+ a64asm->fix_branches();
+ flushcache();
+
+ asm_function_t asm_function = (asm_function_t)(instrMem);
+
+ for(i = 0; i < NUM_REGS; ++i)
+ savedRegs[i] = regs[i];
+
+ asm_mips_test_jacket(asm_function, regs, flags);
+
+ /* Check if all regs except Rd is same */
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ if((i == Rd) || i == 2) continue;
+ if(regs[i] != savedRegs[i])
+ {
+ printf("Test %x failed Reg(%d) tampered Expected(0x%" PRIx64 "),"
+ "Actual(0x%" PRIx64 ") t\n", test.id, i, savedRegs[i],
+ regs[i]);
+ exit(0);
+ return;
+ }
+ }
+
+ if(test.checkRd == 1 && regs[Rd] != test.postRdValue)
+ {
+ printf("Test %x failed, Expected(%" PRIx64 "), Actual(%" PRIx64 ")\n",
+ test.id, test.postRdValue, regs[Rd]);
+ exit(0);
+ }
+ else
+ {
+ printf("Test %x passed\n", test.id);
+ }
+}
+
+
+void dataTransferTest(dataTransferTest_t test, ARMAssemblerInterface *a64asm,
+ uint32_t Rd = R_v1, uint32_t Rn = R_t0,uint32_t Rm = R_t1)
+{
+ int64_t regs[NUM_REGS] = {0};
+ int64_t savedRegs[NUM_REGS] = {0};
+ int32_t flags[NUM_FLAGS] = {0};
+ uint32_t i;
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ regs[i] = i;
+ }
+
+ uint32_t op2;
+
+ regs[Rd] = test.RdValue;
+ regs[Rn] = (uint64_t)(&dataMem[test.RnValue]);
+ regs[Rm] = test.RmValue;
+ flags[test.preFlag] = 1;
+
+ if(test.setMem == true)
+ {
+ unsigned char *mem = (unsigned char *)&dataMem[test.memOffset];
+ uint64_t value = test.memValue;
+ for(int j = 0; j < 8; ++j)
+ {
+ mem[j] = value & 0x00FF;
+ value >>= 8;
+ }
+ }
+ a64asm->reset();
+ a64asm->prolog();
+ if(test.offsetType == REG_SCALE_OFFSET)
+ {
+ op2 = a64asm->reg_scale_pre(Rm);
+ }
+ else if(test.offsetType == REG_OFFSET)
+ {
+ op2 = a64asm->reg_pre(Rm);
+ }
+ else if(test.offsetType == IMM12_OFFSET && test.preIndex == true)
+ {
+ op2 = a64asm->immed12_pre(test.immValue, test.writeBack);
+ }
+ else if(test.offsetType == IMM12_OFFSET && test.postIndex == true)
+ {
+ op2 = a64asm->immed12_post(test.immValue);
+ }
+ else if(test.offsetType == IMM8_OFFSET && test.preIndex == true)
+ {
+ op2 = a64asm->immed8_pre(test.immValue, test.writeBack);
+ }
+ else if(test.offsetType == IMM8_OFFSET && test.postIndex == true)
+ {
+ op2 = a64asm->immed8_post(test.immValue);
+ }
+ else if(test.offsetType == NO_OFFSET)
+ {
+ op2 = a64asm->__immed12_pre(0);
+ }
+ else
+ {
+ printf("Error - Unknown offset\n"); return;
+ }
+
+ switch(test.op)
+ {
+ case INSTR_LDR: a64asm->LDR(test.cond, Rd,Rn,op2); break;
+ case INSTR_LDRB: a64asm->LDRB(test.cond, Rd,Rn,op2); break;
+ case INSTR_LDRH: a64asm->LDRH(test.cond, Rd,Rn,op2); break;
+ case INSTR_ADDR_LDR: a64asm->ADDR_LDR(test.cond, Rd,Rn,op2); break;
+ case INSTR_STR: a64asm->STR(test.cond, Rd,Rn,op2); break;
+ case INSTR_STRB: a64asm->STRB(test.cond, Rd,Rn,op2); break;
+ case INSTR_STRH: a64asm->STRH(test.cond, Rd,Rn,op2); break;
+ case INSTR_ADDR_STR: a64asm->ADDR_STR(test.cond, Rd,Rn,op2); break;
+ default: printf("Error"); return;
+ }
+ a64asm->epilog(0);
+ flushcache();
+
+ asm_function_t asm_function = (asm_function_t)(instrMem);
+
+ for(i = 0; i < NUM_REGS; ++i)
+ savedRegs[i] = regs[i];
+
+ asm_mips_test_jacket(asm_function, regs, flags);
+
+ /* Check if all regs except Rd/Rn are same */
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ if(i == Rd || i == Rn || i == R_v0) continue;
+
+ if(regs[i] != savedRegs[i])
+ {
+ printf("Test %x failed Reg(%d) tampered"
+ " Expected(0x%" PRIx64 "), Actual(0x%" PRIx64 ") t\n",
+ test.id, i, savedRegs[i], regs[i]);
+ return;
+ }
+ }
+
+ if((uint64_t)regs[Rd] != test.postRdValue)
+ {
+ printf("Test %x failed, "
+ "Expected in Rd(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+ test.id, test.postRdValue, regs[Rd]);
+ }
+ else if((uint64_t)regs[Rn] != (uint64_t)(&dataMem[test.postRnValue]))
+ {
+ printf("Test %x failed, "
+ "Expected in Rn(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+ test.id, test.postRnValue, regs[Rn] - (uint64_t)dataMem);
+ }
+ else if(test.checkMem == true)
+ {
+ unsigned char *addr = (unsigned char *)&dataMem[test.postMemOffset];
+ uint64_t value;
+ value = 0;
+ for(uint32_t j = 0; j < test.postMemLength; ++j)
+ value = (value << 8) | addr[test.postMemLength-j-1];
+ if(value != test.postMemValue)
+ {
+ printf("Test %x failed, "
+ "Expected in Mem(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+ test.id, test.postMemValue, value);
+ }
+ else
+ {
+ printf("Test %x passed\n", test.id);
+ }
+ }
+ else
+ {
+ printf("Test %x passed\n", test.id);
+ }
+}
+
+int main(void)
+{
+ uint32_t i;
+
+ /* Allocate memory to store instructions generated by ArmToArm64Assembler */
+ {
+ int fd = ashmem_create_region("code cache", instrMemSize);
+ if(fd < 0) {
+ printf("IF < 0\n");
+ printf("Creating code cache, ashmem_create_region "
+ "failed with error '%s'", strerror(errno));
+ }
+ instrMem = mmap(NULL, instrMemSize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE, fd, 0);
+ }
+
+ ArmToMips64Assembler a64asm(instrMem);
+
+ if(TESTS_DATAOP_ENABLE)
+ {
+ printf("Running data processing tests\n");
+ for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i) {
+ dataOpTest(dataOpTests[i], &a64asm);
+ }
+ }
+
+ if(TESTS_DATATRANSFER_ENABLE)
+ {
+ printf("Running data transfer tests\n");
+ for(i = 0; i < sizeof(dataTransferTests)/sizeof(dataTransferTest_t); ++i)
+ dataTransferTest(dataTransferTests[i], &a64asm);
+ }
+
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
new file mode 100644
index 0000000..7d4177e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ col32cb16blend_test.c \
+ ../../../arch-mips64/col32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips64-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c
new file mode 100644
index 0000000..066eab6
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX 0xFFFFFFFF
+#define ARGB_8888_MIN 0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+ char name[256];
+ uint32_t src_color;
+ uint16_t dst_color;
+ size_t count;
+};
+
+struct test_t tests[] =
+{
+ {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+ {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+ {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+ {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+ {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+ {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+ {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+ {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+ {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+ {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
+{
+ uint32_t srcAlpha = (src>>24);
+ uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
+
+ while (count--)
+ {
+ uint16_t d = *dst;
+ int dstR = (d>>11)&0x1f;
+ int dstG = (d>>5)&0x3f;
+ int dstB = (d)&0x1f;
+ int srcR = (src >> ( 3))&0x1F;
+ int srcG = (src >> ( 8+2))&0x3F;
+ int srcB = (src >> (16+3))&0x1F;
+ srcR += (f*dstR)>>8;
+ srcG += (f*dstG)>>8;
+ srcB += (f*dstB)>>8;
+ *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+ }
+}
+
+void scanline_col32cb16blend_test()
+{
+ uint16_t dst_c[16], dst_asm[16];
+ uint32_t i, j;
+
+ for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+ {
+ struct test_t test = tests[i];
+
+ printf("Testing - %s:",test.name);
+
+ memset(dst_c, 0, sizeof(dst_c));
+ memset(dst_asm, 0, sizeof(dst_asm));
+
+ for(j = 0; j < test.count; ++j)
+ {
+ dst_c[j] = test.dst_color;
+ dst_asm[j] = test.dst_color;
+ }
+
+
+ scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+ scanline_col32cb16blend_mips64(dst_asm, test.src_color, test.count);
+
+ if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+
+ for(j = 0; j < test.count; ++j)
+ {
+ printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+ }
+ }
+}
+
+int main()
+{
+ scanline_col32cb16blend_test();
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
new file mode 100644
index 0000000..4e72b57
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ mips64_disassembler_test.cpp \
+ ../../../codeflinger/mips64_disassem.c
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips64-disassembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp
new file mode 100644
index 0000000..22efa9f
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2015 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 <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include "../../../codeflinger/mips64_disassem.h"
+
+//typedef uint64_t db_addr_t;
+//db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_format);
+
+struct test_table_entry_t
+{
+ uint32_t code;
+ const char *instr;
+};
+
+static test_table_entry_t test_table [] =
+{
+ { 0x00011020, "add\tv0,zero,at" },
+ { 0x00832820, "add\ta1,a0,v1" },
+ { 0x00c74020, "add\ta4,a2,a3" },
+ { 0x012a5820, "add\ta7,a5,a6" },
+ { 0x258dffff, "addiu\tt1,t0,-1" },
+ { 0x25cf0004, "addiu\tt3,t2,4" },
+ { 0x02119021, "addu\ts2,s0,s1" },
+ { 0x0274a821, "addu\ts5,s3,s4" },
+ { 0x02d7c024, "and\tt8,s6,s7" },
+ { 0x333aff00, "andi\tk0,t9,0xff00" },
+ { 0x3f7cffff, "aui\tgp,k1,-1" },
+ { 0x3c1dffff, "lui\tsp,0xffff" },
+ { 0x00e04051, "clo\ta4,a3" },
+ { 0x01205050, "clz\ta6,a5" },
+ { 0x016c682c, "dadd\tt1,a7,t0" },
+ { 0x65cf0008, "daddiu\tt3,t2,8" },
+ { 0x0211902d, "daddu\ts2,s0,s1" },
+ { 0x7e741403, "dext\ts4,s3,16,3" },
+ { 0x7eb6f801, "dextm\ts6,s5,0,64" },
+ { 0x7ef87c02, "dextu\tt8,s7,48,16" },
+ { 0x7f3a8207, "dins\tk0,t9,8,9" },
+ { 0x7f7c0005, "dinsm\tgp,k1,0,33" },
+ { 0x7fbe0806, "dinsu\ts8,sp,32,2" },
+ { 0x03e1102e, "dsub\tv0,ra,at" },
+ { 0x0064282f, "dsubu\ta1,v1,a0" },
+ { 0x7cc77a00, "ext\ta3,a2,8,16" },
+ { 0x7d09fc04, "ins\ta5,a4,16,16" },
+ { 0x00200009, "jr\tat" },
+ { 0x00201009, "jalr\tv0,at" },
+ { 0x0020f809, "jalr\tat" },
+ { 0x8082fff0, "lb\tv0,-16(a0)" },
+ { 0x916c0008, "lbu\tt0,8(a7)" },
+ { 0xdfa3ffe8, "ld\tv1,-24(sp)" },
+ { 0x84850080, "lh\ta1,128(a0)" },
+ { 0x94c7ff80, "lhu\ta3,-128(a2)" },
+ { 0x8d09000c, "lw\ta5,12(a4)" },
+ { 0x9d4bfff4, "lwu\ta7,-12(a6)" },
+ { 0x00620898, "mul\tat,v1,v0" },
+ { 0x006208d8, "muh\tat,v1,v0" },
+ { 0x00620899, "mulu\tat,v1,v0" },
+ { 0x006208d9, "muhu\tat,v1,v0" },
+ { 0x00000000, "nop" },
+ { 0x02329827, "nor\ts3,s1,s2" },
+ { 0x0295b025, "or\ts6,s4,s5" },
+ { 0x36f0ff00, "ori\ts0,s7,0xff00" },
+ { 0x7c03103b, "rdhwr\tv0,v1" },
+ { 0x00242a02, "rotr\ta1,a0,8" },
+ { 0x00c74046, "rotrv\ta4,a3,a2" },
+ { 0xa12afff0, "sb\ta6,-16(a5)" },
+ { 0xfd6c0100, "sd\tt0,256(a7)" },
+ { 0x7c0d7420, "seb\tt2,t1" },
+ { 0x7c0f8620, "seh\ts0,t3" },
+ { 0x02329835, "seleqz\ts3,s1,s2" },
+ { 0x0295b037, "selnez\ts6,s4,s5" },
+ { 0xa6f84000, "sh\tt8,16384(s7)" },
+ { 0x0019d100, "sll\tk0,t9,4" },
+ { 0x037ce804, "sllv\tsp,gp,k1" },
+ { 0x03df082a, "slt\tat,s8,ra" },
+ { 0x28430007, "slti\tv1,v0,7" },
+ { 0x2c850020, "sltiu\ta1,a0,32" },
+ { 0x00c7402b, "sltu\ta4,a2,a3" },
+ { 0x00095103, "sra\ta6,a5,4" },
+ { 0x016c6807, "srav\tt1,t0,a7" },
+ { 0x000e7a02, "srl\tt3,t2,8" },
+ { 0x02119006, "srlv\ts2,s1,s0" },
+ { 0x0274a822, "sub\ts5,s3,s4" },
+ { 0x02d7c023, "subu\tt8,s6,s7" },
+ { 0xaf3afffc, "sw\tk0,-4(t9)" },
+ { 0x7c1be0a0, "wsbh\tgp,k1" },
+ { 0x03bef826, "xor\tra,sp,s8" },
+ { 0x3801ffff, "li\tat,0xffff" },
+ { 0x3843ffff, "xori\tv1,v0,0xffff" },
+};
+
+struct test_branches_table_entry_t
+{
+ uint32_t code;
+ const char *instr;
+ int16_t offset;
+};
+
+static test_branches_table_entry_t test_branches_table [] = {
+ { 0x1000ffff, "b\t", static_cast<int16_t>(0xffff) },
+ { 0x13df0008, "beq\ts8,ra,", 0x8 },
+ { 0x042100ff, "bgez\tat,", 0xff },
+ { 0x1c40ff00, "bgtz\tv0,", static_cast<int16_t>(0xff00) },
+ { 0x18605555, "blez\tv1,", 0x5555 },
+ { 0x0480aaaa, "bltz\ta0,", static_cast<int16_t>(0xaaaa) },
+ { 0x14a68888, "bne\ta1,a2,", static_cast<int16_t>(0x8888) },
+};
+
+struct test_jump_table_entry_t
+{
+ uint32_t code;
+ const char *instr;
+ int32_t offset;
+};
+
+static test_jump_table_entry_t test_jump_table [] = {
+ { 0x0956ae66, "j\t", 0x156ae66 },
+ { 0x0d56ae66, "jal\t", 0x156ae66 },
+};
+
+int main()
+{
+ char instr[256];
+ uint32_t failed = 0;
+
+ for(uint32_t i = 0; i < sizeof(test_table)/sizeof(test_table_entry_t); ++i)
+ {
+ test_table_entry_t *test;
+ test = &test_table[i];
+ mips_disassem(&test->code, instr, 0);
+ if(strcmp(instr, test->instr) != 0)
+ {
+ printf("Test Failed \n"
+ "Code : 0x%0x\n"
+ "Expected : %s\n"
+ "Actual : %s\n", test->code, test->instr, instr);
+ failed++;
+ }
+ }
+ for(uint32_t i = 0; i < sizeof(test_branches_table)/sizeof(test_branches_table_entry_t); ++i)
+ {
+ test_branches_table_entry_t *test;
+ test = &test_branches_table[i];
+ mips_disassem(&test->code, instr, 0);
+ //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
+ uint64_t loc = (uint64_t)test + 4 + (test->offset << 2);
+ //printf("DBG loc: %lx\n", loc);
+ char temp[256], address[16];
+ strcpy(temp, test->instr);
+ sprintf(address, "0x%lx", loc);
+ strcat(temp, address);
+ if(strcmp(instr, temp) != 0)
+ {
+ printf("Test Failed \n"
+ "Code : 0x%0x\n"
+ "Expected : %s\n"
+ "Actual : %s\n", test->code, temp, instr);
+ failed++;
+ }
+ }
+ for(uint32_t i = 0; i < sizeof(test_jump_table)/sizeof(test_jump_table_entry_t); ++i)
+ {
+ test_jump_table_entry_t *test;
+ test = &test_jump_table[i];
+ mips_disassem(&test->code, instr, 0);
+ //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
+ uint64_t loc = ((uint64_t)test & 0xfffffffff0000000) | (test->offset << 2);
+ //printf("DBG loc: %lx\n", loc);
+ char temp[256], address[16];
+ strcpy(temp, test->instr);
+ sprintf(address, "0x%08lx", loc);
+ strcat(temp, address);
+ if(strcmp(instr, temp) != 0)
+ {
+ printf("Test Failed \n"
+ "Code : 0x%0x\n"
+ "Expected : '%s'\n"
+ "Actual : '%s'\n", test->code, temp, instr);
+ failed++;
+ }
+ }
+ if(failed == 0)
+ {
+ printf("All tests PASSED\n");
+ return 0;
+ }
+ else
+ {
+ printf("%d tests FAILED\n", failed);
+ return -1;
+ }
+}
diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp
index 148b6f4..efa6d87 100644
--- a/libpixelflinger/tests/codegen/codegen.cpp
+++ b/libpixelflinger/tests/codegen/codegen.cpp
@@ -11,16 +11,18 @@
#include "codeflinger/ARMAssembler.h"
#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
#include "codeflinger/MIPSAssembler.h"
+#elif defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6
+#include "codeflinger/MIPS64Assembler.h"
#endif
#include "codeflinger/Arm64Assembler.h"
-#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6))) || defined(__aarch64__)
# define ANDROID_ARM_CODEGEN 1
#else
# define ANDROID_ARM_CODEGEN 0
#endif
-#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6))
#define ASSEMBLY_SCRATCH_SIZE 4096
#elif defined(__aarch64__)
#define ASSEMBLY_SCRATCH_SIZE 8192
@@ -58,6 +60,10 @@
GGLAssembler assembler( new ArmToMipsAssembler(a) );
#endif
+#if defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6
+ GGLAssembler assembler( new ArmToMips64Assembler(a) );
+#endif
+
#if defined(__aarch64__)
GGLAssembler assembler( new ArmToArm64Assembler(a) );
#endif
diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp
index 80efeff..234bfdd 100644
--- a/libpixelflinger/trap.cpp
+++ b/libpixelflinger/trap.cpp
@@ -2,29 +2,31 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
+#define LOG_TAG "pixelflinger-trap"
+
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
+#include <cutils/memory.h>
+#include <log/log.h>
+
#include "trap.h"
#include "picker.h"
-#include <cutils/log.h>
-#include <cutils/memory.h>
-
namespace android {
// ----------------------------------------------------------------------------
@@ -563,10 +565,10 @@
c->init_y(c, miny);
for (int32_t y = miny; y < maxy; y++) {
- register int32_t ex0 = ey0;
- register int32_t ex1 = ey1;
- register int32_t ex2 = ey2;
- register int32_t xl, xr;
+ int32_t ex0 = ey0;
+ int32_t ex1 = ey1;
+ int32_t ex2 = ey2;
+ int32_t xl, xr;
for (xl=minx ; xl<maxx ; xl++) {
if (ex0>0 && ex1>0 && ex2>0)
break; // all strictly positive
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
index ee6ba58..0bfc391 100644
--- a/libprocessgroup/Android.mk
+++ b/libprocessgroup/Android.mk
@@ -3,18 +3,17 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := processgroup.cpp
LOCAL_MODULE := libprocessgroup
-LOCAL_SHARED_LIBRARIES := liblog libutils
+LOCAL_STATIC_LIBRARIES := libbase
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Wall -Werror
-LOCAL_REQUIRED_MODULE := processgroup_cleanup
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := cleanup.cpp
-LOCAL_MODULE := processgroup_cleanup
+LOCAL_SRC_FILES := processgroup.cpp
+LOCAL_MODULE := libprocessgroup
+LOCAL_SHARED_LIBRARIES := libbase
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Wall -Werror
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_STATIC_LIBRARIES := libc libcutils
-include $(BUILD_EXECUTABLE)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libprocessgroup/cleanup.cpp b/libprocessgroup/cleanup.cpp
deleted file mode 100644
index cca8dc4..0000000
--- a/libprocessgroup/cleanup.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2014 Google, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 <string.h>
-#include <unistd.h>
-#include <sys/syslimits.h>
-
-#include "processgroup_priv.h"
-
-int main(int argc, char **argv)
-{
- char buf[PATH_MAX];
- if (argc != 2)
- return -1;
-
- memcpy(buf, PROCESSGROUP_CGROUP_PATH, sizeof(PROCESSGROUP_CGROUP_PATH));
- strlcat(buf, argv[1], sizeof(buf));
- return rmdir(buf);
-}
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index a80965f..eb66727 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -22,20 +22,47 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <log/log.h>
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include <android-base/logging.h>
#include <private/android_filesystem_config.h>
-#include <utils/SystemClock.h>
-
#include <processgroup/processgroup.h>
-#include "processgroup_priv.h"
+
+using namespace std::chrono_literals;
+
+// Uncomment line below use memory cgroups for keeping track of (forked) PIDs
+// #define USE_MEMCG 1
+
+#define MEM_CGROUP_PATH "/dev/memcg/apps"
+#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
+#define ACCT_CGROUP_PATH "/acct"
+
+#define PROCESSGROUP_UID_PREFIX "uid_"
+#define PROCESSGROUP_PID_PREFIX "pid_"
+#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
+#define PROCESSGROUP_MAX_UID_LEN 11
+#define PROCESSGROUP_MAX_PID_LEN 11
+#define PROCESSGROUP_MAX_PATH_LEN \
+ ((sizeof(MEM_CGROUP_PATH) > sizeof(ACCT_CGROUP_PATH) ? \
+ sizeof(MEM_CGROUP_PATH) : sizeof(ACCT_CGROUP_PATH)) + \
+ sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
+ PROCESSGROUP_MAX_UID_LEN + \
+ sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
+ PROCESSGROUP_MAX_PID_LEN + \
+ sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
+ 1)
+
+std::once_flag init_path_flag;
struct ctx {
bool initialized;
@@ -45,10 +72,25 @@
size_t buf_len;
};
+static const char* getCgroupRootPath() {
+#ifdef USE_MEMCG
+ static const char* cgroup_root_path = NULL;
+ std::call_once(init_path_flag, [&]() {
+ // 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;
+ });
+ return cgroup_root_path;
+#else
+ return ACCT_CGROUP_PATH;
+#endif
+}
+
static int convertUidToPath(char *path, size_t size, uid_t uid)
{
return snprintf(path, size, "%s/%s%d",
- PROCESSGROUP_CGROUP_PATH,
+ getCgroupRootPath(),
PROCESSGROUP_UID_PREFIX,
uid);
}
@@ -56,7 +98,7 @@
static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
{
return snprintf(path, size, "%s/%s%d/%s%d",
- PROCESSGROUP_CGROUP_PATH,
+ getCgroupRootPath(),
PROCESSGROUP_UID_PREFIX,
uid,
PROCESSGROUP_PID_PREFIX,
@@ -73,7 +115,7 @@
int fd = open(path, O_RDONLY);
if (fd < 0) {
ret = -errno;
- SLOGW("failed to open %s: %s", path, strerror(errno));
+ PLOG(WARNING) << "failed to open " << path;
return ret;
}
@@ -82,7 +124,7 @@
ctx->buf_len = 0;
ctx->initialized = true;
- SLOGV("Initialized context for %s", path);
+ LOG(VERBOSE) << "Initialized context for " << path;
return 0;
}
@@ -102,7 +144,7 @@
ctx->buf_len += ret;
ctx->buf[ctx->buf_len] = 0;
- SLOGV("Read %zd to buffer: %s", ret, ctx->buf);
+ LOG(VERBOSE) << "Read " << ret << " to buffer: " << ctx->buf;
assert(ctx->buf_len <= sizeof(ctx->buf));
@@ -162,11 +204,10 @@
static void removeUidProcessGroups(const char *uid_path)
{
- DIR *uid = opendir(uid_path);
+ std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path), closedir);
if (uid != NULL) {
- struct dirent cur;
- struct dirent *dir;
- while ((readdir_r(uid, &cur, &dir) == 0) && dir) {
+ dirent* dir;
+ while ((dir = readdir(uid.get())) != nullptr) {
char path[PROCESSGROUP_MAX_PATH_LEN];
if (dir->d_type != DT_DIR) {
@@ -178,23 +219,22 @@
}
snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
- SLOGV("removing %s\n", path);
- rmdir(path);
+ LOG(VERBOSE) << "removing " << path;
+ if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
}
- closedir(uid);
}
}
void removeAllProcessGroups()
{
- SLOGV("removeAllProcessGroups()");
- DIR *root = opendir(PROCESSGROUP_CGROUP_PATH);
+ LOG(VERBOSE) << "removeAllProcessGroups()";
+ const char* cgroup_root_path = getCgroupRootPath();
+ std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
if (root == NULL) {
- SLOGE("failed to open %s: %s", PROCESSGROUP_CGROUP_PATH, strerror(errno));
+ PLOG(ERROR) << "failed to open " << cgroup_root_path;
} else {
- struct dirent cur;
- struct dirent *dir;
- while ((readdir_r(root, &cur, &dir) == 0) && dir) {
+ dirent* dir;
+ while ((dir = readdir(root.get())) != nullptr) {
char path[PROCESSGROUP_MAX_PATH_LEN];
if (dir->d_type != DT_DIR) {
@@ -204,12 +244,11 @@
continue;
}
- snprintf(path, sizeof(path), "%s/%s", PROCESSGROUP_CGROUP_PATH, dir->d_name);
+ snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
removeUidProcessGroups(path);
- SLOGV("removing %s\n", path);
- rmdir(path);
+ LOG(VERBOSE) << "removing " << path;
+ if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
}
- closedir(root);
}
}
@@ -226,19 +265,13 @@
if (pid == 0) {
// Should never happen... but if it does, trying to kill this
// will boomerang right back and kill us! Let's not let that happen.
- SLOGW("Yikes, we've been told to kill pid 0! How about we don't do that.");
+ LOG(WARNING) << "Yikes, we've been told to kill pid 0! How about we don't do that?";
continue;
}
- if (pid != initialPid) {
- // We want to be noisy about killing processes so we can understand
- // what is going on in the log; however, don't be noisy about the base
- // process, since that it something we always kill, and we have already
- // logged elsewhere about killing it.
- SLOGI("Killing pid %d in uid %d as part of process group %d", pid, uid, initialPid);
- }
- int ret = kill(pid, signal);
- if (ret == -1) {
- SLOGW("failed to kill pid %d: %s", pid, strerror(errno));
+ LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid
+ << " as part of process group " << initialPid;
+ if (kill(pid, signal) == -1) {
+ PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
}
}
@@ -251,24 +284,27 @@
int killProcessGroup(uid_t uid, int initialPid, int signal)
{
- int processes;
- int sleep_us = 100;
- int64_t startTime = android::uptimeMillis();
+ std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
+ int retry = 40;
+ int processes;
while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
- SLOGV("killed %d processes for processgroup %d\n", processes, initialPid);
- if (sleep_us < 128000) {
- usleep(sleep_us);
- sleep_us *= 2;
+ LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
+ if (retry > 0) {
+ std::this_thread::sleep_for(5ms);
+ --retry;
} else {
- SLOGE("failed to kill %d processes for processgroup %d\n",
- processes, initialPid);
+ LOG(ERROR) << "failed to kill " << processes << " processes for processgroup "
+ << initialPid;
break;
}
}
- SLOGV("Killed process group uid %d pid %d in %" PRId64 "ms, %d procs remain", uid, initialPid,
- android::uptimeMillis()-startTime, processes);
+ std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
+
+ auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+ LOG(VERBOSE) << "Killed process group uid " << uid << " pid " << initialPid << " in "
+ << static_cast<int>(ms) << "ms, " << processes << " procs remain";
if (processes == 0) {
return removeProcessGroup(uid, initialPid);
@@ -277,67 +313,58 @@
}
}
-static int mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
{
- int ret;
-
- ret = mkdir(path, mode);
- if (ret < 0 && errno != EEXIST) {
- return -errno;
+ if (mkdir(path, mode) == -1 && errno != EEXIST) {
+ return false;
}
- ret = chown(path, uid, gid);
- if (ret < 0) {
- ret = -errno;
+ if (chown(path, uid, gid) == -1) {
+ int saved_errno = errno;
rmdir(path);
- return ret;
+ errno = saved_errno;
+ return false;
}
- return 0;
+ return true;
}
int createProcessGroup(uid_t uid, int initialPid)
{
char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
- int ret;
convertUidToPath(path, sizeof(path), uid);
- ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
- if (ret < 0) {
- SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
- return ret;
+ if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+ PLOG(ERROR) << "failed to make and chown " << path;
+ return -errno;
}
convertUidPidToPath(path, sizeof(path), uid, initialPid);
- ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
- if (ret < 0) {
- SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
- return ret;
+ if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+ PLOG(ERROR) << "failed to make and chown " << path;
+ return -errno;
}
strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
int fd = open(path, O_WRONLY);
- if (fd < 0) {
- ret = -errno;
- SLOGE("failed to open %s: %s", path, strerror(errno));
+ if (fd == -1) {
+ int ret = -errno;
+ PLOG(ERROR) << "failed to open " << path;
return ret;
}
char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
int len = snprintf(pid, sizeof(pid), "%d", initialPid);
- ret = write(fd, pid, len);
- if (ret < 0) {
+ int ret = 0;
+ if (write(fd, pid, len) < 0) {
ret = -errno;
- SLOGE("failed to write '%s' to %s: %s", pid, path, strerror(errno));
- } else {
- ret = 0;
+ PLOG(ERROR) << "failed to write '" << pid << "' to " << path;
}
close(fd);
return ret;
}
-
diff --git a/libprocessgroup/processgroup_priv.h b/libprocessgroup/processgroup_priv.h
deleted file mode 100644
index 1895bf9..0000000
--- a/libprocessgroup/processgroup_priv.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2014 Google, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 _PROCESSGROUP_PRIV_H_
-#define _PROCESSGROUP_PRIV_H_
-
-#define PROCESSGROUP_CGROUP_PATH "/acct"
-#define PROCESSGROUP_UID_PREFIX "uid_"
-#define PROCESSGROUP_PID_PREFIX "pid_"
-#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
-#define PROCESSGROUP_MAX_UID_LEN 11
-#define PROCESSGROUP_MAX_PID_LEN 11
-#define PROCESSGROUP_MAX_PATH_LEN \
- (sizeof(PROCESSGROUP_CGROUP_PATH) + \
- sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
- PROCESSGROUP_MAX_UID_LEN + \
- sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
- PROCESSGROUP_MAX_PID_LEN + \
- sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
- 1)
-
-#endif
diff --git a/libprocinfo/.clang-format b/libprocinfo/.clang-format
new file mode 100644
index 0000000..b8c6428
--- /dev/null
+++ b/libprocinfo/.clang-format
@@ -0,0 +1,14 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
new file mode 100644
index 0000000..8e17f1b
--- /dev/null
+++ b/libprocinfo/Android.bp
@@ -0,0 +1,73 @@
+//
+// 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.
+//
+
+libprocinfo_cppflags = [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+]
+
+cc_library {
+ name: "libprocinfo",
+ host_supported: true,
+ srcs: [
+ "process.cpp",
+ ],
+ cppflags: libprocinfo_cppflags,
+
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ shared_libs: ["libbase"],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+}
+
+// Tests
+// ------------------------------------------------------------------------------
+cc_test {
+ name: "libprocinfo_test",
+ host_supported: true,
+ srcs: [
+ "process_test.cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+
+ cppflags: libprocinfo_cppflags,
+ shared_libs: ["libbase", "libprocinfo"],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+}
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
new file mode 100644
index 0000000..fb140ff
--- /dev/null
+++ b/libprocinfo/include/procinfo/process.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace procinfo {
+
+#if defined(__linux__)
+
+struct ProcessInfo {
+ std::string name;
+ pid_t tid;
+ pid_t pid;
+ pid_t ppid;
+ pid_t tracer;
+ uid_t uid;
+ uid_t gid;
+};
+
+// Parse the contents of /proc/<tid>/status into |process_info|.
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info);
+
+// Parse the contents of <fd>/status into |process_info|.
+// |fd| should be an fd pointing at a /proc/<pid> directory.
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info);
+
+// Fetch the list of threads from a given process's /proc/<pid> directory.
+// |fd| should be an fd pointing at a /proc/<pid> directory.
+template <typename Collection>
+auto GetProcessTidsFromProcPidFd(int fd, Collection* out) ->
+ typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
+ out->clear();
+
+ int task_fd = openat(fd, "task", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(fdopendir(task_fd), closedir);
+ if (!dir) {
+ PLOG(ERROR) << "failed to open task directory";
+ return false;
+ }
+
+ struct dirent* dent;
+ while ((dent = readdir(dir.get()))) {
+ if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
+ pid_t tid;
+ if (!android::base::ParseInt(dent->d_name, &tid, 1, std::numeric_limits<pid_t>::max())) {
+ LOG(ERROR) << "failed to parse task id: " << dent->d_name;
+ return false;
+ }
+
+ out->insert(out->end(), tid);
+ }
+ }
+
+ return true;
+}
+
+template <typename Collection>
+auto GetProcessTids(pid_t pid, Collection* out) ->
+ typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
+ char task_path[PATH_MAX];
+ if (snprintf(task_path, PATH_MAX, "/proc/%d", pid) >= PATH_MAX) {
+ LOG(ERROR) << "task path overflow (pid = " << pid << ")";
+ return false;
+ }
+
+ android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "failed to open " << task_path;
+ return false;
+ }
+
+ return GetProcessTidsFromProcPidFd(fd.get(), out);
+}
+
+#endif
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
new file mode 100644
index 0000000..c513e16
--- /dev/null
+++ b/libprocinfo/process.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+namespace android {
+namespace procinfo {
+
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "/proc/%d", tid);
+
+ unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
+ if (dirfd == -1) {
+ PLOG(ERROR) << "failed to open " << path;
+ return false;
+ }
+
+ return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
+}
+
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
+ int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
+
+ if (status_fd == -1) {
+ PLOG(ERROR) << "failed to open status fd in GetProcessInfoFromProcPidFd";
+ return false;
+ }
+
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose);
+ if (!fp) {
+ PLOG(ERROR) << "failed to open status file in GetProcessInfoFromProcPidFd";
+ close(status_fd);
+ return false;
+ }
+
+ int field_bitmap = 0;
+ static constexpr int finished_bitmap = 127;
+ char* line = nullptr;
+ size_t len = 0;
+
+ while (getline(&line, &len, fp.get()) != -1 && field_bitmap != finished_bitmap) {
+ char* tab = strchr(line, '\t');
+ if (tab == nullptr) {
+ continue;
+ }
+
+ size_t header_len = tab - line;
+ std::string header = std::string(line, header_len);
+ if (header == "Name:") {
+ std::string name = line + header_len + 1;
+
+ // line includes the trailing newline.
+ name.pop_back();
+ process_info->name = std::move(name);
+
+ field_bitmap |= 1;
+ } else if (header == "Pid:") {
+ process_info->tid = atoi(tab + 1);
+ field_bitmap |= 2;
+ } else if (header == "Tgid:") {
+ process_info->pid = atoi(tab + 1);
+ field_bitmap |= 4;
+ } else if (header == "PPid:") {
+ process_info->ppid = atoi(tab + 1);
+ field_bitmap |= 8;
+ } else if (header == "TracerPid:") {
+ process_info->tracer = atoi(tab + 1);
+ field_bitmap |= 16;
+ } else if (header == "Uid:") {
+ process_info->uid = atoi(tab + 1);
+ field_bitmap |= 32;
+ } else if (header == "Gid:") {
+ process_info->gid = atoi(tab + 1);
+ field_bitmap |= 64;
+ }
+ }
+
+ free(line);
+ return field_bitmap == finished_bitmap;
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp
new file mode 100644
index 0000000..5ffd236
--- /dev/null
+++ b/libprocinfo/process_test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#if !defined(__BIONIC__)
+#include <syscall.h>
+static pid_t gettid() {
+ return syscall(__NR_gettid);
+}
+#endif
+
+TEST(process_info, process_info_smoke) {
+ android::procinfo::ProcessInfo self;
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(gettid(), &self));
+ ASSERT_EQ(gettid(), self.tid);
+ ASSERT_EQ(getpid(), self.pid);
+ ASSERT_EQ(getppid(), self.ppid);
+ ASSERT_EQ(getuid(), self.uid);
+ ASSERT_EQ(getgid(), self.gid);
+}
+
+TEST(process_info, process_info_proc_pid_fd_smoke) {
+ android::procinfo::ProcessInfo self;
+ int fd = open(android::base::StringPrintf("/proc/%d", gettid()).c_str(), O_DIRECTORY | O_RDONLY);
+ ASSERT_NE(-1, fd);
+ ASSERT_TRUE(android::procinfo::GetProcessInfoFromProcPidFd(fd, &self));
+
+ // Process name is capped at 15 bytes.
+ ASSERT_EQ("libprocinfo_tes", self.name);
+ ASSERT_EQ(gettid(), self.tid);
+ ASSERT_EQ(getpid(), self.pid);
+ ASSERT_EQ(getppid(), self.ppid);
+ ASSERT_EQ(getuid(), self.uid);
+ ASSERT_EQ(getgid(), self.gid);
+ close(fd);
+}
+
+TEST(process_info, process_tids_smoke) {
+ pid_t main_tid = gettid();
+ std::thread([main_tid]() {
+ pid_t thread_tid = gettid();
+
+ {
+ std::vector<pid_t> vec;
+ ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &vec));
+ ASSERT_EQ(1, std::count(vec.begin(), vec.end(), main_tid));
+ ASSERT_EQ(1, std::count(vec.begin(), vec.end(), thread_tid));
+ }
+
+ {
+ std::set<pid_t> set;
+ ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &set));
+ ASSERT_EQ(1, std::count(set.begin(), set.end(), main_tid));
+ ASSERT_EQ(1, std::count(set.begin(), set.end(), thread_tid));
+ }
+ }).join();
+}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
new file mode 100644
index 0000000..dd8b5fd
--- /dev/null
+++ b/libsparse/Android.bp
@@ -0,0 +1,67 @@
+// Copyright 2010 The Android Open Source Project
+
+cc_library {
+ name: "libsparse",
+ host_supported: true,
+ unique_host_soname: true,
+ srcs: [
+ "backed_block.c",
+ "output_file.c",
+ "sparse.c",
+ "sparse_crc32.c",
+ "sparse_err.c",
+ "sparse_read.c",
+ ],
+ cflags: ["-Werror"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ target: {
+ host: {
+ shared_libs: ["libz-host"],
+ },
+ android: {
+ shared_libs: ["libz"],
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+}
+
+cc_binary {
+ name: "simg2img",
+ host_supported: true,
+ srcs: [
+ "simg2img.c",
+ "sparse_crc32.c",
+ ],
+ static_libs: [
+ "libsparse",
+ "libz",
+ ],
+
+ cflags: ["-Werror"],
+}
+
+cc_binary {
+ name: "img2simg",
+ host_supported: true,
+ srcs: ["img2simg.c"],
+ static_libs: [
+ "libsparse",
+ "libz",
+ ],
+
+ cflags: ["-Werror"],
+}
+
+cc_binary_host {
+ name: "append2simg",
+ srcs: ["append2simg.c"],
+ static_libs: [
+ "libsparse",
+ "libz",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/libsparse/Android.mk b/libsparse/Android.mk
index 925b98b..05e68bc 100644
--- a/libsparse/Android.mk
+++ b/libsparse/Android.mk
@@ -2,105 +2,6 @@
LOCAL_PATH:= $(call my-dir)
-libsparse_src_files := \
- backed_block.c \
- output_file.c \
- sparse.c \
- sparse_crc32.c \
- sparse_err.c \
- sparse_read.c
-
-
-include $(CLEAR_VARS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SRC_FILES := $(libsparse_src_files)
-LOCAL_MODULE := libsparse_host
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-include $(CLEAR_VARS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SRC_FILES := $(libsparse_src_files)
-LOCAL_MODULE := libsparse
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := \
- libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-
-include $(CLEAR_VARS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SRC_FILES := $(libsparse_src_files)
-LOCAL_MODULE := libsparse_static
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := simg2img.c \
- sparse_crc32.c
-LOCAL_MODULE := simg2img_host
-# Need a unique module name, but exe should still be called simg2img
-LOCAL_MODULE_STEM := simg2img
-LOCAL_STATIC_LIBRARIES := \
- libsparse_host \
- libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_HOST_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := simg2img.c \
- sparse_crc32.c
-LOCAL_MODULE := simg2img
-LOCAL_STATIC_LIBRARIES := \
- libsparse_static \
- libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := img2simg.c
-LOCAL_MODULE := img2simg_host
-# Need a unique module name, but exe should still be called simg2img
-LOCAL_MODULE_STEM := img2simg
-LOCAL_STATIC_LIBRARIES := \
- libsparse_host \
- libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_HOST_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := img2simg.c
-LOCAL_MODULE := img2simg
-LOCAL_STATIC_LIBRARIES := \
- libsparse_static \
- libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
-
-
-ifneq ($(HOST_OS),windows)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := append2simg.c
-LOCAL_MODULE := append2simg
-LOCAL_STATIC_LIBRARIES := \
- libsparse_host \
- libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_HOST_EXECUTABLE)
-
-endif
-
include $(CLEAR_VARS)
LOCAL_MODULE := simg_dump.py
LOCAL_SRC_FILES := simg_dump.py
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
index 1cf827c..eef8764 100644
--- a/libsparse/append2simg.c
+++ b/libsparse/append2simg.c
@@ -82,7 +82,7 @@
exit(-1);
}
- sparse_output = sparse_file_import_auto(output, true, true);
+ sparse_output = sparse_file_import_auto(output, false, true);
if (!sparse_output) {
fprintf(stderr, "Couldn't import output file\n");
exit(-1);
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
index 3e72b57..794cd6b 100644
--- a/libsparse/backed_block.c
+++ b/libsparse/backed_block.c
@@ -221,7 +221,8 @@
}
break;
case BACKED_BLOCK_FILE:
- if (a->file.filename != b->file.filename ||
+ /* Already make sure b->type is BACKED_BLOCK_FILE */
+ if (strcmp(a->file.filename, b->file.filename) ||
a->file.offset + a->len != b->file.offset) {
return -EINVAL;
}
@@ -279,7 +280,10 @@
}
merge_bb(bbl, new_bb, new_bb->next);
- merge_bb(bbl, bb, new_bb);
+ if (!merge_bb(bbl, bb, new_bb)) {
+ /* new_bb destroyed, point to retained as last_used */
+ bbl->last_used = bb;
+ }
return 0;
}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 42d4adb..356f65f 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -176,6 +176,13 @@
int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc);
/**
+ * sparse_file_block_size
+ *
+ * @s - sparse file cookie
+ */
+unsigned int sparse_file_block_size(struct sparse_file *s);
+
+/**
* sparse_file_callback - call a callback for blocks in sparse file
*
* @s - sparse file cookie
@@ -197,6 +204,24 @@
int (*write)(void *priv, const void *data, int len), void *priv);
/**
+ * sparse_file_foreach_chunk - call a callback for data blocks in sparse file
+ *
+ * @s - sparse file cookie
+ * @sparse - write in the Android sparse file format
+ * @crc - append a crc chunk
+ * @write - function to call for each block
+ * @priv - value that will be passed as the first argument to write
+ *
+ * The function has the same behavior as 'sparse_file_callback', except it only
+ * iterates on blocks that contain data.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
+ int (*write)(void *priv, const void *data, int len, unsigned int block,
+ unsigned int nr_blocks),
+ void *priv);
+/**
* sparse_file_read - read a file into a sparse file cookie
*
* @s - sparse file cookie
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index cd30800..2115998 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -34,7 +34,7 @@
#include "sparse_crc32.h"
#include "sparse_format.h"
-#ifndef USE_MINGW
+#ifndef _WIN32
#include <sys/mman.h>
#define O_BINARY 0
#else
@@ -57,13 +57,13 @@
#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
#define container_of(inner, outer_t, elem) \
- ((outer_t *)((char *)inner - offsetof(outer_t, elem)))
+ ((outer_t *)((char *)(inner) - offsetof(outer_t, elem)))
struct output_file_ops {
int (*open)(struct output_file *, int fd);
int (*skip)(struct output_file *, int64_t);
int (*pad)(struct output_file *, int64_t);
- int (*write)(struct output_file *, void *, int);
+ int (*write)(struct output_file *, void *, size_t);
void (*close)(struct output_file *);
};
@@ -149,18 +149,23 @@
return 0;
}
-static int file_write(struct output_file *out, void *data, int len)
+static int file_write(struct output_file *out, void *data, size_t len)
{
- int ret;
+ ssize_t ret;
struct output_file_normal *outn = to_output_file_normal(out);
- ret = write(outn->fd, data, len);
- if (ret < 0) {
- error_errno("write");
- return -1;
- } else if (ret < len) {
- error("incomplete write");
- return -1;
+ while (len > 0) {
+ ret = write(outn->fd, data, len);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ error_errno("write");
+ return -1;
+ }
+
+ data = (char *)data + ret;
+ len -= ret;
}
return 0;
@@ -232,18 +237,20 @@
return 0;
}
-static int gz_file_write(struct output_file *out, void *data, int len)
+static int gz_file_write(struct output_file *out, void *data, size_t len)
{
int ret;
struct output_file_gz *outgz = to_output_file_gz(out);
- ret = gzwrite(outgz->gz_fd, data, len);
- if (ret < 0) {
- error_errno("gzwrite");
- return -1;
- } else if (ret < len) {
- error("incomplete gzwrite");
- return -1;
+ while (len > 0) {
+ ret = gzwrite(outgz->gz_fd, data,
+ min(len, (unsigned int)INT_MAX));
+ if (ret == 0) {
+ error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
+ return -1;
+ }
+ len -= ret;
+ data = (char *)data + ret;
}
return 0;
@@ -293,7 +300,7 @@
return -1;
}
-static int callback_file_write(struct output_file *out, void *data, int len)
+static int callback_file_write(struct output_file *out, void *data, size_t len)
{
struct output_file_callback *outc = to_output_file_callback(out);
@@ -698,14 +705,16 @@
int ret;
int64_t aligned_offset;
int aligned_diff;
- int buffer_size;
+ uint64_t buffer_size;
char *ptr;
aligned_offset = offset & ~(4096 - 1);
aligned_diff = offset - aligned_offset;
- buffer_size = len + aligned_diff;
+ buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-#ifndef USE_MINGW
+#ifndef _WIN32
+ if (buffer_size > SIZE_MAX)
+ return -E2BIG;
char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd,
aligned_offset);
if (data == MAP_FAILED) {
@@ -733,7 +742,7 @@
ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-#ifndef USE_MINGW
+#ifndef _WIN32
munmap(data, buffer_size);
#else
free(data);
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
index 95e9b5b..b9b438e 100644
--- a/libsparse/simg2img.c
+++ b/libsparse/simg2img.c
@@ -40,7 +40,6 @@
int in;
int out;
int i;
- int ret;
struct sparse_file *s;
if (argc < 3) {
@@ -71,10 +70,12 @@
exit(-1);
}
- lseek(out, SEEK_SET, 0);
+ if (lseek(out, 0, SEEK_SET) == -1) {
+ perror("lseek failed");
+ exit(EXIT_FAILURE);
+ }
- ret = sparse_file_write(s, out, false, false, false);
- if (ret < 0) {
+ if (sparse_file_write(s, out, false, false, false) < 0) {
fprintf(stderr, "Cannot write output file\n");
exit(-1);
}
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
index 311678a..b175860 100644
--- a/libsparse/sparse.c
+++ b/libsparse/sparse.c
@@ -199,6 +199,57 @@
return ret;
}
+struct chunk_data {
+ void *priv;
+ unsigned int block;
+ unsigned int nr_blocks;
+ int (*write)(void *priv, const void *data, int len, unsigned int block,
+ unsigned int nr_blocks);
+};
+
+static int foreach_chunk_write(void *priv, const void *data, int len)
+{
+ struct chunk_data *chk = priv;
+
+ return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
+}
+
+int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
+ int (*write)(void *priv, const void *data, int len, unsigned int block,
+ unsigned int nr_blocks),
+ void *priv)
+{
+ int ret;
+ int chunks;
+ struct chunk_data chk;
+ struct output_file *out;
+ struct backed_block *bb;
+
+ chk.priv = priv;
+ chk.write = write;
+ chk.block = chk.nr_blocks = 0;
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_callback(foreach_chunk_write, &chk,
+ s->block_size, s->len, false, sparse,
+ chunks, crc);
+
+ if (!out)
+ return -ENOMEM;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb;
+ bb = backed_block_iter_next(bb)) {
+ chk.block = backed_block_block(bb);
+ chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
+ ret = sparse_file_write_block(out, bb);
+ if (ret)
+ return ret;
+ }
+
+ output_file_close(out);
+
+ return ret;
+}
+
static int out_counter_write(void *priv, const void *data __unused, int len)
{
int64_t *count = priv;
@@ -230,6 +281,11 @@
return count;
}
+unsigned int sparse_file_block_size(struct sparse_file *s)
+{
+ return s->block_size;
+}
+
static struct backed_block *move_chunks_up_to_len(struct sparse_file *from,
struct sparse_file *to, unsigned int len)
{
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.c
index 9b10293..a188202 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.c
@@ -18,6 +18,7 @@
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
+#include <inttypes.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
@@ -78,7 +79,7 @@
s = " at ";
}
if (verbose) {
-#ifndef USE_MINGW
+#ifndef _WIN32
if (err == -EOVERFLOW) {
sparse_print_verbose("EOF while reading file%s%s\n", s, at);
} else
@@ -198,7 +199,7 @@
return 0;
}
-static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t crc32)
+static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
{
uint32_t file_crc32;
int ret;
@@ -212,7 +213,7 @@
return ret;
}
- if (file_crc32 != crc32) {
+ if (crc32 != NULL && file_crc32 != *crc32) {
return -EINVAL;
}
@@ -233,7 +234,7 @@
ret = process_raw_chunk(s, chunk_data_size, fd, offset,
chunk_header->chunk_sz, cur_block, crc_ptr);
if (ret < 0) {
- verbose_error(s->verbose, ret, "data block at %lld", offset);
+ verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
return ret;
}
return chunk_header->chunk_sz;
@@ -241,7 +242,7 @@
ret = process_fill_chunk(s, chunk_data_size, fd,
chunk_header->chunk_sz, cur_block, crc_ptr);
if (ret < 0) {
- verbose_error(s->verbose, ret, "fill block at %lld", offset);
+ verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
return ret;
}
return chunk_header->chunk_sz;
@@ -250,21 +251,21 @@
chunk_header->chunk_sz, cur_block, crc_ptr);
if (chunk_data_size != 0) {
if (ret < 0) {
- verbose_error(s->verbose, ret, "skip block at %lld", offset);
+ verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
return ret;
}
}
return chunk_header->chunk_sz;
case CHUNK_TYPE_CRC32:
- ret = process_crc32_chunk(fd, chunk_data_size, *crc_ptr);
+ ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
if (ret < 0) {
- verbose_error(s->verbose, -EINVAL, "crc block at %lld",
+ verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
offset);
return ret;
}
return 0;
default:
- verbose_error(s->verbose, -EINVAL, "unknown block %04X at %lld",
+ verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
chunk_header->chunk_type, offset);
}
@@ -373,6 +374,7 @@
ret = read_all(fd, buf, to_read);
if (ret < 0) {
error("failed to read sparse file");
+ free(buf);
return ret;
}
@@ -400,6 +402,7 @@
block++;
}
+ free(buf);
return 0;
}
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
new file mode 100644
index 0000000..d27ceea
--- /dev/null
+++ b/libsuspend/Android.bp
@@ -0,0 +1,21 @@
+// Copyright 2012 The Android Open Source Project
+
+cc_library {
+ name: "libsuspend",
+ srcs: [
+ "autosuspend.c",
+ "autosuspend_autosleep.c",
+ "autosuspend_earlysuspend.c",
+ "autosuspend_wakeup_count.c",
+ ],
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ ],
+ cflags: [
+ "-Werror",
+ // "-DLOG_NDEBUG=0",
+ ],
+}
diff --git a/libsuspend/Android.mk b/libsuspend/Android.mk
deleted file mode 100644
index 1ba2f59..0000000
--- a/libsuspend/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright 2012 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-libsuspend_src_files := \
- autosuspend.c \
- autosuspend_autosleep.c \
- autosuspend_earlysuspend.c \
- autosuspend_wakeup_count.c \
-
-libsuspend_libraries := \
- liblog libcutils
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(libsuspend_src_files)
-LOCAL_MODULE := libsuspend
-LOCAL_MODULE_TAGS := optional
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := $(libsuspend_libraries)
-LOCAL_CFLAGS := -Werror
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(libsuspend_src_files)
-LOCAL_MODULE := libsuspend
-LOCAL_MODULE_TAGS := optional
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c
index edd1007..54730c2 100644
--- a/libsuspend/autosuspend.c
+++ b/libsuspend/autosuspend.c
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#define LOG_TAG "libsuspend"
+
#include <stdbool.h>
-#define LOG_TAG "libsuspend"
-#include <cutils/log.h>
+#include <log/log.h>
#include <suspend/autosuspend.h>
diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c
index 0d31e74..77d8db0 100644
--- a/libsuspend/autosuspend_autosleep.c
+++ b/libsuspend/autosuspend_autosleep.c
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "libsuspend"
+
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
@@ -22,8 +24,7 @@
#include <sys/types.h>
#include <unistd.h>
-#define LOG_TAG "libsuspend"
-#include <cutils/log.h>
+#include <log/log.h>
#include "autosuspend_ops.h"
@@ -40,7 +41,7 @@
ALOGV("autosuspend_autosleep_enable\n");
- ret = write(autosleep_fd, sleep_state, strlen(sleep_state));
+ ret = TEMP_FAILURE_RETRY(write(autosleep_fd, sleep_state, strlen(sleep_state)));
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
@@ -62,7 +63,7 @@
ALOGV("autosuspend_autosleep_disable\n");
- ret = write(autosleep_fd, on_state, strlen(on_state));
+ ret = TEMP_FAILURE_RETRY(write(autosleep_fd, on_state, strlen(on_state)));
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
@@ -86,7 +87,7 @@
{
char buf[80];
- autosleep_fd = open(SYS_POWER_AUTOSLEEP, O_WRONLY);
+ autosleep_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_AUTOSLEEP, O_WRONLY));
if (autosleep_fd < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error opening %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c
index 2bece4c..809ee82 100644
--- a/libsuspend/autosuspend_earlysuspend.c
+++ b/libsuspend/autosuspend_earlysuspend.c
@@ -14,18 +14,19 @@
* limitations under the License.
*/
+#define LOG_TAG "libsuspend"
+
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
-#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
-#define LOG_TAG "libsuspend"
-#include <cutils/log.h>
+#include <log/log.h>
#include "autosuspend_ops.h"
@@ -49,11 +50,9 @@
{
int err = 0;
char buf;
- int fd = open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0);
+ int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0));
// if the file doesn't exist, the error will be caught in read() below
- do {
- err = read(fd, &buf, 1);
- } while (err < 0 && errno == EINTR);
+ err = TEMP_FAILURE_RETRY(read(fd, &buf, 1));
ALOGE_IF(err < 0,
"*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
close(fd);
@@ -64,11 +63,9 @@
{
int err = 0;
char buf;
- int fd = open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0);
+ int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0));
// if the file doesn't exist, the error will be caught in read() below
- do {
- err = read(fd, &buf, 1);
- } while (err < 0 && errno == EINTR);
+ err = TEMP_FAILURE_RETRY(read(fd, &buf, 1));
ALOGE_IF(err < 0,
"*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
close(fd);
@@ -134,7 +131,7 @@
ALOGV("autosuspend_earlysuspend_disable\n");
- ret = write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on));
+ ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on)));
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
@@ -195,7 +192,7 @@
char buf[80];
int ret;
- sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR);
+ sPowerStatefd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR));
if (sPowerStatefd < 0) {
strerror_r(errno, buf, sizeof(buf));
@@ -203,7 +200,7 @@
return NULL;
}
- ret = write(sPowerStatefd, "on", 2);
+ ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, "on", 2));
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index 7483a8f..2da204a 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -14,31 +14,46 @@
* limitations under the License.
*/
+#define LOG_TAG "libsuspend"
+//#define LOG_NDEBUG 0
+
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <stddef.h>
+#include <stdbool.h>
#include <string.h>
+#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
-#define LOG_TAG "libsuspend"
-//#define LOG_NDEBUG 0
-#include <cutils/log.h>
+#include <log/log.h>
#include "autosuspend_ops.h"
#define SYS_POWER_STATE "/sys/power/state"
#define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
+#define BASE_SLEEP_TIME 100000
+
static int state_fd;
static int wakeup_count_fd;
static pthread_t suspend_thread;
static sem_t suspend_lockout;
static const char *sleep_state = "mem";
-static void (*wakeup_func)(void) = NULL;
+static void (*wakeup_func)(bool success) = NULL;
+static int sleep_time = BASE_SLEEP_TIME;
+
+static void update_sleep_time(bool success) {
+ if (success) {
+ sleep_time = BASE_SLEEP_TIME;
+ return;
+ }
+ // double sleep time after each failure up to one minute
+ sleep_time = MIN(sleep_time * 2, 60000000);
+}
static void *suspend_thread_func(void *arg __attribute__((unused)))
{
@@ -46,12 +61,16 @@
char wakeup_count[20];
int wakeup_count_len;
int ret;
+ bool success = true;
while (1) {
- usleep(100000);
+ update_sleep_time(success);
+ usleep(sleep_time);
+ success = false;
ALOGV("%s: read wakeup_count\n", __func__);
lseek(wakeup_count_fd, 0, SEEK_SET);
- wakeup_count_len = read(wakeup_count_fd, wakeup_count, sizeof(wakeup_count));
+ wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
+ sizeof(wakeup_count)));
if (wakeup_count_len < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
@@ -72,21 +91,19 @@
}
ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
- ret = write(wakeup_count_fd, wakeup_count, wakeup_count_len);
+ ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
} else {
ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
- ret = write(state_fd, sleep_state, strlen(sleep_state));
- if (ret < 0) {
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error writing to %s: %s\n", SYS_POWER_STATE, buf);
- } else {
- void (*func)(void) = wakeup_func;
- if (func != NULL) {
- (*func)();
- }
+ ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
+ if (ret >= 0) {
+ success = true;
+ }
+ void (*func)(bool success) = wakeup_func;
+ if (func != NULL) {
+ (*func)(success);
}
}
@@ -138,7 +155,7 @@
return ret;
}
-void set_wakeup_callback(void (*func)(void))
+void set_wakeup_callback(void (*func)(bool success))
{
if (wakeup_func != NULL) {
ALOGE("Duplicate wakeup callback applied, keeping original");
@@ -157,14 +174,14 @@
int ret;
char buf[80];
- state_fd = open(SYS_POWER_STATE, O_RDWR);
+ state_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_STATE, O_RDWR));
if (state_fd < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf);
goto err_open_state;
}
- wakeup_count_fd = open(SYS_POWER_WAKEUP_COUNT, O_RDWR);
+ wakeup_count_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_WAKEUP_COUNT, O_RDWR));
if (wakeup_count_fd < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h
index 10e3d27..59188a8 100644
--- a/libsuspend/include/suspend/autosuspend.h
+++ b/libsuspend/include/suspend/autosuspend.h
@@ -18,6 +18,7 @@
#define _LIBSUSPEND_AUTOSUSPEND_H_
#include <sys/cdefs.h>
+#include <stdbool.h>
__BEGIN_DECLS
@@ -46,9 +47,11 @@
/*
* set_wakeup_callback
*
- * Set a function to be called each time the device wakes up from suspend.
+ * Set a function to be called each time the device returns from suspend.
+ * success is true if the suspend was sucessful and false if the suspend
+ * aborted due to some reason.
*/
-void set_wakeup_callback(void (*func)(void));
+void set_wakeup_callback(void (*func)(bool success));
__END_DECLS
diff --git a/libsync/Android.bp b/libsync/Android.bp
new file mode 100644
index 0000000..a4e5599
--- /dev/null
+++ b/libsync/Android.bp
@@ -0,0 +1,41 @@
+cc_defaults {
+ name: "libsync_defaults",
+ srcs: ["sync.c"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ cflags: ["-Werror"],
+}
+
+cc_library_shared {
+ name: "libsync",
+ defaults: ["libsync_defaults"],
+}
+
+// libsync_recovery is only intended for the recovery binary.
+// Future versions of the kernel WILL require an updated libsync, and will break
+// anything statically linked against the current libsync.
+cc_library_static {
+ name: "libsync_recovery",
+ defaults: ["libsync_defaults"],
+}
+
+cc_test {
+ name: "sync_test",
+ defaults: ["libsync_defaults"],
+ gtest: false,
+ srcs: ["sync_test.c"],
+}
+
+cc_test {
+ name: "sync-unit-tests",
+ shared_libs: ["libsync"],
+ srcs: ["tests/sync_test.cpp"],
+ cflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-sign-compare",
+ ],
+ clang: true,
+}
diff --git a/libsync/Android.mk b/libsync/Android.mk
deleted file mode 100644
index fd1c88c..0000000
--- a/libsync/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := sync.c
-LOCAL_MODULE := libsync
-LOCAL_MODULE_TAGS := optional
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := sync.c sync_test.c
-LOCAL_MODULE := sync_test
-LOCAL_MODULE_TAGS := optional tests
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
index 2e5d82f..50ed0ac 100644
--- a/libsync/include/sync/sync.h
+++ b/libsync/include/sync/sync.h
@@ -22,9 +22,16 @@
#include <sys/cdefs.h>
#include <stdint.h>
+#include <linux/types.h>
+
__BEGIN_DECLS
-// XXX: These structs are copied from the header "linux/sync.h".
+struct sync_legacy_merge_data {
+ int32_t fd2;
+ char name[32];
+ int32_t fence;
+};
+
struct sync_fence_info_data {
uint32_t len;
char name[32];
@@ -41,6 +48,108 @@
uint8_t driver_data[0];
};
+#define SYNC_IOC_MAGIC '>'
+
+/**
+ * DOC: SYNC_IOC_LEGACY_WAIT - wait for a fence to signal
+ *
+ * pass timeout in milliseconds. Waits indefinitely timeout < 0.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data. Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_MERGE _IOWR(SYNC_IOC_MAGIC, 1, \
+ struct sync_legacy_merge_data)
+
+/**
+ * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
+ *
+ * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
+ * Caller should write the size of the buffer into len. On return, len is
+ * updated to reflect the total size of the sync_fence_info_data including
+ * pt_info.
+ *
+ * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
+ * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\
+ struct sync_fence_info_data)
+
+struct sync_merge_data {
+ char name[32];
+ int32_t fd2;
+ int32_t fence;
+ uint32_t flags;
+ uint32_t pad;
+};
+
+struct sync_file_info {
+ char name[32];
+ int32_t status;
+ uint32_t flags;
+ uint32_t num_fences;
+ uint32_t pad;
+
+ uint64_t sync_fence_info;
+};
+
+struct sync_fence_info {
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint32_t flags;
+ uint64_t timestamp_ns;
+};
+
+/**
+ * Mainline API:
+ *
+ * Opcodes 0, 1 and 2 were burned during a API change to avoid users of the
+ * old API to get weird errors when trying to handling sync_files. The API
+ * change happened during the de-stage of the Sync Framework when there was
+ * no upstream users available.
+ */
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data. Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the new version of the Sync API after the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
+
+/**
+ * DOC: SYNC_IOC_FILE_INFO - get detailed information on a sync_file
+ *
+ * Takes a struct sync_file_info. If num_fences is 0, the field is updated
+ * with the actual number of fences. If num_fences is > 0, the system will
+ * use the pointer provided on sync_fence_info to return up to num_fences of
+ * struct sync_fence_info, with detailed fence information.
+ *
+ * This is the new version of the Sync API after the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_FILE_INFO _IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info)
+
/* timeout in msecs */
int sync_wait(int fd, int timeout);
int sync_merge(const char *name, int fd1, int fd2);
diff --git a/libsync/sync.c b/libsync/sync.c
index d73bb11..9ed03db 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -20,53 +20,155 @@
#include <malloc.h>
#include <stdint.h>
#include <string.h>
-
-#include <linux/sync.h>
-#include <linux/sw_sync.h>
+#include <errno.h>
+#include <poll.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sync/sync.h>
+
+
+struct sw_sync_create_fence_data {
+ __u32 value;
+ char name[32];
+ __s32 fence;
+};
+
+#define SW_SYNC_IOC_MAGIC 'W'
+#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0, struct sw_sync_create_fence_data)
+#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
+
int sync_wait(int fd, int timeout)
{
- __s32 to = timeout;
+ struct pollfd fds;
+ int ret;
- return ioctl(fd, SYNC_IOC_WAIT, &to);
+ if (fd < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ fds.fd = fd;
+ fds.events = POLLIN;
+
+ do {
+ ret = poll(&fds, 1, timeout);
+ if (ret > 0) {
+ if (fds.revents & (POLLERR | POLLNVAL)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+ } else if (ret == 0) {
+ errno = ETIME;
+ return -1;
+ }
+ } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+ return ret;
}
int sync_merge(const char *name, int fd1, int fd2)
{
+ struct sync_legacy_merge_data legacy_data;
struct sync_merge_data data;
- int err;
+ int ret;
data.fd2 = fd2;
strlcpy(data.name, name, sizeof(data.name));
+ data.flags = 0;
+ data.pad = 0;
- err = ioctl(fd1, SYNC_IOC_MERGE, &data);
- if (err < 0)
- return err;
+ ret = ioctl(fd1, SYNC_IOC_MERGE, &data);
+ if (ret < 0 && errno == ENOTTY) {
+ legacy_data.fd2 = fd2;
+ strlcpy(legacy_data.name, name, sizeof(legacy_data.name));
+
+ ret = ioctl(fd1, SYNC_IOC_LEGACY_MERGE, &legacy_data);
+ if (ret < 0)
+ return ret;
+
+ return legacy_data.fence;
+ } else if (ret < 0) {
+ return ret;
+ }
return data.fence;
}
struct sync_fence_info_data *sync_fence_info(int fd)
{
- struct sync_fence_info_data *info;
- int err;
+ struct sync_fence_info_data *legacy_info;
+ struct sync_pt_info *legacy_pt_info;
+ struct sync_file_info *info;
+ struct sync_fence_info *fence_info;
+ int err, num_fences, i;
- info = malloc(4096);
- if (info == NULL)
+ legacy_info = malloc(4096);
+ if (legacy_info == NULL)
return NULL;
- info->len = 4096;
- err = ioctl(fd, SYNC_IOC_FENCE_INFO, info);
- if (err < 0) {
- free(info);
+ legacy_info->len = 4096;
+ err = ioctl(fd, SYNC_IOC_LEGACY_FENCE_INFO, legacy_info);
+ if (err < 0 && errno != ENOTTY) {
+ free(legacy_info);
return NULL;
+ } else if (err == 0) {
+ return legacy_info;
}
- return info;
+ info = calloc(1, sizeof(*info));
+ if (info == NULL)
+ goto free;
+
+ err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
+ if (err < 0)
+ goto free;
+
+ num_fences = info->num_fences;
+
+ if (num_fences) {
+ info->flags = 0;
+ info->num_fences = num_fences;
+ info->sync_fence_info = (uint64_t) calloc(num_fences,
+ sizeof(struct sync_fence_info));
+ if ((void *)info->sync_fence_info == NULL)
+ goto free;
+
+ err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
+ if (err < 0) {
+ free((void *)info->sync_fence_info);
+ goto free;
+ }
+ }
+
+ legacy_info->len = sizeof(*legacy_info) +
+ num_fences * sizeof(struct sync_fence_info);
+ strlcpy(legacy_info->name, info->name, sizeof(legacy_info->name));
+ legacy_info->status = info->status;
+
+ legacy_pt_info = (struct sync_pt_info *)legacy_info->pt_info;
+ fence_info = (struct sync_fence_info *)info->sync_fence_info;
+ for (i = 0 ; i < num_fences ; i++) {
+ legacy_pt_info[i].len = sizeof(*legacy_pt_info);
+ strlcpy(legacy_pt_info[i].obj_name, fence_info[i].obj_name,
+ sizeof(legacy_pt_info->obj_name));
+ strlcpy(legacy_pt_info[i].driver_name, fence_info[i].driver_name,
+ sizeof(legacy_pt_info->driver_name));
+ legacy_pt_info[i].status = fence_info[i].status;
+ legacy_pt_info[i].timestamp_ns = fence_info[i].timestamp_ns;
+ }
+
+ free((void *)info->sync_fence_info);
+ free(info);
+ return legacy_info;
+
+free:
+ free(legacy_info);
+ free(info);
+ return NULL;
}
struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
@@ -91,7 +193,13 @@
int sw_sync_timeline_create(void)
{
- return open("/dev/sw_sync", O_RDWR);
+ int ret;
+
+ ret = open("/sys/kernel/debug/sync/sw_sync", O_RDWR);
+ if (ret < 0)
+ ret = open("/dev/sw_sync", O_RDWR);
+
+ return ret;
}
int sw_sync_timeline_inc(int fd, unsigned count)
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index ee9ea3c..9a5f7d8 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -92,7 +92,7 @@
for (j = 0; j < 2; j++) {
unsigned val = i + j * 3 + 1;
- sprintf(str, "test_fence%d-%d", i, j);
+ snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
if (fd < 0) {
printf("can't create sync pt %d: %s", val, strerror(errno));
@@ -106,7 +106,7 @@
sync_data[3].thread_no = 3;
for (j = 0; j < 2; j++) {
- sprintf(str, "merged_fence%d", j);
+ snprintf(str, sizeof(str), "merged_fence%d", j);
sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
if (sync_data[3].fd[j] < 0) {
printf("can't merge sync pts %d and %d: %s\n",
diff --git a/libsync/tests/Android.mk b/libsync/tests/Android.mk
deleted file mode 100644
index 8137c7a..0000000
--- a/libsync/tests/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_MODULE := sync-unit-tests
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers -Wno-sign-compare
-LOCAL_SHARED_LIBRARIES += libsync
-LOCAL_STATIC_LIBRARIES += libgtest_main
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
-LOCAL_SRC_FILES := \
- sync_test.cpp
-include $(BUILD_NATIVE_TEST)
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 55cd687..401aaee 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -50,7 +50,7 @@
bool isValid() const {
if (m_fdInitialized) {
int status = fcntl(m_fd, F_GETFD, 0);
- if (status == 0)
+ if (status >= 0)
return true;
else
return false;
@@ -92,7 +92,7 @@
bool isValid() const {
if (m_fdInitialized) {
int status = fcntl(m_fd, F_GETFD, 0);
- if (status == 0)
+ if (status >= 0)
return true;
else
return false;
@@ -348,33 +348,6 @@
ASSERT_EQ(selfMergeFence.getSignaledCount(), 1);
}
-TEST(FenceTest, WaitOnDestroyedTimeline) {
- SyncTimeline timeline;
- ASSERT_TRUE(timeline.isValid());
-
- SyncFence fenceSig(timeline, 100);
- SyncFence fenceKill(timeline, 200);
-
- // Spawn a thread to wait on a fence when the timeline is killed.
- thread waitThread{
- [&]() {
- ASSERT_EQ(timeline.inc(100), 0);
-
- ASSERT_EQ(fenceKill.wait(-1), -1);
- ASSERT_EQ(errno, ENOENT);
- }
- };
-
- // Wait for the thread to spool up.
- fenceSig.wait();
-
- // Kill the timeline.
- timeline.destroy();
-
- // wait for the thread to clean up.
- waitThread.join();
-}
-
TEST(FenceTest, PollOnDestroyedTimeline) {
SyncTimeline timeline;
ASSERT_TRUE(timeline.isValid());
@@ -391,8 +364,7 @@
struct pollfd fds;
fds.fd = fenceKill.getFd();
fds.events = POLLIN | POLLERR;
- ASSERT_EQ(poll(&fds, 1, -1), 1);
- ASSERT_TRUE(fds.revents & POLLERR);
+ ASSERT_EQ(poll(&fds, 1, 0), 0);
}
};
@@ -564,7 +536,7 @@
ASSERT_TRUE(fence.isValid());
unordered_map<int, int> fenceMap;
- fenceMap.insert(make_tuple(0, 0));
+ fenceMap.insert(make_pair(0, 0));
// Randomly create syncpoints out of a fixed set of timelines, and merge them together.
for (int i = 0; i < mergeCount; i++) {
@@ -577,12 +549,12 @@
// Keep track of the latest syncpoint in each timeline.
auto itr = fenceMap.find(timelineOffset);
if (itr == end(fenceMap)) {
- fenceMap.insert(tie(timelineOffset, syncPoint));
+ fenceMap.insert(make_pair(timelineOffset, syncPoint));
}
else {
int oldSyncPoint = itr->second;
fenceMap.erase(itr);
- fenceMap.insert(tie(timelineOffset, max(syncPoint, oldSyncPoint)));
+ fenceMap.insert(make_pair(timelineOffset, max(syncPoint, oldSyncPoint)));
}
// Merge.
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
index 7bf53e3..330d6cb 100644
--- a/libsysutils/Android.mk
+++ b/libsysutils/Android.mk
@@ -21,5 +21,7 @@
liblog \
libnl
+LOCAL_EXPORT_C_INCLUDE_DIRS := system/core/libsysutils/include
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/include/sysutils/FrameworkClient.h b/libsysutils/include/sysutils/FrameworkClient.h
similarity index 100%
rename from include/sysutils/FrameworkClient.h
rename to libsysutils/include/sysutils/FrameworkClient.h
diff --git a/include/sysutils/FrameworkCommand.h b/libsysutils/include/sysutils/FrameworkCommand.h
similarity index 100%
rename from include/sysutils/FrameworkCommand.h
rename to libsysutils/include/sysutils/FrameworkCommand.h
diff --git a/libsysutils/include/sysutils/FrameworkListener.h b/libsysutils/include/sysutils/FrameworkListener.h
new file mode 100644
index 0000000..2137069
--- /dev/null
+++ b/libsysutils/include/sysutils/FrameworkListener.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 _FRAMEWORKSOCKETLISTENER_H
+#define _FRAMEWORKSOCKETLISTENER_H
+
+#include "SocketListener.h"
+#include "FrameworkCommand.h"
+
+class SocketClient;
+
+class FrameworkListener : public SocketListener {
+public:
+ static const int CMD_ARGS_MAX = 26;
+
+ /* 1 out of errorRate will be dropped */
+ int errorRate;
+
+private:
+ int mCommandCount;
+ bool mWithSeq;
+ FrameworkCommandCollection *mCommands;
+ bool mSkipToNextNullByte;
+
+public:
+ FrameworkListener(const char *socketName);
+ FrameworkListener(const char *socketName, bool withSeq);
+ FrameworkListener(int sock);
+ virtual ~FrameworkListener() {}
+
+protected:
+ void registerCmd(FrameworkCommand *cmd);
+ virtual bool onDataAvailable(SocketClient *c);
+
+private:
+ void dispatchCommand(SocketClient *c, char *data);
+ void init(const char *socketName, bool withSeq);
+};
+#endif
diff --git a/include/sysutils/List.h b/libsysutils/include/sysutils/List.h
similarity index 100%
rename from include/sysutils/List.h
rename to libsysutils/include/sysutils/List.h
diff --git a/libsysutils/include/sysutils/NetlinkEvent.h b/libsysutils/include/sysutils/NetlinkEvent.h
new file mode 100644
index 0000000..b80f3ea
--- /dev/null
+++ b/libsysutils/include/sysutils/NetlinkEvent.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 _NETLINKEVENT_H
+#define _NETLINKEVENT_H
+
+#include <sysutils/NetlinkListener.h>
+
+#define NL_PARAMS_MAX 32
+
+class NetlinkEvent {
+public:
+ enum class Action {
+ kUnknown = 0,
+ kAdd = 1,
+ kRemove = 2,
+ kChange = 3,
+ kLinkUp = 4,
+ kLinkDown = 5,
+ kAddressUpdated = 6,
+ kAddressRemoved = 7,
+ kRdnss = 8,
+ kRouteUpdated = 9,
+ kRouteRemoved = 10,
+ };
+
+private:
+ int mSeq;
+ char *mPath;
+ Action mAction;
+ char *mSubsystem;
+ char *mParams[NL_PARAMS_MAX];
+
+public:
+ NetlinkEvent();
+ virtual ~NetlinkEvent();
+
+ bool decode(char *buffer, int size, int format = NetlinkListener::NETLINK_FORMAT_ASCII);
+ const char *findParam(const char *paramName);
+
+ const char *getSubsystem() { return mSubsystem; }
+ Action getAction() { return mAction; }
+
+ void dump();
+
+ protected:
+ bool parseBinaryNetlinkMessage(char *buffer, int size);
+ bool parseAsciiNetlinkMessage(char *buffer, int size);
+ bool parseIfInfoMessage(const struct nlmsghdr *nh);
+ bool parseIfAddrMessage(const struct nlmsghdr *nh);
+ bool parseUlogPacketMessage(const struct nlmsghdr *nh);
+ bool parseNfPacketMessage(struct nlmsghdr *nh);
+ bool parseRtMessage(const struct nlmsghdr *nh);
+ bool parseNdUserOptMessage(const struct nlmsghdr *nh);
+};
+
+#endif
diff --git a/include/sysutils/NetlinkListener.h b/libsysutils/include/sysutils/NetlinkListener.h
similarity index 100%
rename from include/sysutils/NetlinkListener.h
rename to libsysutils/include/sysutils/NetlinkListener.h
diff --git a/include/sysutils/ServiceManager.h b/libsysutils/include/sysutils/ServiceManager.h
similarity index 100%
rename from include/sysutils/ServiceManager.h
rename to libsysutils/include/sysutils/ServiceManager.h
diff --git a/include/sysutils/SocketClient.h b/libsysutils/include/sysutils/SocketClient.h
similarity index 100%
rename from include/sysutils/SocketClient.h
rename to libsysutils/include/sysutils/SocketClient.h
diff --git a/include/sysutils/SocketClientCommand.h b/libsysutils/include/sysutils/SocketClientCommand.h
similarity index 100%
rename from include/sysutils/SocketClientCommand.h
rename to libsysutils/include/sysutils/SocketClientCommand.h
diff --git a/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h
similarity index 100%
rename from include/sysutils/SocketListener.h
rename to libsysutils/include/sysutils/SocketListener.h
diff --git a/libsysutils/src/FrameworkClient.cpp b/libsysutils/src/FrameworkClient.cpp
index 2f37055..72b3d0a 100644
--- a/libsysutils/src/FrameworkClient.cpp
+++ b/libsysutils/src/FrameworkClient.cpp
@@ -1,11 +1,27 @@
-#include <alloca.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <pthread.h>
+/*
+ * Copyright (C) 2009-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#define LOG_TAG "FrameworkClient"
-#include <cutils/log.h>
+#include <alloca.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/types.h>
+
+#include <android/log.h>
#include <sysutils/FrameworkClient.h>
FrameworkClient::FrameworkClient(int socket) {
diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp
index 0b95a81..a6c4abc 100644
--- a/libsysutils/src/FrameworkCommand.cpp
+++ b/libsysutils/src/FrameworkCommand.cpp
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <errno.h>
#define LOG_TAG "FrameworkCommand"
-#include <cutils/log.h>
+#include <errno.h>
+#include <log/log.h>
#include <sysutils/FrameworkCommand.h>
#define UNUSED __attribute__((unused))
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index e7b3dd6..1b6076f 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -13,16 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
#define LOG_TAG "FrameworkListener"
-#include <cutils/log.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
-#include <sysutils/FrameworkListener.h>
+#include <log/log.h>
#include <sysutils/FrameworkCommand.h>
+#include <sysutils/FrameworkListener.h>
#include <sysutils/SocketClient.h>
static const int CMD_BUF_SIZE = 1024;
@@ -49,6 +50,7 @@
errorRate = 0;
mCommandCount = 0;
mWithSeq = withSeq;
+ mSkipToNextNullByte = false;
}
bool FrameworkListener::onDataAvailable(SocketClient *c) {
@@ -59,10 +61,15 @@
if (len < 0) {
SLOGE("read() failed (%s)", strerror(errno));
return false;
- } else if (!len)
+ } else if (!len) {
return false;
- if(buffer[len-1] != '\0')
+ } else if (buffer[len-1] != '\0') {
SLOGW("String is not zero-terminated");
+ android_errorWriteLog(0x534e4554, "29831647");
+ c->sendMsg(500, "Command too large for buffer", false);
+ mSkipToNextNullByte = true;
+ return false;
+ }
int offset = 0;
int i;
@@ -70,11 +77,16 @@
for (i = 0; i < len; i++) {
if (buffer[i] == '\0') {
/* IMPORTANT: dispatchCommand() expects a zero-terminated string */
- dispatchCommand(c, buffer + offset);
+ if (mSkipToNextNullByte) {
+ mSkipToNextNullByte = false;
+ } else {
+ dispatchCommand(c, buffer + offset);
+ }
offset = i + 1;
}
}
+ mSkipToNextNullByte = false;
return true;
}
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 909df86..fef801a 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -13,54 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <stdlib.h>
-#include <string.h>
#define LOG_TAG "NetlinkEvent"
-#include <cutils/log.h>
-#include <sysutils/NetlinkEvent.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/icmp6.h>
#include <arpa/inet.h>
-#include <net/if.h>
-
#include <linux/if.h>
#include <linux/if_addr.h>
#include <linux/if_link.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_log.h>
#include <linux/netfilter_ipv4/ipt_ULOG.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <netlink/attr.h>
+#include <netlink/genl/genl.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
/* From kernel's net/netfilter/xt_quota2.c */
const int LOCAL_QLOG_NL_EVENT = 112;
const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-#include <netlink/attr.h>
-#include <netlink/genl/genl.h>
-#include <netlink/handlers.h>
-#include <netlink/msg.h>
-
-const int NetlinkEvent::NlActionUnknown = 0;
-const int NetlinkEvent::NlActionAdd = 1;
-const int NetlinkEvent::NlActionRemove = 2;
-const int NetlinkEvent::NlActionChange = 3;
-const int NetlinkEvent::NlActionLinkUp = 4;
-const int NetlinkEvent::NlActionLinkDown = 5;
-const int NetlinkEvent::NlActionAddressUpdated = 6;
-const int NetlinkEvent::NlActionAddressRemoved = 7;
-const int NetlinkEvent::NlActionRdnss = 8;
-const int NetlinkEvent::NlActionRouteUpdated = 9;
-const int NetlinkEvent::NlActionRouteRemoved = 10;
+#include <log/log.h>
+#include <sysutils/NetlinkEvent.h>
NetlinkEvent::NetlinkEvent() {
- mAction = NlActionUnknown;
+ mAction = Action::kUnknown;
memset(mParams, 0, sizeof(mParams));
mPath = NULL;
mSubsystem = NULL;
@@ -154,8 +139,8 @@
switch(rta->rta_type) {
case IFLA_IFNAME:
asprintf(&mParams[0], "INTERFACE=%s", (char *) RTA_DATA(rta));
- mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? NlActionLinkUp :
- NlActionLinkDown;
+ mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? Action::kLinkUp :
+ Action::kLinkDown;
mSubsystem = strdup("net");
return true;
}
@@ -171,7 +156,7 @@
struct ifaddrmsg *ifaddr = (struct ifaddrmsg *) NLMSG_DATA(nh);
struct ifa_cacheinfo *cacheinfo = NULL;
char addrstr[INET6_ADDRSTRLEN] = "";
- char ifname[IFNAMSIZ];
+ char ifname[IFNAMSIZ] = "";
if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
return false;
@@ -219,8 +204,7 @@
// Find the interface name.
if (!if_indextoname(ifaddr->ifa_index, ifname)) {
- SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype);
- return false;
+ SLOGD("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype);
}
} else if (rta->rta_type == IFA_CACHEINFO) {
@@ -244,11 +228,10 @@
}
// Fill in netlink event information.
- mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated :
- NlActionAddressRemoved;
+ mAction = (type == RTM_NEWADDR) ? Action::kAddressUpdated :
+ Action::kAddressRemoved;
mSubsystem = strdup("net");
- asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr,
- ifaddr->ifa_prefixlen);
+ asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, ifaddr->ifa_prefixlen);
asprintf(&mParams[1], "INTERFACE=%s", ifname);
asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
@@ -276,7 +259,7 @@
asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
asprintf(&mParams[1], "INTERFACE=%s", devname);
mSubsystem = strdup("qlog");
- mAction = NlActionChange;
+ mAction = Action::kChange;
return true;
}
@@ -311,7 +294,7 @@
asprintf(&mParams[0], "UID=%d", uid);
mParams[1] = hex;
mSubsystem = strdup("strict");
- mAction = NlActionChange;
+ mAction = Action::kChange;
return true;
}
@@ -397,8 +380,8 @@
return false;
// Fill in netlink event information.
- mAction = (type == RTM_NEWROUTE) ? NlActionRouteUpdated :
- NlActionRouteRemoved;
+ mAction = (type == RTM_NEWROUTE) ? Action::kRouteUpdated :
+ Action::kRouteRemoved;
mSubsystem = strdup("net");
asprintf(&mParams[0], "ROUTE=%s/%d", dst, prefixLength);
asprintf(&mParams[1], "GATEWAY=%s", (*gw) ? gw : "");
@@ -467,25 +450,27 @@
SLOGE("Invalid optlen %d for RDNSS option\n", optlen);
return false;
}
- int numaddrs = (optlen - 1) / 2;
+ const int numaddrs = (optlen - 1) / 2;
// Find the lifetime.
struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
- uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
+ const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
// Construct "SERVERS=<comma-separated string of DNS addresses>".
- // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the
- // the last address are followed by ','; the last is followed by '\0'.
static const char kServerTag[] = "SERVERS=";
- static const int kTagLength = sizeof(kServerTag) - 1;
- int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1);
+ static const size_t kTagLength = strlen(kServerTag);
+ // Reserve sufficient space for an IPv6 link-local address: all but the
+ // last address are followed by ','; the last is followed by '\0'.
+ static const size_t kMaxSingleAddressLength =
+ INET6_ADDRSTRLEN + strlen("%") + IFNAMSIZ + strlen(",");
+ const size_t bufsize = kTagLength + numaddrs * kMaxSingleAddressLength;
char *buf = (char *) malloc(bufsize);
if (!buf) {
SLOGE("RDNSS option: out of memory\n");
return false;
}
strcpy(buf, kServerTag);
- int pos = kTagLength;
+ size_t pos = kTagLength;
struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
for (int i = 0; i < numaddrs; i++) {
@@ -494,10 +479,14 @@
}
inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos);
pos += strlen(buf + pos);
+ if (IN6_IS_ADDR_LINKLOCAL(addrs + i)) {
+ buf[pos++] = '%';
+ pos += strlcpy(buf + pos, ifname, bufsize - pos);
+ }
}
buf[pos] = '\0';
- mAction = NlActionRdnss;
+ mAction = Action::kRdnss;
mSubsystem = strdup("net");
asprintf(&mParams[0], "INTERFACE=%s", ifname);
asprintf(&mParams[1], "LIFETIME=%u", lifetime);
@@ -617,11 +606,11 @@
const char* a;
if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
if (!strcmp(a, "add"))
- mAction = NlActionAdd;
+ mAction = Action::kAdd;
else if (!strcmp(a, "remove"))
- mAction = NlActionRemove;
+ mAction = Action::kRemove;
else if (!strcmp(a, "change"))
- mAction = NlActionChange;
+ mAction = Action::kChange;
} else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
mSeq = atoi(a);
} else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
index 637aa1e..896dad3 100644
--- a/libsysutils/src/NetlinkListener.cpp
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -13,17 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <errno.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <string.h>
#define LOG_TAG "NetlinkListener"
-#include <cutils/log.h>
-#include <cutils/uevent.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/netlink.h> /* out of order because must follow sys/socket.h */
+
+#include <cutils/uevent.h>
+#include <log/log.h>
#include <sysutils/NetlinkEvent.h>
#if 1
diff --git a/libsysutils/src/ServiceManager.cpp b/libsysutils/src/ServiceManager.cpp
index 41ac1dd..13bac09 100644
--- a/libsysutils/src/ServiceManager.cpp
+++ b/libsysutils/src/ServiceManager.cpp
@@ -1,11 +1,29 @@
-#include <errno.h>
-#include <string.h>
-
-#include <sysutils/ServiceManager.h>
+/*
+ * Copyright (C) 2009-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#define LOG_TAG "Service"
-#include <cutils/log.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
#include <cutils/properties.h>
+#include <log/log.h>
+#include <sysutils/ServiceManager.h>
ServiceManager::ServiceManager() {
}
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index bb9b6a1..971f908 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -1,16 +1,33 @@
+/*
+ * Copyright (C) 2009-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SocketClient"
+
#include <alloca.h>
+#include <arpa/inet.h>
#include <errno.h>
#include <malloc.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
-#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <unistd.h>
-#define LOG_TAG "SocketClient"
-#include <cutils/log.h>
-
+#include <log/log.h>
#include <sysutils/SocketClient.h>
SocketClient::SocketClient(int socket, bool owned) {
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 527a6a0..3f8f3db 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -13,19 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <stdio.h>
+
+#define LOG_TAG "SocketListener"
+
#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <sys/socket.h>
#include <sys/select.h>
+#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
+#include <unistd.h>
-#define LOG_TAG "SocketListener"
-#include <cutils/log.h>
#include <cutils/sockets.h>
-
+#include <log/log.h>
#include <sysutils/SocketListener.h>
#include <sysutils/SocketClient.h>
@@ -86,6 +88,7 @@
return -1;
}
SLOGV("got mSock = %d for %s", mSock, mSocketName);
+ fcntl(mSock, F_SETFD, FD_CLOEXEC);
}
if (mListen && listen(mSock, backlog) < 0) {
@@ -198,15 +201,7 @@
continue;
}
if (mListen && FD_ISSET(mSock, &read_fds)) {
- struct sockaddr addr;
- socklen_t alen;
- int c;
-
- do {
- alen = sizeof(addr);
- c = accept(mSock, &addr, &alen);
- SLOGV("%s got %d from accept", mSocketName, c);
- } while (c < 0 && errno == EINTR);
+ int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));
if (c < 0) {
SLOGE("accept failed (%s)", strerror(errno));
sleep(1);
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
new file mode 100644
index 0000000..9bb1304
--- /dev/null
+++ b/libunwindstack/Android.bp
@@ -0,0 +1,132 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "libunwindstack_flags",
+
+ host_supported: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_defaults {
+ name: "libunwindstack_common",
+ defaults: ["libunwindstack_flags"],
+
+ srcs: [
+ "ArmExidx.cpp",
+ "Memory.cpp",
+ "Log.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+}
+
+cc_library {
+ name: "libunwindstack",
+ defaults: ["libunwindstack_common"],
+}
+
+cc_library {
+ name: "libunwindstack_debug",
+ defaults: ["libunwindstack_common"],
+
+ cflags: [
+ "-UNDEBUG",
+ "-O0",
+ "-g",
+ ],
+}
+
+//-------------------------------------------------------------------------
+// Unit Tests
+//-------------------------------------------------------------------------
+cc_defaults {
+ name: "libunwindstack_test_common",
+ defaults: ["libunwindstack_flags"],
+
+ srcs: [
+ "tests/ArmExidxDecodeTest.cpp",
+ "tests/ArmExidxExtractTest.cpp",
+ "tests/LogFake.cpp",
+ "tests/MemoryFake.cpp",
+ "tests/MemoryFileTest.cpp",
+ "tests/MemoryLocalTest.cpp",
+ "tests/MemoryRangeTest.cpp",
+ "tests/MemoryRemoteTest.cpp",
+ "tests/RegsTest.cpp",
+ ],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ target: {
+ linux: {
+ host_ldlibs: [
+ "-lrt",
+ ],
+ },
+ },
+}
+
+// These unit tests run against the shared library.
+cc_test {
+ name: "libunwindstack_test",
+ defaults: ["libunwindstack_test_common"],
+
+ shared_libs: [
+ "libunwindstack",
+ ],
+}
+
+// These unit tests run against the static debug library.
+cc_test {
+ name: "libunwindstack_test_debug",
+ defaults: ["libunwindstack_test_common"],
+
+ static_libs: [
+ "libunwindstack_debug",
+ ],
+}
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
new file mode 100644
index 0000000..3b78918
--- /dev/null
+++ b/libunwindstack/ArmExidx.cpp
@@ -0,0 +1,680 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#include "ArmExidx.h"
+#include "Log.h"
+#include "Machine.h"
+
+void ArmExidx::LogRawData() {
+ std::string log_str("Raw Data:");
+ for (const uint8_t data : data_) {
+ log_str += android::base::StringPrintf(" 0x%02x", data);
+ }
+ log(log_indent_, log_str.c_str());
+}
+
+bool ArmExidx::ExtractEntryData(uint32_t entry_offset) {
+ data_.clear();
+ status_ = ARM_STATUS_NONE;
+
+ if (entry_offset & 1) {
+ // The offset needs to be at least two byte aligned.
+ status_ = ARM_STATUS_INVALID_ALIGNMENT;
+ return false;
+ }
+
+ // Each entry is a 32 bit prel31 offset followed by 32 bits
+ // of unwind information. If bit 31 of the unwind data is zero,
+ // then this is a prel31 offset to the start of the unwind data.
+ // If the unwind data is 1, then this is a cant unwind entry.
+ // Otherwise, this data is the compact form of the unwind information.
+ uint32_t data;
+ if (!elf_memory_->Read32(entry_offset + 4, &data)) {
+ status_ = ARM_STATUS_READ_FAILED;
+ return false;
+ }
+ if (data == 1) {
+ // This is a CANT UNWIND entry.
+ status_ = ARM_STATUS_NO_UNWIND;
+ if (log_) {
+ log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+ log(log_indent_, "[cantunwind]");
+ }
+ return false;
+ }
+
+ if (data & (1UL << 31)) {
+ // This is a compact table entry.
+ if ((data >> 24) & 0xf) {
+ // This is a non-zero index, this code doesn't support
+ // other formats.
+ status_ = ARM_STATUS_INVALID_PERSONALITY;
+ return false;
+ }
+ data_.push_back((data >> 16) & 0xff);
+ data_.push_back((data >> 8) & 0xff);
+ uint8_t last_op = data & 0xff;
+ data_.push_back(last_op);
+ if (last_op != ARM_OP_FINISH) {
+ // If this didn't end with a finish op, add one.
+ data_.push_back(ARM_OP_FINISH);
+ }
+ if (log_) {
+ LogRawData();
+ }
+ return true;
+ }
+
+ // Get the address of the ops.
+ // Sign extend the data value if necessary.
+ int32_t signed_data = static_cast<int32_t>(data << 1) >> 1;
+ uint32_t addr = (entry_offset + 4) + signed_data;
+ if (!elf_memory_->Read32(addr, &data)) {
+ status_ = ARM_STATUS_READ_FAILED;
+ return false;
+ }
+
+ size_t num_table_words;
+ if (data & (1UL << 31)) {
+ // Compact model.
+ switch ((data >> 24) & 0xf) {
+ case 0:
+ num_table_words = 0;
+ data_.push_back((data >> 16) & 0xff);
+ break;
+ case 1:
+ case 2:
+ num_table_words = (data >> 16) & 0xff;
+ addr += 4;
+ break;
+ default:
+ // Only a personality of 0, 1, 2 is valid.
+ status_ = ARM_STATUS_INVALID_PERSONALITY;
+ return false;
+ }
+ data_.push_back((data >> 8) & 0xff);
+ data_.push_back(data & 0xff);
+ } else {
+ // Generic model.
+
+ // Skip the personality routine data, it doesn't contain any data
+ // needed to decode the unwind information.
+ addr += 4;
+ if (!elf_memory_->Read32(addr, &data)) {
+ status_ = ARM_STATUS_READ_FAILED;
+ return false;
+ }
+ num_table_words = (data >> 24) & 0xff;
+ data_.push_back((data >> 16) & 0xff);
+ data_.push_back((data >> 8) & 0xff);
+ data_.push_back(data & 0xff);
+ addr += 4;
+ }
+
+ if (num_table_words > 5) {
+ status_ = ARM_STATUS_MALFORMED;
+ return false;
+ }
+
+ for (size_t i = 0; i < num_table_words; i++) {
+ if (!elf_memory_->Read32(addr, &data)) {
+ status_ = ARM_STATUS_READ_FAILED;
+ return false;
+ }
+ data_.push_back((data >> 24) & 0xff);
+ data_.push_back((data >> 16) & 0xff);
+ data_.push_back((data >> 8) & 0xff);
+ data_.push_back(data & 0xff);
+ addr += 4;
+ }
+
+ if (data_.back() != ARM_OP_FINISH) {
+ // If this didn't end with a finish op, add one.
+ data_.push_back(ARM_OP_FINISH);
+ }
+
+ if (log_) {
+ LogRawData();
+ }
+ return true;
+}
+
+inline bool ArmExidx::GetByte(uint8_t* byte) {
+ if (data_.empty()) {
+ status_ = ARM_STATUS_TRUNCATED;
+ return false;
+ }
+ *byte = data_.front();
+ data_.pop_front();
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) {
+ assert((byte >> 4) == 0x8);
+
+ uint16_t registers = (byte & 0xf) << 8;
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ registers |= byte;
+ if (registers == 0) {
+ // 10000000 00000000: Refuse to unwind
+ if (log_) {
+ log(log_indent_, "Refuse to unwind");
+ }
+ status_ = ARM_STATUS_NO_UNWIND;
+ return false;
+ }
+ // 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
+ if (log_) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t i = 0; i < 12; i++) {
+ if (registers & (1 << i)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("r%zu", i + 4);
+ add_comma = true;
+ }
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+
+ registers <<= 4;
+ for (size_t reg = 4; reg < 16; reg++) {
+ if (registers & (1 << reg)) {
+ if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
+ status_ = ARM_STATUS_READ_FAILED;
+ return false;
+ }
+ cfa_ += 4;
+ }
+ }
+ // If the sp register is modified, change the cfa value.
+ if (registers & (1 << ARM_REG_SP)) {
+ cfa_ = (*regs_)[ARM_REG_SP];
+ }
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
+ assert((byte >> 4) == 0x9);
+
+ uint8_t bits = byte & 0xf;
+ if (bits == 13 || bits == 15) {
+ // 10011101: Reserved as prefix for ARM register to register moves
+ // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
+ if (log_) {
+ log(log_indent_, "[Reserved]");
+ }
+ status_ = ARM_STATUS_RESERVED;
+ return false;
+ }
+ // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
+ if (log_) {
+ log(log_indent_, "vsp = r%d", bits);
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // It is impossible for bits to be larger than the total number of
+ // arm registers, so don't bother checking if bits is a valid register.
+ cfa_ = (*regs_)[bits];
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) {
+ assert((byte >> 4) == 0xa);
+
+ // 10100nnn: Pop r4-r[4+nnn]
+ // 10101nnn: Pop r4-r[4+nnn], r14
+ if (log_) {
+ std::string msg = "pop {r4";
+ uint8_t end_reg = byte & 0x7;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-r%d", 4 + end_reg);
+ }
+ if (byte & 0x8) {
+ log(log_indent_, "%s, r14}", msg.c_str());
+ } else {
+ log(log_indent_, "%s}", msg.c_str());
+ }
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+
+ for (size_t i = 4; i <= 4 + (byte & 0x7); i++) {
+ if (!process_memory_->Read32(cfa_, &(*regs_)[i])) {
+ status_ = ARM_STATUS_READ_FAILED;
+ return false;
+ }
+ cfa_ += 4;
+ }
+ if (byte & 0x8) {
+ if (!process_memory_->Read32(cfa_, &(*regs_)[ARM_REG_R14])) {
+ status_ = ARM_STATUS_READ_FAILED;
+ return false;
+ }
+ cfa_ += 4;
+ }
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0000() {
+ // 10110000: Finish
+ if (log_) {
+ log(log_indent_, "finish");
+ if (log_skip_execution_) {
+ status_ = ARM_STATUS_FINISH;
+ return false;
+ }
+ }
+ if (!(*regs_)[ARM_REG_PC]) {
+ (*regs_)[ARM_REG_PC] = (*regs_)[ARM_REG_LR];
+ }
+ status_ = ARM_STATUS_FINISH;
+ return false;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0001() {
+ uint8_t byte;
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (byte == 0) {
+ // 10110001 00000000: Spare
+ if (log_) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+ if (byte >> 4) {
+ // 10110001 xxxxyyyy: Spare (xxxx != 0000)
+ if (log_) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+
+ // 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
+ if (log_) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t i = 0; i < 4; i++) {
+ if (byte & (1 << i)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("r%zu", i);
+ add_comma = true;
+ }
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+
+ for (size_t reg = 0; reg < 4; reg++) {
+ if (byte & (1 << reg)) {
+ if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
+ status_ = ARM_STATUS_READ_FAILED;
+ return false;
+ }
+ cfa_ += 4;
+ }
+ }
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0010() {
+ // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
+ uint32_t result = 0;
+ uint32_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ result <<= 2;
+ if (log_) {
+ log(log_indent_, "vsp = vsp + %d", 0x204 + result);
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ cfa_ += 0x204 + result;
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0011() {
+ // 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
+ uint8_t byte;
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (log_) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+ uint8_t end_reg = start_reg + (byte & 0xf);
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ cfa_ += (byte & 0xf) * 8 + 12;
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_01nn() {
+ // 101101nn: Spare
+ if (log_) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
+ assert((byte & ~0x07) == 0xb8);
+
+ // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
+ if (log_) {
+ std::string msg = "pop {d8";
+ uint8_t last_reg = (byte & 0x7);
+ if (last_reg) {
+ msg += android::base::StringPrintf("-d%d", last_reg + 8);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0x7) * 8 + 12;
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10(uint8_t byte) {
+ assert((byte >> 6) == 0x2);
+
+ switch ((byte >> 4) & 0x3) {
+ case 0:
+ return DecodePrefix_10_00(byte);
+ case 1:
+ return DecodePrefix_10_01(byte);
+ case 2:
+ return DecodePrefix_10_10(byte);
+ default:
+ switch (byte & 0xf) {
+ case 0:
+ return DecodePrefix_10_11_0000();
+ case 1:
+ return DecodePrefix_10_11_0001();
+ case 2:
+ return DecodePrefix_10_11_0010();
+ case 3:
+ return DecodePrefix_10_11_0011();
+ default:
+ if (byte & 0x8) {
+ return DecodePrefix_10_11_1nnn(byte);
+ } else {
+ return DecodePrefix_10_11_01nn();
+ }
+ }
+ }
+}
+
+inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) {
+ assert((byte & ~0x07) == 0xc0);
+
+ uint8_t bits = byte & 0x7;
+ if (bits == 6) {
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
+ if (log_) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0xf) * 8 + 8;
+ } else if (bits == 7) {
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (byte == 0) {
+ // 11000111 00000000: Spare
+ if (log_) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ } else if ((byte >> 4) == 0) {
+ // 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
+ if (log_) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t i = 0; i < 4; i++) {
+ if (byte & (1 << i)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("wCGR%zu", i);
+ add_comma = true;
+ }
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ }
+ // Only update the cfa.
+ cfa_ += __builtin_popcount(byte) * 4;
+ } else {
+ // 11000111 xxxxyyyy: Spare (xxxx != 0000)
+ if (log_) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+ } else {
+ // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
+ if (log_) {
+ std::string msg = "pop {wR10";
+ uint8_t nnn = byte & 0x7;
+ if (nnn) {
+ msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0x7) * 8 + 8;
+ }
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) {
+ assert((byte & ~0x07) == 0xc8);
+
+ uint8_t bits = byte & 0x7;
+ if (bits == 0) {
+ // 11001000 sssscccc: Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] by VPUSH
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (log_) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0xf) * 8 + 8;
+ } else if (bits == 1) {
+ // 11001001 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by VPUSH
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (log_) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0xf) * 8 + 8;
+ } else {
+ // 11001yyy: Spare (yyy != 000, 001)
+ if (log_) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
+ assert((byte & ~0x07) == 0xd0);
+
+ // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
+ if (log_) {
+ std::string msg = "pop {d8";
+ uint8_t end_reg = byte & 0x7;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ cfa_ += (byte & 0x7) * 8 + 8;
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
+ assert((byte >> 6) == 0x3);
+
+ switch ((byte >> 3) & 0x7) {
+ case 0:
+ return DecodePrefix_11_000(byte);
+ case 1:
+ return DecodePrefix_11_001(byte);
+ case 2:
+ return DecodePrefix_11_010(byte);
+ default:
+ // 11xxxyyy: Spare (xxx != 000, 001, 010)
+ if (log_) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+}
+
+bool ArmExidx::Decode() {
+ status_ = ARM_STATUS_NONE;
+ uint8_t byte;
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ switch (byte >> 6) {
+ case 0:
+ // 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
+ if (log_) {
+ log(log_indent_, "vsp = vsp + %d", ((byte & 0x3f) << 2) + 4);
+ if (log_skip_execution_) {
+ break;
+ }
+ }
+ cfa_ += ((byte & 0x3f) << 2) + 4;
+ break;
+ case 1:
+ // 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
+ if (log_) {
+ log(log_indent_, "vsp = vsp - %d", ((byte & 0x3f) << 2) + 4);
+ if (log_skip_execution_) {
+ break;
+ }
+ }
+ cfa_ -= ((byte & 0x3f) << 2) + 4;
+ break;
+ case 2:
+ return DecodePrefix_10(byte);
+ default:
+ return DecodePrefix_11(byte);
+ }
+ return true;
+}
+
+bool ArmExidx::Eval() {
+ while (Decode());
+ return status_ == ARM_STATUS_FINISH;
+}
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
new file mode 100644
index 0000000..a92caef
--- /dev/null
+++ b/libunwindstack/ArmExidx.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_ARM_EXIDX_H
+#define _LIBUNWINDSTACK_ARM_EXIDX_H
+
+#include <stdint.h>
+
+#include <deque>
+
+#include "Memory.h"
+#include "Regs.h"
+
+enum ArmStatus : size_t {
+ ARM_STATUS_NONE = 0,
+ ARM_STATUS_NO_UNWIND,
+ ARM_STATUS_FINISH,
+ ARM_STATUS_RESERVED,
+ ARM_STATUS_SPARE,
+ ARM_STATUS_TRUNCATED,
+ ARM_STATUS_READ_FAILED,
+ ARM_STATUS_MALFORMED,
+ ARM_STATUS_INVALID_ALIGNMENT,
+ ARM_STATUS_INVALID_PERSONALITY,
+};
+
+enum ArmOp : uint8_t {
+ ARM_OP_FINISH = 0xb0,
+};
+
+class ArmExidx {
+ public:
+ ArmExidx(Regs32* regs, Memory* elf_memory, Memory* process_memory)
+ : regs_(regs), elf_memory_(elf_memory), process_memory_(process_memory) {}
+ virtual ~ArmExidx() {}
+
+ void LogRawData();
+
+ bool ExtractEntryData(uint32_t entry_offset);
+
+ bool Eval();
+
+ bool Decode();
+
+ std::deque<uint8_t>* data() { return &data_; }
+
+ ArmStatus status() { return status_; }
+
+ Regs32* regs() { return regs_; }
+
+ uint32_t cfa() { return cfa_; }
+ void set_cfa(uint32_t cfa) { cfa_ = cfa; }
+
+ void set_log(bool log) { log_ = log; }
+ void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
+ void set_log_indent(uint8_t indent) { log_indent_ = indent; }
+
+ private:
+ bool GetByte(uint8_t* byte);
+
+ bool DecodePrefix_10_00(uint8_t byte);
+ bool DecodePrefix_10_01(uint8_t byte);
+ bool DecodePrefix_10_10(uint8_t byte);
+ bool DecodePrefix_10_11_0000();
+ bool DecodePrefix_10_11_0001();
+ bool DecodePrefix_10_11_0010();
+ bool DecodePrefix_10_11_0011();
+ bool DecodePrefix_10_11_01nn();
+ bool DecodePrefix_10_11_1nnn(uint8_t byte);
+ bool DecodePrefix_10(uint8_t byte);
+
+ bool DecodePrefix_11_000(uint8_t byte);
+ bool DecodePrefix_11_001(uint8_t byte);
+ bool DecodePrefix_11_010(uint8_t byte);
+ bool DecodePrefix_11(uint8_t byte);
+
+ Regs32* regs_ = nullptr;
+ uint32_t cfa_ = 0;
+ std::deque<uint8_t> data_;
+ ArmStatus status_ = ARM_STATUS_NONE;
+
+ Memory* elf_memory_;
+ Memory* process_memory_;
+
+ bool log_ = false;
+ uint8_t log_indent_ = 0;
+ bool log_skip_execution_ = false;
+};
+
+#endif // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/Log.cpp b/libunwindstack/Log.cpp
new file mode 100644
index 0000000..faeb66c
--- /dev/null
+++ b/libunwindstack/Log.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <string>
+
+#define LOG_TAG "unwind"
+#include <log/log.h>
+
+#include <android-base/stringprintf.h>
+
+#include "Log.h"
+
+static bool g_print_to_stdout = false;
+
+void log_to_stdout(bool enable) {
+ g_print_to_stdout = enable;
+}
+
+// Send the data to the log.
+void log(uint8_t indent, const char* format, ...) {
+ std::string real_format;
+ if (indent > 0) {
+ real_format = android::base::StringPrintf("%*s%s", 2 * indent, " ", format);
+ } else {
+ real_format = format;
+ }
+ va_list args;
+ va_start(args, format);
+ if (g_print_to_stdout) {
+ real_format += '\n';
+ vprintf(real_format.c_str(), args);
+ } else {
+ LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, real_format.c_str(), args);
+ }
+ va_end(args);
+}
diff --git a/libunwindstack/Log.h b/libunwindstack/Log.h
new file mode 100644
index 0000000..2d01aa8
--- /dev/null
+++ b/libunwindstack/Log.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_LOG_H
+#define _LIBUNWINDSTACK_LOG_H
+
+#include <stdint.h>
+
+void log_to_stdout(bool enable);
+void log(uint8_t indent, const char* format, ...);
+
+#endif // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h
new file mode 100644
index 0000000..db84271
--- /dev/null
+++ b/libunwindstack/Machine.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_H
+#define _LIBUNWINDSTACK_MACHINE_H
+
+#include <stdint.h>
+
+class Regs;
+
+enum ArmReg : uint16_t {
+ ARM_REG_R0 = 0,
+ ARM_REG_R1,
+ ARM_REG_R2,
+ ARM_REG_R3,
+ ARM_REG_R4,
+ ARM_REG_R5,
+ ARM_REG_R6,
+ ARM_REG_R7,
+ ARM_REG_R8,
+ ARM_REG_R9,
+ ARM_REG_R10,
+ ARM_REG_R11,
+ ARM_REG_R12,
+ ARM_REG_R13,
+ ARM_REG_R14,
+ ARM_REG_R15,
+ ARM_REG_LAST,
+
+ ARM_REG_SP = ARM_REG_R13,
+ ARM_REG_LR = ARM_REG_R14,
+ ARM_REG_PC = ARM_REG_R15,
+};
+
+enum Arm64Reg : uint16_t {
+ ARM64_REG_R0 = 0,
+ ARM64_REG_R1,
+ ARM64_REG_R2,
+ ARM64_REG_R3,
+ ARM64_REG_R4,
+ ARM64_REG_R5,
+ ARM64_REG_R6,
+ ARM64_REG_R7,
+ ARM64_REG_R8,
+ ARM64_REG_R9,
+ ARM64_REG_R10,
+ ARM64_REG_R11,
+ ARM64_REG_R12,
+ ARM64_REG_R13,
+ ARM64_REG_R14,
+ ARM64_REG_R15,
+ ARM64_REG_R16,
+ ARM64_REG_R17,
+ ARM64_REG_R18,
+ ARM64_REG_R19,
+ ARM64_REG_R20,
+ ARM64_REG_R21,
+ ARM64_REG_R22,
+ ARM64_REG_R23,
+ ARM64_REG_R24,
+ ARM64_REG_R25,
+ ARM64_REG_R26,
+ ARM64_REG_R27,
+ ARM64_REG_R28,
+ ARM64_REG_R29,
+ ARM64_REG_R30,
+ ARM64_REG_R31,
+ ARM64_REG_PC,
+ ARM64_REG_LAST,
+
+ ARM64_REG_SP = ARM64_REG_R31,
+ ARM64_REG_LR = ARM64_REG_R30,
+};
+
+enum X86Reg : uint16_t {
+ X86_REG_EAX = 0,
+ X86_REG_ECX,
+ X86_REG_EDX,
+ X86_REG_EBX,
+ X86_REG_ESP,
+ X86_REG_EBP,
+ X86_REG_ESI,
+ X86_REG_EDI,
+ X86_REG_EIP,
+ X86_REG_EFL,
+ X86_REG_CS,
+ X86_REG_SS,
+ X86_REG_DS,
+ X86_REG_ES,
+ X86_REG_FS,
+ X86_REG_GS,
+ X86_REG_LAST,
+
+ X86_REG_SP = X86_REG_ESP,
+ X86_REG_PC = X86_REG_EIP,
+};
+
+enum X86_64Reg : uint16_t {
+ X86_64_REG_RAX = 0,
+ X86_64_REG_RDX,
+ X86_64_REG_RCX,
+ X86_64_REG_RBX,
+ X86_64_REG_RSI,
+ X86_64_REG_RDI,
+ X86_64_REG_RBP,
+ X86_64_REG_RSP,
+ X86_64_REG_R8,
+ X86_64_REG_R9,
+ X86_64_REG_R10,
+ X86_64_REG_R11,
+ X86_64_REG_R12,
+ X86_64_REG_R13,
+ X86_64_REG_R14,
+ X86_64_REG_R15,
+ X86_64_REG_RIP,
+ X86_64_REG_LAST,
+
+ X86_64_REG_SP = X86_64_REG_RSP,
+ X86_64_REG_PC = X86_64_REG_RIP,
+};
+
+#endif // _LIBUNWINDSTACK_MACHINE_H
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
new file mode 100644
index 0000000..336e4fe
--- /dev/null
+++ b/libunwindstack/Memory.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+
+#include "Memory.h"
+
+bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
+ string->clear();
+ uint64_t bytes_read = 0;
+ while (bytes_read < max_read) {
+ uint8_t value;
+ if (!Read(addr, &value, sizeof(value))) {
+ return false;
+ }
+ if (value == '\0') {
+ return true;
+ }
+ string->push_back(value);
+ addr++;
+ bytes_read++;
+ }
+ return false;
+}
+
+MemoryFileAtOffset::~MemoryFileAtOffset() {
+ if (data_) {
+ munmap(&data_[-offset_], size_ + offset_);
+ data_ = nullptr;
+ }
+}
+
+bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ return false;
+ }
+ struct stat buf;
+ if (fstat(fd, &buf) == -1) {
+ return false;
+ }
+ if (offset >= static_cast<uint64_t>(buf.st_size)) {
+ return false;
+ }
+
+ offset_ = offset & (getpagesize() - 1);
+ uint64_t aligned_offset = offset & ~(getpagesize() - 1);
+ size_ = buf.st_size - aligned_offset;
+ void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
+ if (map == MAP_FAILED) {
+ return false;
+ }
+
+ data_ = &reinterpret_cast<uint8_t*>(map)[offset_];
+ size_ -= offset_;
+
+ return true;
+}
+
+bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
+ if (addr + size > size_) {
+ return false;
+ }
+ memcpy(dst, &data_[addr], size);
+ return true;
+}
+
+static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
+ // ptrace() returns -1 and sets errno when the operation fails.
+ // To disambiguate -1 from a valid result, we clear errno beforehand.
+ errno = 0;
+ *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+ if (*value == -1 && errno) {
+ return false;
+ }
+ return true;
+}
+
+bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
+ size_t bytes_read = 0;
+ long data;
+ size_t align_bytes = addr & (sizeof(long) - 1);
+ if (align_bytes != 0) {
+ if (!PtraceRead(pid_, addr & ~(sizeof(long) - 1), &data)) {
+ return false;
+ }
+ size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
+ memcpy(dst, reinterpret_cast<uint8_t*>(&data) + align_bytes, copy_bytes);
+ addr += copy_bytes;
+ dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + copy_bytes);
+ bytes -= copy_bytes;
+ bytes_read += copy_bytes;
+ }
+
+ for (size_t i = 0; i < bytes / sizeof(long); i++) {
+ if (!PtraceRead(pid_, addr, &data)) {
+ return false;
+ }
+ memcpy(dst, &data, sizeof(long));
+ dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
+ addr += sizeof(long);
+ bytes_read += sizeof(long);
+ }
+
+ size_t left_over = bytes & (sizeof(long) - 1);
+ if (left_over) {
+ if (!PtraceRead(pid_, addr, &data)) {
+ return false;
+ }
+ memcpy(dst, &data, left_over);
+ bytes_read += left_over;
+ }
+ return true;
+}
+
+bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
+ // The process_vm_readv call does will not always work on remote
+ // processes, so only use it for reads from the current pid.
+ // Use this method to avoid crashes if an address is invalid since
+ // unwind data could try to access any part of the address space.
+ struct iovec local_io;
+ local_io.iov_base = dst;
+ local_io.iov_len = size;
+
+ struct iovec remote_io;
+ remote_io.iov_base = reinterpret_cast<void*>(static_cast<uintptr_t>(addr));
+ remote_io.iov_len = size;
+
+ ssize_t bytes_read = process_vm_readv(getpid(), &local_io, 1, &remote_io, 1, 0);
+ if (bytes_read == -1) {
+ return false;
+ }
+ return static_cast<size_t>(bytes_read) == size;
+}
+
+bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
+ if (!MemoryFileAtOffset::Init(file, offset)) {
+ return false;
+ }
+ // The first uint64_t value is the start of memory.
+ if (!MemoryFileAtOffset::Read(0, &start_, sizeof(start_))) {
+ return false;
+ }
+ // Subtract the first 64 bit value from the total size.
+ size_ -= sizeof(start_);
+ return true;
+}
+
+bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
+ if (addr < start_ || addr + size > start_ + offset_ + size_) {
+ return false;
+ }
+ memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
+ return true;
+}
diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h
new file mode 100644
index 0000000..5ab031d
--- /dev/null
+++ b/libunwindstack/Memory.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_H
+#define _LIBUNWINDSTACK_MEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+constexpr bool kMemoryStatsEnabled = true;
+
+class Memory {
+ public:
+ Memory() = default;
+ virtual ~Memory() = default;
+
+ virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+
+ virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
+
+ inline bool Read(uint64_t addr, void* start, void* field, size_t size) {
+ return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start),
+ field, size);
+ }
+
+ inline bool Read32(uint64_t addr, uint32_t* dst) {
+ return Read(addr, dst, sizeof(uint32_t));
+ }
+
+ inline bool Read64(uint64_t addr, uint64_t* dst) {
+ return Read(addr, dst, sizeof(uint64_t));
+ }
+};
+
+class MemoryFileAtOffset : public Memory {
+ public:
+ MemoryFileAtOffset() = default;
+ virtual ~MemoryFileAtOffset();
+
+ bool Init(const std::string& file, uint64_t offset);
+
+ bool Read(uint64_t addr, void* dst, size_t size) override;
+
+ protected:
+ size_t size_ = 0;
+ size_t offset_ = 0;
+ uint8_t* data_ = nullptr;
+};
+
+class MemoryOffline : public MemoryFileAtOffset {
+ public:
+ MemoryOffline() = default;
+ virtual ~MemoryOffline() = default;
+
+ bool Init(const std::string& file, uint64_t offset);
+
+ bool Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ uint64_t start_;
+};
+
+class MemoryRemote : public Memory {
+ public:
+ MemoryRemote(pid_t pid) : pid_(pid) {}
+ virtual ~MemoryRemote() = default;
+
+ bool Read(uint64_t addr, void* dst, size_t size) override;
+
+ pid_t pid() { return pid_; }
+
+ private:
+ pid_t pid_;
+};
+
+class MemoryLocal : public Memory {
+ public:
+ MemoryLocal() = default;
+ virtual ~MemoryLocal() = default;
+
+ bool Read(uint64_t addr, void* dst, size_t size) override;
+};
+
+class MemoryRange : public Memory {
+ public:
+ MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
+ : memory_(memory), begin_(begin), length_(end - begin_) {}
+ virtual ~MemoryRange() { delete memory_; }
+
+ inline bool Read(uint64_t addr, void* dst, size_t size) override {
+ if (addr + size <= length_) {
+ return memory_->Read(addr + begin_, dst, size);
+ }
+ return false;
+ }
+
+ private:
+ Memory* memory_;
+ uint64_t begin_;
+ uint64_t length_;
+};
+
+#endif // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/Regs.h b/libunwindstack/Regs.h
new file mode 100644
index 0000000..2766c6f
--- /dev/null
+++ b/libunwindstack/Regs.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_H
+#define _LIBUNWINDSTACK_REGS_H
+
+#include <stdint.h>
+
+#include <vector>
+
+class Regs {
+ public:
+ Regs(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
+ : pc_reg_(pc_reg), sp_reg_(sp_reg), total_regs_(total_regs) {
+ }
+ virtual ~Regs() = default;
+
+ uint16_t pc_reg() { return pc_reg_; }
+ uint16_t sp_reg() { return sp_reg_; }
+ uint16_t total_regs() { return total_regs_; }
+
+ virtual void* raw_data() = 0;
+ virtual uint64_t pc() = 0;
+ virtual uint64_t sp() = 0;
+
+ protected:
+ uint16_t pc_reg_;
+ uint16_t sp_reg_;
+ uint16_t total_regs_;
+};
+
+template <typename AddressType>
+class RegsTmpl : public Regs {
+ public:
+ RegsTmpl(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
+ : Regs(pc_reg, sp_reg, total_regs), regs_(total_regs) {}
+ virtual ~RegsTmpl() = default;
+
+ uint64_t pc() override { return regs_[pc_reg_]; }
+ uint64_t sp() override { return regs_[sp_reg_]; }
+
+ inline AddressType& operator[](size_t reg) { return regs_[reg]; }
+
+ void* raw_data() override { return regs_.data(); }
+
+ private:
+ std::vector<AddressType> regs_;
+};
+
+class Regs32 : public RegsTmpl<uint32_t> {
+ public:
+ Regs32(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
+ : RegsTmpl(pc_reg, sp_reg, total_regs) {}
+ virtual ~Regs32() = default;
+};
+
+class Regs64 : public RegsTmpl<uint64_t> {
+ public:
+ Regs64(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
+ : RegsTmpl(pc_reg, sp_reg, total_regs) {}
+ virtual ~Regs64() = default;
+};
+
+#endif // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
new file mode 100644
index 0000000..9ea917a
--- /dev/null
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <ios>
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "ArmExidx.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
+ protected:
+ void Init(Memory* process_memory = nullptr) {
+ TearDown();
+
+ if (process_memory == nullptr) {
+ process_memory = &process_memory_;
+ }
+
+ regs32_.reset(new Regs32(0, 1, 32));
+ for (size_t i = 0; i < 32; i++) {
+ (*regs32_)[i] = 0;
+ }
+
+ exidx_.reset(new ArmExidx(regs32_.get(), &elf_memory_, process_memory));
+ if (log_) {
+ exidx_->set_log(true);
+ exidx_->set_log_indent(0);
+ exidx_->set_log_skip_execution(false);
+ }
+ data_ = exidx_->data();
+ exidx_->set_cfa(0x10000);
+ }
+
+ void SetUp() override {
+ if (GetParam() != "no_logging") {
+ log_ = false;
+ } else {
+ log_ = true;
+ }
+ ResetLogs();
+ elf_memory_.Clear();
+ process_memory_.Clear();
+ Init();
+ }
+
+ std::unique_ptr<ArmExidx> exidx_;
+ std::unique_ptr<Regs32> regs32_;
+ std::deque<uint8_t>* data_;
+
+ MemoryFake elf_memory_;
+ MemoryFake process_memory_;
+ bool log_;
+};
+
+TEST_P(ArmExidxDecodeTest, vsp_incr) {
+ // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+
+ ResetLogs();
+ data_->clear();
+ data_->push_back(0x01);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+ ResetLogs();
+ data_->clear();
+ data_->push_back(0x3f);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x1010cU, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, vsp_decr) {
+ // 01xxxxxx: vsp = vsp - (xxxxxx << 2) + 4
+ data_->push_back(0x40);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0xfffcU, exidx_->cfa());
+
+ ResetLogs();
+ data_->clear();
+ data_->push_back(0x41);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0xfff4U, exidx_->cfa());
+
+ ResetLogs();
+ data_->clear();
+ data_->push_back(0x7f);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0xfef4U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, refuse_unwind) {
+ // 10000000 00000000: Refuse to unwind
+ data_->push_back(0x80);
+ data_->push_back(0x00);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_up_to_12) {
+ // 1000iiii iiiiiiii: Pop up to 12 integer registers
+ data_->push_back(0x80);
+ data_->push_back(0x01);
+ process_memory_.SetData(0x10000, 0x10);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[4]);
+
+ ResetLogs();
+ data_->push_back(0x8f);
+ data_->push_back(0xff);
+ for (size_t i = 0; i < 12; i++) {
+ process_memory_.SetData(0x10004 + i * 4, i + 0x20);
+ }
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
+ GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ // Popping r13 results in a modified cfa.
+ ASSERT_EQ(0x29U, exidx_->cfa());
+
+ ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x21U, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x22U, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x23U, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x24U, (*exidx_->regs())[8]);
+ ASSERT_EQ(0x25U, (*exidx_->regs())[9]);
+ ASSERT_EQ(0x26U, (*exidx_->regs())[10]);
+ ASSERT_EQ(0x27U, (*exidx_->regs())[11]);
+ ASSERT_EQ(0x28U, (*exidx_->regs())[12]);
+ ASSERT_EQ(0x29U, (*exidx_->regs())[13]);
+ ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
+ ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
+
+ ResetLogs();
+ exidx_->set_cfa(0x10034);
+ data_->push_back(0x81);
+ data_->push_back(0x28);
+ process_memory_.SetData(0x10034, 0x11);
+ process_memory_.SetData(0x10038, 0x22);
+ process_memory_.SetData(0x1003c, 0x33);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10040U, exidx_->cfa());
+ ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x22U, (*exidx_->regs())[9]);
+ ASSERT_EQ(0x33U, (*exidx_->regs())[12]);
+}
+
+TEST_P(ArmExidxDecodeTest, set_vsp_from_register) {
+ // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
+ exidx_->set_cfa(0x100);
+ for (size_t i = 0; i < 15; i++) {
+ (*regs32_)[i] = i + 1;
+ }
+
+ data_->push_back(0x90);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(1U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0x93);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(4U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0x9e);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(15U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, reserved_prefix) {
+ // 10011101: Reserved as prefix for ARM register to register moves
+ data_->push_back(0x9d);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
+
+ // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
+ ResetLogs();
+ data_->push_back(0x9f);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers) {
+ // 10100nnn: Pop r4-r[4+nnn]
+ data_->push_back(0xa0);
+ process_memory_.SetData(0x10000, 0x14);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+ ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
+
+ ResetLogs();
+ data_->push_back(0xa3);
+ process_memory_.SetData(0x10004, 0x20);
+ process_memory_.SetData(0x10008, 0x30);
+ process_memory_.SetData(0x1000c, 0x40);
+ process_memory_.SetData(0x10010, 0x50);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10014U, exidx_->cfa());
+ ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
+
+ ResetLogs();
+ data_->push_back(0xa7);
+ process_memory_.SetData(0x10014, 0x41);
+ process_memory_.SetData(0x10018, 0x51);
+ process_memory_.SetData(0x1001c, 0x61);
+ process_memory_.SetData(0x10020, 0x71);
+ process_memory_.SetData(0x10024, 0x81);
+ process_memory_.SetData(0x10028, 0x91);
+ process_memory_.SetData(0x1002c, 0xa1);
+ process_memory_.SetData(0x10030, 0xb1);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10034U, exidx_->cfa());
+ ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x71U, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x81U, (*exidx_->regs())[8]);
+ ASSERT_EQ(0x91U, (*exidx_->regs())[9]);
+ ASSERT_EQ(0xa1U, (*exidx_->regs())[10]);
+ ASSERT_EQ(0xb1U, (*exidx_->regs())[11]);
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers_with_r14) {
+ // 10101nnn: Pop r4-r[4+nnn], r14
+ data_->push_back(0xa8);
+ process_memory_.SetData(0x10000, 0x12);
+ process_memory_.SetData(0x10004, 0x22);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+ ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
+
+ ResetLogs();
+ data_->push_back(0xab);
+ process_memory_.SetData(0x10008, 0x1);
+ process_memory_.SetData(0x1000c, 0x2);
+ process_memory_.SetData(0x10010, 0x3);
+ process_memory_.SetData(0x10014, 0x4);
+ process_memory_.SetData(0x10018, 0x5);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x1001cU, exidx_->cfa());
+ ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
+
+ ResetLogs();
+ data_->push_back(0xaf);
+ process_memory_.SetData(0x1001c, 0x1a);
+ process_memory_.SetData(0x10020, 0x2a);
+ process_memory_.SetData(0x10024, 0x3a);
+ process_memory_.SetData(0x10028, 0x4a);
+ process_memory_.SetData(0x1002c, 0x5a);
+ process_memory_.SetData(0x10030, 0x6a);
+ process_memory_.SetData(0x10034, 0x7a);
+ process_memory_.SetData(0x10038, 0x8a);
+ process_memory_.SetData(0x1003c, 0x9a);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10040U, exidx_->cfa());
+ ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x4aU, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x5aU, (*exidx_->regs())[8]);
+ ASSERT_EQ(0x6aU, (*exidx_->regs())[9]);
+ ASSERT_EQ(0x7aU, (*exidx_->regs())[10]);
+ ASSERT_EQ(0x8aU, (*exidx_->regs())[11]);
+ ASSERT_EQ(0x9aU, (*exidx_->regs())[14]);
+}
+
+TEST_P(ArmExidxDecodeTest, finish) {
+ // 10110000: Finish
+ data_->push_back(0xb0);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa());
+ ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, spare) {
+ // 10110001 00000000: Spare
+ data_->push_back(0xb1);
+ data_->push_back(0x00);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa());
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+
+ // 10110001 xxxxyyyy: Spare (xxxx != 0000)
+ for (size_t x = 1; x < 16; x++) {
+ for (size_t y = 0; y < 16; y++) {
+ ResetLogs();
+ data_->push_back(0xb1);
+ data_->push_back((x << 4) | y);
+ ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+ if (log_) {
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+ }
+
+ // 101101nn: Spare
+ for (size_t n = 0; n < 4; n++) {
+ ResetLogs();
+ data_->push_back(0xb4 | n);
+ ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
+ ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
+ if (log_) {
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+
+ // 11000111 00000000: Spare
+ ResetLogs();
+ data_->push_back(0xc7);
+ data_->push_back(0x00);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa());
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+
+ // 11000111 xxxxyyyy: Spare (xxxx != 0000)
+ for (size_t x = 1; x < 16; x++) {
+ for (size_t y = 0; y < 16; y++) {
+ ResetLogs();
+ data_->push_back(0xc7);
+ data_->push_back(0x10);
+ ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+ if (log_) {
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+ }
+
+ // 11001yyy: Spare (yyy != 000, 001)
+ for (size_t y = 2; y < 8; y++) {
+ ResetLogs();
+ data_->push_back(0xc8 | y);
+ ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
+ ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
+ if (log_) {
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+
+ // 11xxxyyy: Spare (xxx != 000, 001, 010)
+ for (size_t x = 3; x < 8; x++) {
+ for (size_t y = 0; y < 8; y++) {
+ ResetLogs();
+ data_->push_back(0xc0 | (x << 3) | y);
+ ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+ if (log_) {
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+ }
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers_under_mask) {
+ // 10110001 0000iiii: Pop integer registers {r0, r1, r2, r3}
+ data_->push_back(0xb1);
+ data_->push_back(0x01);
+ process_memory_.SetData(0x10000, 0x45);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+ ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
+
+ ResetLogs();
+ data_->push_back(0xb1);
+ data_->push_back(0x0a);
+ process_memory_.SetData(0x10004, 0x23);
+ process_memory_.SetData(0x10008, 0x24);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x1000cU, exidx_->cfa());
+ ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
+ ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
+
+ ResetLogs();
+ data_->push_back(0xb1);
+ data_->push_back(0x0f);
+ process_memory_.SetData(0x1000c, 0x65);
+ process_memory_.SetData(0x10010, 0x54);
+ process_memory_.SetData(0x10014, 0x43);
+ process_memory_.SetData(0x10018, 0x32);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x1001cU, exidx_->cfa());
+ ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
+ ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
+ ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
+ ASSERT_EQ(0x32U, (*exidx_->regs())[3]);
+}
+
+TEST_P(ArmExidxDecodeTest, vsp_large_incr) {
+ // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
+ data_->push_back(0xb2);
+ data_->push_back(0x7f);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10400U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xb2);
+ data_->push_back(0xff);
+ data_->push_back(0x02);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10c00U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xb2);
+ data_->push_back(0xff);
+ data_->push_back(0x82);
+ data_->push_back(0x30);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x311400U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
+ // 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
+ data_->push_back(0xb3);
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xb3);
+ data_->push_back(0x48);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10058U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
+ // 10111nnn: Pop VFP double precision registers D[8]-D[8+nnn] by FSTMFDX
+ data_->push_back(0xb8);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xbb);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10030U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xbf);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10074U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
+ // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
+ data_->push_back(0xc0);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc2);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10020U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc5);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10050U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
+ // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
+ data_->push_back(0xc6);
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc6);
+ data_->push_back(0x25);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10038U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc6);
+ data_->push_back(0xff);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x100b8U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
+ // 11000111 0000iiii: Intel Wireless MMX pop wCGR registes {wCGR0,1,2,3}
+ data_->push_back(0xc7);
+ data_->push_back(0x01);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc7);
+ data_->push_back(0x0a);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc7);
+ data_->push_back(0x0f);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x1001cU, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
+ // 11001000 sssscccc: Pop VFP double precision registers d[16+ssss]-D[16+ssss+cccc] by VPUSH
+ data_->push_back(0xc8);
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc8);
+ data_->push_back(0x14);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10030U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc8);
+ data_->push_back(0xff);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x100b0U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
+ // 11001001 sssscccc: Pop VFP double precision registers d[ssss]-D[ssss+cccc] by VPUSH
+ data_->push_back(0xc9);
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc9);
+ data_->push_back(0x23);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10028U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xc9);
+ data_->push_back(0xff);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x100a8U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
+ // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
+ data_->push_back(0xd0);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xd2);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10020U, exidx_->cfa());
+
+ ResetLogs();
+ data_->push_back(0xd7);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ if (log_) {
+ ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+ } else {
+ ASSERT_EQ("", GetFakeLogPrint());
+ }
+ ASSERT_EQ(0x10060U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, expect_truncated) {
+ // This test verifies that any op that requires extra ops will
+ // fail if the data is not present.
+ data_->push_back(0x80);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xb1);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xb2);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xb3);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xc6);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xc7);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xc8);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xc9);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, verify_no_truncated) {
+ // This test verifies that no pattern results in a crash or truncation.
+ MemoryFakeAlwaysReadZero memory_zero;
+ Init(&memory_zero);
+
+ for (size_t x = 0; x < 256; x++) {
+ if (x == 0xb2) {
+ // This opcode is followed by an uleb128, so just skip this one.
+ continue;
+ }
+ for (size_t y = 0; y < 256; y++) {
+ data_->clear();
+ data_->push_back(x);
+ data_->push_back(y);
+ if (!exidx_->Decode()) {
+ ASSERT_NE(ARM_STATUS_TRUNCATED, exidx_->status())
+ << "x y = 0x" << std::hex << x << " 0x" << y;
+ ASSERT_NE(ARM_STATUS_READ_FAILED, exidx_->status())
+ << "x y = 0x" << std::hex << x << " 0x" << y;
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
new file mode 100644
index 0000000..021765a
--- /dev/null
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ArmExidx.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+class ArmExidxExtractTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ elf_memory_.Clear();
+ exidx_ = new ArmExidx(nullptr, &elf_memory_, nullptr);
+ data_ = exidx_->data();
+ data_->clear();
+ }
+
+ void TearDown() override {
+ delete exidx_;
+ }
+
+ ArmExidx* exidx_ = nullptr;
+ std::deque<uint8_t>* data_;
+ MemoryFake elf_memory_;
+};
+
+TEST_F(ArmExidxExtractTest, bad_alignment) {
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x1001));
+ ASSERT_EQ(ARM_STATUS_INVALID_ALIGNMENT, exidx_->status());
+ ASSERT_TRUE(data_->empty());
+}
+
+TEST_F(ArmExidxExtractTest, cant_unwind) {
+ elf_memory_.SetData(0x1000, 0x7fff2340);
+ elf_memory_.SetData(0x1004, 1);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
+ ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+ ASSERT_TRUE(data_->empty());
+}
+
+TEST_F(ArmExidxExtractTest, compact) {
+ elf_memory_.SetData(0x4000, 0x7ffa3000);
+ elf_memory_.SetData(0x4004, 0x80a8b0b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
+ ASSERT_EQ(3U, data_->size());
+ ASSERT_EQ(0xa8, data_->at(0));
+ ASSERT_EQ(0xb0, data_->at(1));
+ ASSERT_EQ(0xb0, data_->at(2));
+
+ // Missing finish gets added.
+ elf_memory_.Clear();
+ elf_memory_.SetData(0x534, 0x7ffa3000);
+ elf_memory_.SetData(0x538, 0x80a1a2a3);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x534));
+ ASSERT_EQ(4U, data_->size());
+ ASSERT_EQ(0xa1, data_->at(0));
+ ASSERT_EQ(0xa2, data_->at(1));
+ ASSERT_EQ(0xa3, data_->at(2));
+ ASSERT_EQ(0xb0, data_->at(3));
+}
+
+TEST_F(ArmExidxExtractTest, compact_non_zero_personality) {
+ elf_memory_.SetData(0x4000, 0x7ffa3000);
+
+ uint32_t compact_value = 0x80a8b0b0;
+ for (size_t i = 1; i < 16; i++) {
+ elf_memory_.SetData(0x4004, compact_value | (i << 24));
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x4000));
+ ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+ }
+}
+
+TEST_F(ArmExidxExtractTest, second_read_compact_personality_1_2) {
+ elf_memory_.SetData(0x5000, 0x1234);
+ elf_memory_.SetData(0x5004, 0x00001230);
+ elf_memory_.SetData(0x6234, 0x8100f3b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(2U, data_->size());
+ ASSERT_EQ(0xf3, data_->at(0));
+ ASSERT_EQ(0xb0, data_->at(1));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData(0x5000, 0x1234);
+ elf_memory_.SetData(0x5004, 0x00001230);
+ elf_memory_.SetData(0x6234, 0x8200f3f4);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(3U, data_->size());
+ ASSERT_EQ(0xf3, data_->at(0));
+ ASSERT_EQ(0xf4, data_->at(1));
+ ASSERT_EQ(0xb0, data_->at(2));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData(0x5000, 0x1234);
+ elf_memory_.SetData(0x5004, 0x00001230);
+ elf_memory_.SetData(0x6234, 0x8201f3f4);
+ elf_memory_.SetData(0x6238, 0x102030b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(6U, data_->size());
+ ASSERT_EQ(0xf3, data_->at(0));
+ ASSERT_EQ(0xf4, data_->at(1));
+ ASSERT_EQ(0x10, data_->at(2));
+ ASSERT_EQ(0x20, data_->at(3));
+ ASSERT_EQ(0x30, data_->at(4));
+ ASSERT_EQ(0xb0, data_->at(5));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData(0x5000, 0x1234);
+ elf_memory_.SetData(0x5004, 0x00001230);
+ elf_memory_.SetData(0x6234, 0x8103f3f4);
+ elf_memory_.SetData(0x6238, 0x10203040);
+ elf_memory_.SetData(0x623c, 0x50607080);
+ elf_memory_.SetData(0x6240, 0x90a0c0d0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(15U, data_->size());
+ ASSERT_EQ(0xf3, data_->at(0));
+ ASSERT_EQ(0xf4, data_->at(1));
+ ASSERT_EQ(0x10, data_->at(2));
+ ASSERT_EQ(0x20, data_->at(3));
+ ASSERT_EQ(0x30, data_->at(4));
+ ASSERT_EQ(0x40, data_->at(5));
+ ASSERT_EQ(0x50, data_->at(6));
+ ASSERT_EQ(0x60, data_->at(7));
+ ASSERT_EQ(0x70, data_->at(8));
+ ASSERT_EQ(0x80, data_->at(9));
+ ASSERT_EQ(0x90, data_->at(10));
+ ASSERT_EQ(0xa0, data_->at(11));
+ ASSERT_EQ(0xc0, data_->at(12));
+ ASSERT_EQ(0xd0, data_->at(13));
+ ASSERT_EQ(0xb0, data_->at(14));
+}
+
+TEST_F(ArmExidxExtractTest, second_read_compact_personality_illegal) {
+ elf_memory_.SetData(0x5000, 0x7ffa1e48);
+ elf_memory_.SetData(0x5004, 0x1230);
+ elf_memory_.SetData(0x6234, 0x832132b0);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+
+ elf_memory_.Clear();
+ elf_memory_.SetData(0x5000, 0x7ffa1e48);
+ elf_memory_.SetData(0x5004, 0x1230);
+ elf_memory_.SetData(0x6234, 0x842132b0);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, second_read_offset_is_negative) {
+ elf_memory_.SetData(0x5000, 0x7ffa1e48);
+ elf_memory_.SetData(0x5004, 0x7fffb1e0);
+ elf_memory_.SetData(0x1e4, 0x842132b0);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, second_read_not_compact) {
+ elf_memory_.SetData(0x5000, 0x1234);
+ elf_memory_.SetData(0x5004, 0x00001230);
+ elf_memory_.SetData(0x6234, 0x1);
+ elf_memory_.SetData(0x6238, 0x001122b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(3U, data_->size());
+ ASSERT_EQ(0x11, data_->at(0));
+ ASSERT_EQ(0x22, data_->at(1));
+ ASSERT_EQ(0xb0, data_->at(2));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData(0x5000, 0x1234);
+ elf_memory_.SetData(0x5004, 0x00001230);
+ elf_memory_.SetData(0x6234, 0x2);
+ elf_memory_.SetData(0x6238, 0x00112233);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(4U, data_->size());
+ ASSERT_EQ(0x11, data_->at(0));
+ ASSERT_EQ(0x22, data_->at(1));
+ ASSERT_EQ(0x33, data_->at(2));
+ ASSERT_EQ(0xb0, data_->at(3));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData(0x5000, 0x1234);
+ elf_memory_.SetData(0x5004, 0x00001230);
+ elf_memory_.SetData(0x6234, 0x3);
+ elf_memory_.SetData(0x6238, 0x01112233);
+ elf_memory_.SetData(0x623c, 0x445566b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(7U, data_->size());
+ ASSERT_EQ(0x11, data_->at(0));
+ ASSERT_EQ(0x22, data_->at(1));
+ ASSERT_EQ(0x33, data_->at(2));
+ ASSERT_EQ(0x44, data_->at(3));
+ ASSERT_EQ(0x55, data_->at(4));
+ ASSERT_EQ(0x66, data_->at(5));
+ ASSERT_EQ(0xb0, data_->at(6));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData(0x5000, 0x1234);
+ elf_memory_.SetData(0x5004, 0x00001230);
+ elf_memory_.SetData(0x6234, 0x3);
+ elf_memory_.SetData(0x6238, 0x05112233);
+ elf_memory_.SetData(0x623c, 0x01020304);
+ elf_memory_.SetData(0x6240, 0x05060708);
+ elf_memory_.SetData(0x6244, 0x090a0b0c);
+ elf_memory_.SetData(0x6248, 0x0d0e0f10);
+ elf_memory_.SetData(0x624c, 0x11121314);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(24U, data_->size());
+ ASSERT_EQ(0x11, data_->at(0));
+ ASSERT_EQ(0x22, data_->at(1));
+ ASSERT_EQ(0x33, data_->at(2));
+ ASSERT_EQ(0x01, data_->at(3));
+ ASSERT_EQ(0x02, data_->at(4));
+ ASSERT_EQ(0x03, data_->at(5));
+ ASSERT_EQ(0x04, data_->at(6));
+ ASSERT_EQ(0x05, data_->at(7));
+ ASSERT_EQ(0x06, data_->at(8));
+ ASSERT_EQ(0x07, data_->at(9));
+ ASSERT_EQ(0x08, data_->at(10));
+ ASSERT_EQ(0x09, data_->at(11));
+ ASSERT_EQ(0x0a, data_->at(12));
+ ASSERT_EQ(0x0b, data_->at(13));
+ ASSERT_EQ(0x0c, data_->at(14));
+ ASSERT_EQ(0x0d, data_->at(15));
+ ASSERT_EQ(0x0e, data_->at(16));
+ ASSERT_EQ(0x0f, data_->at(17));
+ ASSERT_EQ(0x10, data_->at(18));
+ ASSERT_EQ(0x11, data_->at(19));
+ ASSERT_EQ(0x12, data_->at(20));
+ ASSERT_EQ(0x13, data_->at(21));
+ ASSERT_EQ(0x14, data_->at(22));
+ ASSERT_EQ(0xb0, data_->at(23));
+}
+
+TEST_F(ArmExidxExtractTest, read_failures) {
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+ elf_memory_.SetData(0x5000, 0x100);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+ elf_memory_.SetData(0x5004, 0x100);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+ elf_memory_.SetData(0x5104, 0x1);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+ elf_memory_.SetData(0x5108, 0x01010203);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, malformed) {
+ elf_memory_.SetData(0x5000, 0x100);
+ elf_memory_.SetData(0x5004, 0x100);
+ elf_memory_.SetData(0x5104, 0x1);
+ elf_memory_.SetData(0x5108, 0x06010203);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
+
+ elf_memory_.Clear();
+ elf_memory_.SetData(0x5000, 0x100);
+ elf_memory_.SetData(0x5004, 0x100);
+ elf_memory_.SetData(0x5104, 0x1);
+ elf_memory_.SetData(0x5108, 0x81060203);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, cant_unwind_log) {
+ elf_memory_.SetData(0x1000, 0x7fff2340);
+ elf_memory_.SetData(0x1004, 1);
+
+ exidx_->set_log(true);
+ exidx_->set_log_indent(0);
+ exidx_->set_log_skip_execution(false);
+
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
+ ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+
+ ASSERT_EQ("4 unwind Raw Data: 0x00 0x00 0x00 0x01\n"
+ "4 unwind [cantunwind]\n", GetFakeLogPrint());
+}
+
+TEST_F(ArmExidxExtractTest, raw_data_compact) {
+ elf_memory_.SetData(0x4000, 0x7ffa3000);
+ elf_memory_.SetData(0x4004, 0x80a8b0b0);
+
+ exidx_->set_log(true);
+ exidx_->set_log_indent(0);
+ exidx_->set_log_skip_execution(false);
+
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
+ ASSERT_EQ("4 unwind Raw Data: 0xa8 0xb0 0xb0\n", GetFakeLogPrint());
+}
+
+TEST_F(ArmExidxExtractTest, raw_data_non_compact) {
+ elf_memory_.SetData(0x5000, 0x1234);
+ elf_memory_.SetData(0x5004, 0x00001230);
+ elf_memory_.SetData(0x6234, 0x2);
+ elf_memory_.SetData(0x6238, 0x00112233);
+
+ exidx_->set_log(true);
+ exidx_->set_log_indent(0);
+ exidx_->set_log_skip_execution(false);
+
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
+}
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp
new file mode 100644
index 0000000..411594a
--- /dev/null
+++ b/libunwindstack/tests/LogFake.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+
+#include "LogFake.h"
+
+// Forward declarations.
+class Backtrace;
+struct EventTagMap;
+struct AndroidLogEntry;
+
+std::string g_fake_log_buf;
+
+std::string g_fake_log_print;
+
+void ResetLogs() {
+ g_fake_log_buf = "";
+ g_fake_log_print = "";
+}
+
+std::string GetFakeLogBuf() {
+ return g_fake_log_buf;
+}
+
+std::string GetFakeLogPrint() {
+ return g_fake_log_print;
+}
+
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+ g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
+ g_fake_log_buf += tag;
+ g_fake_log_buf += ' ';
+ g_fake_log_buf += msg;
+ return 1;
+}
+
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ int val = __android_log_vprint(prio, tag, fmt, ap);
+ va_end(ap);
+
+ return val;
+}
+
+extern "C" int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+ g_fake_log_print += std::to_string(prio) + ' ';
+ g_fake_log_print += tag;
+ g_fake_log_print += ' ';
+
+ android::base::StringAppendV(&g_fake_log_print, fmt, ap);
+
+ g_fake_log_print += '\n';
+
+ return 1;
+}
+
+extern "C" log_id_t android_name_to_log_id(const char*) {
+ return LOG_ID_SYSTEM;
+}
+
+extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+ errno = EACCES;
+ return nullptr;
+}
+
+extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
+ return 0;
+}
+
+extern "C" EventTagMap* android_openEventTagMap(const char*) {
+ return nullptr;
+}
+
+extern "C" int android_log_processBinaryLogBuffer(
+ struct logger_entry*,
+ AndroidLogEntry*, const EventTagMap*, char*, int) {
+ return 0;
+}
+
+extern "C" void android_logger_list_free(struct logger_list*) {
+}
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
new file mode 100644
index 0000000..006d393
--- /dev/null
+++ b/libunwindstack/tests/LogFake.h
@@ -0,0 +1,26 @@
+/*
+ * 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 _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+
+#include <string>
+
+void ResetLogs();
+std::string GetFakeLogBuf();
+std::string GetFakeLogPrint();
+
+#endif // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 0000000..216873f
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Maps.h"
+
+#include "LogFake.h"
+
+class MapsTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ }
+};
+
+TEST_F(MapsTest, parse_permissions) {
+ MapsBuffer maps("1000-2000 ---- 00000000 00:00 0\n"
+ "2000-3000 r--- 00000000 00:00 0\n"
+ "3000-4000 -w-- 00000000 00:00 0\n"
+ "4000-5000 --x- 00000000 00:00 0\n"
+ "5000-6000 rwx- 00000000 00:00 0\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(PROT_NONE, it->flags);
+ ASSERT_EQ(0x1000U, it->start);
+ ASSERT_EQ(0x2000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_READ, it->flags);
+ ASSERT_EQ(0x2000U, it->start);
+ ASSERT_EQ(0x3000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_WRITE, it->flags);
+ ASSERT_EQ(0x3000U, it->start);
+ ASSERT_EQ(0x4000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_EXEC, it->flags);
+ ASSERT_EQ(0x4000U, it->start);
+ ASSERT_EQ(0x5000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
+ ASSERT_EQ(0x5000U, it->start);
+ ASSERT_EQ(0x6000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+TEST_F(MapsTest, parse_name) {
+ MapsBuffer maps("720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
+ "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "720b29f000-720b2a0000 rw-p 00000000 00:00 0");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ("", it->name);
+ ASSERT_EQ(0x720b29b000U, it->start);
+ ASSERT_EQ(0x720b29e000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ("/system/lib/fake.so", it->name);
+ ASSERT_EQ(0x720b29e000U, it->start);
+ ASSERT_EQ(0x720b29f000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ("", it->name);
+ ASSERT_EQ(0x720b29f000U, it->start);
+ ASSERT_EQ(0x720b2a0000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+TEST_F(MapsTest, parse_offset) {
+ MapsBuffer maps("a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(2U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(0xa000U, it->start);
+ ASSERT_EQ(0xe000U, it->end);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ASSERT_EQ("/system/lib/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(0xa12345U, it->offset);
+ ASSERT_EQ(0xe000U, it->start);
+ ASSERT_EQ(0xf000U, it->end);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ASSERT_EQ("/system/lib/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(maps.end(), it);
+}
+
+TEST_F(MapsTest, file_smoke) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "720b29b000-720b29e000 r-xp a0000000 00:00 0 /fake.so\n"
+ "720b2b0000-720b2e0000 r-xp b0000000 00:00 0 /fake2.so\n"
+ "720b2e0000-720b2f0000 r-xp c0000000 00:00 0 /fake3.so\n",
+ tf.path, 0660, getuid(), getgid()));
+
+ MapsFile maps(tf.path);
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(0x720b29b000U, it->start);
+ ASSERT_EQ(0x720b29e000U, it->end);
+ ASSERT_EQ(0xa0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(0x720b2b0000U, it->start);
+ ASSERT_EQ(0x720b2e0000U, it->end);
+ ASSERT_EQ(0xb0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake2.so", it->name);
+ ++it;
+ ASSERT_EQ(0x720b2e0000U, it->start);
+ ASSERT_EQ(0x720b2f0000U, it->end);
+ ASSERT_EQ(0xc0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake3.so", it->name);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+TEST_F(MapsTest, find) {
+ MapsBuffer maps("1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
+ "3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n"
+ "6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n"
+ "a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n"
+ "e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n");
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5U, maps.Total());
+
+ ASSERT_TRUE(maps.Find(0x500) == nullptr);
+ ASSERT_TRUE(maps.Find(0x2000) == nullptr);
+ ASSERT_TRUE(maps.Find(0x5010) == nullptr);
+ ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
+ ASSERT_TRUE(maps.Find(0xf000) == nullptr);
+ ASSERT_TRUE(maps.Find(0xf010) == nullptr);
+
+ MapInfo* info = maps.Find(0x1000);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0x1000U, info->start);
+ ASSERT_EQ(0x2000U, info->end);
+ ASSERT_EQ(0x10U, info->offset);
+ ASSERT_EQ(PROT_READ, info->flags);
+ ASSERT_EQ("/system/lib/fake1.so", info->name);
+
+ info = maps.Find(0x3020);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0x3000U, info->start);
+ ASSERT_EQ(0x4000U, info->end);
+ ASSERT_EQ(0x20U, info->offset);
+ ASSERT_EQ(PROT_WRITE, info->flags);
+ ASSERT_EQ("/system/lib/fake2.so", info->name);
+
+ info = maps.Find(0x6020);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0x6000U, info->start);
+ ASSERT_EQ(0x8000U, info->end);
+ ASSERT_EQ(0x30U, info->offset);
+ ASSERT_EQ(PROT_EXEC, info->flags);
+ ASSERT_EQ("/system/lib/fake3.so", info->name);
+
+ info = maps.Find(0xafff);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0xa000U, info->start);
+ ASSERT_EQ(0xb000U, info->end);
+ ASSERT_EQ(0x40U, info->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
+ ASSERT_EQ("/system/lib/fake4.so", info->name);
+
+ info = maps.Find(0xe500);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0xe000U, info->start);
+ ASSERT_EQ(0xf000U, info->end);
+ ASSERT_EQ(0x50U, info->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+ ASSERT_EQ("/system/lib/fake5.so", info->name);
+}
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
new file mode 100644
index 0000000..afb1029
--- /dev/null
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "MemoryFake.h"
+
+void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
+ for (size_t i = 0; i < length; i++, addr++) {
+ auto value = data_.find(addr);
+ if (value != data_.end()) {
+ value->second = src[i];
+ } else {
+ data_.insert({ addr, src[i] });
+ }
+ }
+}
+
+bool MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
+ uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
+ for (size_t i = 0; i < size; i++, addr++) {
+ auto value = data_.find(addr);
+ if (value == data_.end()) {
+ return false;
+ }
+ dst[i] = value->second;
+ }
+ return true;
+}
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
new file mode 100644
index 0000000..4f898fa
--- /dev/null
+++ b/libunwindstack/tests/MemoryFake.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include "Memory.h"
+
+class MemoryFake : public Memory {
+ public:
+ MemoryFake() = default;
+ virtual ~MemoryFake() = default;
+
+ bool Read(uint64_t addr, void* buffer, size_t size) override;
+
+ void SetMemory(uint64_t addr, const void* memory, size_t length);
+
+ void SetData(uint64_t addr, uint32_t value) {
+ SetMemory(addr, &value, sizeof(value));
+ }
+
+ void SetMemory(uint64_t addr, std::vector<uint8_t> values) {
+ SetMemory(addr, values.data(), values.size());
+ }
+
+ void SetMemory(uint64_t addr, std::string string) {
+ SetMemory(addr, string.c_str(), string.size() + 1);
+ }
+
+ void Clear() { data_.clear(); }
+
+ private:
+ std::unordered_map<uint64_t, uint8_t> data_;
+};
+
+class MemoryFakeAlwaysReadZero : public Memory {
+ public:
+ MemoryFakeAlwaysReadZero() = default;
+ virtual ~MemoryFakeAlwaysReadZero() = default;
+
+ bool Read(uint64_t, void* buffer, size_t size) override {
+ memset(buffer, 0, size);
+ return true;
+ }
+};
+
+#endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
new file mode 100644
index 0000000..ebc6118
--- /dev/null
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/test_utils.h>
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryFileTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ tf_ = new TemporaryFile;
+ }
+
+ void TearDown() override {
+ delete tf_;
+ }
+
+ void WriteTestData() {
+ ASSERT_TRUE(android::base::WriteStringToFd("0123456789abcdefghijklmnopqrstuvxyz", tf_->fd));
+ }
+
+ MemoryFileAtOffset memory_;
+
+ TemporaryFile* tf_ = nullptr;
+};
+
+TEST_F(MemoryFileTest, offset_0) {
+ WriteTestData();
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 0));
+ std::vector<char> buffer(11);
+ ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+ buffer[10] = '\0';
+ ASSERT_STREQ("0123456789", buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_non_zero) {
+ WriteTestData();
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 10));
+ std::vector<char> buffer(11);
+ ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+ buffer[10] = '\0';
+ ASSERT_STREQ("abcdefghij", buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_non_zero_larger_than_pagesize) {
+ size_t pagesize = getpagesize();
+ std::string large_string;
+ for (size_t i = 0; i < pagesize; i++) {
+ large_string += '1';
+ }
+ large_string += "012345678901234abcdefgh";
+ ASSERT_TRUE(android::base::WriteStringToFd(large_string, tf_->fd));
+
+ ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 15));
+ std::vector<char> buffer(9);
+ ASSERT_TRUE(memory_.Read(0, buffer.data(), 8));
+ buffer[8] = '\0';
+ ASSERT_STREQ("abcdefgh", buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_pagesize_aligned) {
+ size_t pagesize = getpagesize();
+ std::string data;
+ for (size_t i = 0; i < 2 * pagesize; i++) {
+ data += static_cast<char>((i / pagesize) + '0');
+ data += static_cast<char>((i % 10) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+ ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize));
+ std::vector<char> buffer(11);
+ ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+ buffer[10] = '\0';
+ std::string expected_str;
+ for (size_t i = 0; i < 5; i++) {
+ expected_str += '1';
+ expected_str += static_cast<char>(((i + pagesize) % 10) + '0');
+ }
+ ASSERT_STREQ(expected_str.c_str(), buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) {
+ size_t pagesize = getpagesize();
+ std::string data;
+ for (size_t i = 0; i < 2 * pagesize; i++) {
+ data += static_cast<char>((i / pagesize) + '0');
+ data += static_cast<char>((i % 10) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+ ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize + 10));
+ std::vector<char> buffer(11);
+ ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+ buffer[10] = '\0';
+ std::string expected_str;
+ for (size_t i = 0; i < 5; i++) {
+ expected_str += '1';
+ expected_str += static_cast<char>(((i + pagesize + 5) % 10) + '0');
+ }
+ ASSERT_STREQ(expected_str.c_str(), buffer.data());
+}
+
+TEST_F(MemoryFileTest, read_error) {
+ std::string data;
+ for (size_t i = 0; i < 5000; i++) {
+ data += static_cast<char>((i % 10) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+ std::vector<char> buffer(100);
+
+ // Read before init.
+ ASSERT_FALSE(memory_.Read(0, buffer.data(), 10));
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 0));
+
+ ASSERT_FALSE(memory_.Read(10000, buffer.data(), 10));
+ ASSERT_FALSE(memory_.Read(5000, buffer.data(), 10));
+ ASSERT_FALSE(memory_.Read(4990, buffer.data(), 11));
+ ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
+ ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
+ ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
+}
+
+TEST_F(MemoryFileTest, read_string) {
+ std::string value("name_in_file");
+ ASSERT_TRUE(android::base::WriteFully(tf_->fd, value.c_str(), value.size() + 1));
+
+ std::string name;
+ ASSERT_TRUE(memory_.Init(tf_->path, 0));
+ ASSERT_TRUE(memory_.ReadString(0, &name));
+ ASSERT_EQ("name_in_file", name);
+ ASSERT_TRUE(memory_.ReadString(5, &name));
+ ASSERT_EQ("in_file", name);
+}
+
+TEST_F(MemoryFileTest, read_string_error) {
+ std::vector<uint8_t> buffer = { 0x23, 0x32, 0x45 };
+ ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
+
+ std::string name;
+ ASSERT_TRUE(memory_.Init(tf_->path, 0));
+
+ // Read from a non-existant address.
+ ASSERT_FALSE(memory_.ReadString(100, &name));
+
+ // This should fail because there is no terminating \0
+ ASSERT_FALSE(memory_.ReadString(0, &name));
+}
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
new file mode 100644
index 0000000..49ece9d
--- /dev/null
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryLocalTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ }
+};
+
+TEST_F(MemoryLocalTest, read) {
+ std::vector<uint8_t> src(1024);
+ memset(src.data(), 0x4c, 1024);
+
+ MemoryLocal local;
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+ ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
+ for (size_t i = 0; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]);
+ }
+
+ memset(src.data(), 0x23, 512);
+ ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+ ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
+ for (size_t i = 0; i < 512; i++) {
+ ASSERT_EQ(0x23U, dst[i]);
+ }
+ for (size_t i = 512; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]);
+ }
+}
+
+TEST_F(MemoryLocalTest, read_string) {
+ std::string name("string_in_memory");
+
+ MemoryLocal local;
+
+ std::vector<uint8_t> dst(1024);
+ std::string dst_name;
+ ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(name.c_str()), &dst_name));
+ ASSERT_EQ("string_in_memory", dst_name);
+
+ ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name));
+ ASSERT_EQ("in_memory", dst_name);
+
+ ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 10));
+ ASSERT_EQ("in_memory", dst_name);
+
+ ASSERT_FALSE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 9));
+}
+
+TEST_F(MemoryLocalTest, read_illegal) {
+ MemoryLocal local;
+
+ std::vector<uint8_t> dst(100);
+ ASSERT_FALSE(local.Read(0, dst.data(), 1));
+ ASSERT_FALSE(local.Read(0, dst.data(), 100));
+}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
new file mode 100644
index 0000000..fcae3a4
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_ = new MemoryFake;
+ }
+
+ MemoryFake* memory_;
+};
+
+TEST_F(MemoryRangeTest, read) {
+ std::vector<uint8_t> src(1024);
+ memset(src.data(), 0x4c, 1024);
+ memory_->SetMemory(9001, src);
+
+ MemoryRange range(memory_, 9001, 9001 + src.size());
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
+ for (size_t i = 0; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryRangeTest, read_near_limit) {
+ std::vector<uint8_t> src(4096);
+ memset(src.data(), 0x4c, 4096);
+ memory_->SetMemory(1000, src);
+
+ MemoryRange range(memory_, 1000, 2024);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_TRUE(range.Read(1020, dst.data(), 4));
+ for (size_t i = 0; i < 4; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+
+ // Verify that reads outside of the range will fail.
+ ASSERT_FALSE(range.Read(1020, dst.data(), 5));
+ ASSERT_FALSE(range.Read(1024, dst.data(), 1));
+ ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+}
+
+TEST_F(MemoryRangeTest, read_string_past_end) {
+ std::string name("0123456789");
+ memory_->SetMemory(0, name);
+
+ // Verify a read past the range fails.
+ MemoryRange range(memory_, 0, 5);
+ std::string dst_name;
+ ASSERT_FALSE(range.ReadString(0, &dst_name));
+}
+
+TEST_F(MemoryRangeTest, read_string_to_end) {
+ std::string name("0123456789");
+ memory_->SetMemory(30, name);
+
+ // Verify the range going to the end of the string works.
+ MemoryRange range(memory_, 30, 30 + name.size() + 1);
+ std::string dst_name;
+ ASSERT_TRUE(range.ReadString(0, &dst_name));
+ ASSERT_EQ("0123456789", dst_name);
+}
+
+TEST_F(MemoryRangeTest, read_string_fencepost) {
+ std::string name("0123456789");
+ memory_->SetMemory(10, name);
+
+ // Verify the range set to one byte less than the end of the string fails.
+ MemoryRange range(memory_, 10, 10 + name.size());
+ std::string dst_name;
+ ASSERT_FALSE(range.ReadString(0, &dst_name));
+}
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
new file mode 100644
index 0000000..49244a5
--- /dev/null
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryRemoteTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ }
+
+ static uint64_t NanoTime() {
+ struct timespec t = { 0, 0 };
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
+ }
+
+ static bool Attach(pid_t pid) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ uint64_t start = NanoTime();
+ siginfo_t si;
+ while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
+ if ((NanoTime() - start) > 10 * NS_PER_SEC) {
+ printf("%d: Failed to stop after 10 seconds.\n", pid);
+ return false;
+ }
+ usleep(30);
+ }
+ return true;
+ }
+
+ static bool Detach(pid_t pid) {
+ return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+ }
+
+ static constexpr size_t NS_PER_SEC = 1000000000ULL;
+};
+
+TEST_F(MemoryRemoteTest, read) {
+ std::vector<uint8_t> src(1024);
+ memset(src.data(), 0x4c, 1024);
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true);
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+ for (size_t i = 0; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_TRUE(Detach(pid));
+
+ kill(pid, SIGKILL);
+}
+
+TEST_F(MemoryRemoteTest, read_fail) {
+ int pagesize = getpagesize();
+ void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
+ memset(src, 0x4c, pagesize * 2);
+ ASSERT_NE(MAP_FAILED, src);
+ // Put a hole right after the first page.
+ ASSERT_EQ(0, munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(src) + pagesize),
+ pagesize));
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true);
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+
+ std::vector<uint8_t> dst(pagesize);
+ ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src), dst.data(), pagesize));
+ for (size_t i = 0; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize, dst.data(), 1));
+ ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
+ ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
+
+ ASSERT_EQ(0, munmap(src, pagesize));
+
+ ASSERT_TRUE(Detach(pid));
+
+ kill(pid, SIGKILL);
+}
+
+TEST_F(MemoryRemoteTest, read_illegal) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true);
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+
+ std::vector<uint8_t> dst(100);
+ ASSERT_FALSE(remote.Read(0, dst.data(), 1));
+ ASSERT_FALSE(remote.Read(0, dst.data(), 100));
+
+ ASSERT_TRUE(Detach(pid));
+
+ kill(pid, SIGKILL);
+}
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
new file mode 100644
index 0000000..f9e8b0e
--- /dev/null
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include "Regs.h"
+
+class RegsTest : public ::testing::Test {};
+
+TEST_F(RegsTest, regs32) {
+ Regs32 regs32(10, 20, 30);
+
+ ASSERT_EQ(10U, regs32.pc_reg());
+ ASSERT_EQ(20U, regs32.sp_reg());
+ ASSERT_EQ(30U, regs32.total_regs());
+
+ uint32_t* raw = reinterpret_cast<uint32_t*>(regs32.raw_data());
+ for (size_t i = 0; i < 30; i++) {
+ raw[i] = 0xf0000000 + i;
+ }
+
+ ASSERT_EQ(0xf000000aU, regs32.pc());
+ ASSERT_EQ(0xf0000014U, regs32.sp());
+
+ ASSERT_EQ(0xf0000001U, regs32[1]);
+ regs32[1] = 10;
+ ASSERT_EQ(10U, regs32[1]);
+
+ ASSERT_EQ(0xf000001dU, regs32[29]);
+}
+
+TEST_F(RegsTest, regs64) {
+ Regs64 regs64(10, 20, 30);
+
+ ASSERT_EQ(10U, regs64.pc_reg());
+ ASSERT_EQ(20U, regs64.sp_reg());
+ ASSERT_EQ(30U, regs64.total_regs());
+
+ uint64_t* raw = reinterpret_cast<uint64_t*>(regs64.raw_data());
+ for (size_t i = 0; i < 30; i++) {
+ raw[i] = 0xf123456780000000UL + i;
+ }
+
+ ASSERT_EQ(0xf12345678000000aUL, regs64.pc());
+ ASSERT_EQ(0xf123456780000014UL, regs64.sp());
+
+ ASSERT_EQ(0xf123456780000008U, regs64[8]);
+ regs64[8] = 10;
+ ASSERT_EQ(10U, regs64[8]);
+
+ ASSERT_EQ(0xf12345678000001dU, regs64[29]);
+}
diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk
index 5c12f2c..9439846 100644
--- a/libusbhost/Android.mk
+++ b/libusbhost/Android.mk
@@ -22,11 +22,11 @@
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
-
LOCAL_MODULE := libusbhost
LOCAL_SRC_FILES := usbhost.c
LOCAL_CFLAGS := -Werror
-
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_HOST_STATIC_LIBRARY)
endif
@@ -35,24 +35,22 @@
# ========================================================
include $(CLEAR_VARS)
-
LOCAL_MODULE := libusbhost
LOCAL_SRC_FILES := usbhost.c
-
LOCAL_CFLAGS := -g -DUSE_LIBLOG -Werror
-
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
# needed for logcat
LOCAL_SHARED_LIBRARIES := libcutils
-
include $(BUILD_SHARED_LIBRARY)
# Static library for target
# ========================================================
include $(CLEAR_VARS)
-
LOCAL_MODULE := libusbhost
LOCAL_SRC_FILES := usbhost.c
LOCAL_CFLAGS := -Werror
-
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_STATIC_LIBRARY)
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
new file mode 100644
index 0000000..84594c8
--- /dev/null
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __USB_HOST_H
+#define __USB_HOST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+
+struct usb_host_context;
+struct usb_endpoint_descriptor;
+
+struct usb_descriptor_iter {
+ unsigned char* config;
+ unsigned char* config_end;
+ unsigned char* curr_desc;
+};
+
+struct usb_request
+{
+ struct usb_device *dev;
+ void* buffer;
+ int buffer_length;
+ int actual_length;
+ int max_packet_size;
+ void *private_data; /* struct usbdevfs_urb* */
+ int endpoint;
+ void *client_data; /* free for use by client */
+};
+
+/* Callback for notification when new USB devices are attached.
+ * Return true to exit from usb_host_run.
+ */
+typedef int (* usb_device_added_cb)(const char *dev_name, void *client_data);
+
+/* Callback for notification when USB devices are removed.
+ * Return true to exit from usb_host_run.
+ */
+typedef int (* usb_device_removed_cb)(const char *dev_name, void *client_data);
+
+/* Callback indicating that initial device discovery is done.
+ * Return true to exit from usb_host_run.
+ */
+typedef int (* usb_discovery_done_cb)(void *client_data);
+
+/* Call this to initialize the USB host library. */
+struct usb_host_context *usb_host_init(void);
+
+/* Call this to cleanup the USB host library. */
+void usb_host_cleanup(struct usb_host_context *context);
+
+/* Call this to get the inotify file descriptor. */
+int usb_host_get_fd(struct usb_host_context *context);
+
+/* Call this to initialize the usb host context. */
+int usb_host_load(struct usb_host_context *context,
+ usb_device_added_cb added_cb,
+ usb_device_removed_cb removed_cb,
+ usb_discovery_done_cb discovery_done_cb,
+ void *client_data);
+
+/* Call this to read and handle occuring usb event. */
+int usb_host_read_event(struct usb_host_context *context);
+
+/* Call this to monitor the USB bus for new and removed devices.
+ * This is intended to be called from a dedicated thread,
+ * as it will not return until one of the callbacks returns true.
+ * added_cb will be called immediately for each existing USB device,
+ * and subsequently each time a new device is added.
+ * removed_cb is called when USB devices are removed from the bus.
+ * discovery_done_cb is called after the initial discovery of already
+ * connected devices is complete.
+ */
+void usb_host_run(struct usb_host_context *context,
+ usb_device_added_cb added_cb,
+ usb_device_removed_cb removed_cb,
+ usb_discovery_done_cb discovery_done_cb,
+ void *client_data);
+
+/* Creates a usb_device object for a USB device */
+struct usb_device *usb_device_open(const char *dev_name);
+
+/* Releases all resources associated with the USB device */
+void usb_device_close(struct usb_device *device);
+
+/* Creates a usb_device object for already open USB device */
+struct usb_device *usb_device_new(const char *dev_name, int fd);
+
+/* Returns the file descriptor for the usb_device */
+int usb_device_get_fd(struct usb_device *device);
+
+/* Returns the name for the USB device, which is the same as
+ * the dev_name passed to usb_device_open()
+ */
+const char* usb_device_get_name(struct usb_device *device);
+
+/* Returns a unique ID for the device.
+ *Currently this is generated from the dev_name path.
+ */
+int usb_device_get_unique_id(struct usb_device *device);
+
+/* Returns a unique ID for the device name.
+ * Currently this is generated from the device path.
+ */
+int usb_device_get_unique_id_from_name(const char* name);
+
+/* Returns the device name for the unique ID.
+ * Call free() to deallocate the returned string */
+char* usb_device_get_name_from_unique_id(int id);
+
+/* Returns the USB vendor ID from the device descriptor for the USB device */
+uint16_t usb_device_get_vendor_id(struct usb_device *device);
+
+/* Returns the USB product ID from the device descriptor for the USB device */
+uint16_t usb_device_get_product_id(struct usb_device *device);
+
+const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
+
+/* Returns a USB descriptor string for the given string ID.
+ * Used to implement usb_device_get_manufacturer_name,
+ * usb_device_get_product_name and usb_device_get_serial.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_string(struct usb_device *device, int id);
+
+/* Returns the manufacturer name for the USB device.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_manufacturer_name(struct usb_device *device);
+
+/* Returns the product name for the USB device.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_product_name(struct usb_device *device);
+
+/* Returns the version number for the USB device.
+ */
+int usb_device_get_version(struct usb_device *device);
+
+/* Returns the USB serial number for the USB device.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_serial(struct usb_device *device);
+
+/* Returns true if we have write access to the USB device,
+ * and false if we only have access to the USB device configuration.
+ */
+int usb_device_is_writeable(struct usb_device *device);
+
+/* Initializes a usb_descriptor_iter, which can be used to iterate through all
+ * the USB descriptors for a USB device.
+ */
+void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter);
+
+/* Returns the next USB descriptor for a device, or NULL if we have reached the
+ * end of the list.
+ */
+struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter);
+
+/* Claims the specified interface of a USB device */
+int usb_device_claim_interface(struct usb_device *device, unsigned int interface);
+
+/* Releases the specified interface of a USB device */
+int usb_device_release_interface(struct usb_device *device, unsigned int interface);
+
+/* Requests the kernel to connect or disconnect its driver for the specified interface.
+ * This can be used to ask the kernel to disconnect its driver for a device
+ * so usb_device_claim_interface can claim it instead.
+ */
+int usb_device_connect_kernel_driver(struct usb_device *device,
+ unsigned int interface, int connect);
+
+/* Sets the current configuration for the device to the specified configuration */
+int usb_device_set_configuration(struct usb_device *device, int configuration);
+
+/* Sets the specified interface of a USB device */
+int usb_device_set_interface(struct usb_device *device, unsigned int interface,
+ unsigned int alt_setting);
+
+/* Sends a control message to the specified device on endpoint zero */
+int usb_device_control_transfer(struct usb_device *device,
+ int requestType,
+ int request,
+ int value,
+ int index,
+ void* buffer,
+ int length,
+ unsigned int timeout);
+
+/* Reads or writes on a bulk endpoint.
+ * Returns number of bytes transferred, or negative value for error.
+ */
+int usb_device_bulk_transfer(struct usb_device *device,
+ int endpoint,
+ void* buffer,
+ unsigned int length,
+ unsigned int timeout);
+
+/** Reset USB bus for the device */
+int usb_device_reset(struct usb_device *device);
+
+/* Creates a new usb_request. */
+struct usb_request *usb_request_new(struct usb_device *dev,
+ const struct usb_endpoint_descriptor *ep_desc);
+
+/* Releases all resources associated with the request */
+void usb_request_free(struct usb_request *req);
+
+/* Submits a read or write request on the specified device */
+int usb_request_queue(struct usb_request *req);
+
+ /* Waits for the results of a previous usb_request_queue operation.
+ * Returns a usb_request, or NULL for error.
+ */
+struct usb_request *usb_request_wait(struct usb_device *dev);
+
+/* Cancels a pending usb_request_queue() operation. */
+int usb_request_cancel(struct usb_request *req);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __USB_HOST_H */
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 684f401..68aca17 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -56,6 +56,9 @@
#define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d"
#define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d"
+// Some devices fail to send string descriptors if we attempt reading > 255 bytes
+#define MAX_STRING_DESCRIPTOR_LENGTH 255
+
// From drivers/usb/core/devio.c
// I don't know why this isn't in a kernel header
#define MAX_USBFS_BUFFER_SIZE 16384
@@ -449,8 +452,8 @@
char* usb_device_get_string(struct usb_device *device, int id)
{
char string[256];
- __u16 buffer[128];
- __u16 languages[128];
+ __u16 buffer[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
+ __u16 languages[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
int i, result;
int languageCount = 0;
@@ -498,6 +501,12 @@
return usb_device_get_string(device, desc->iProduct);
}
+int usb_device_get_version(struct usb_device *device)
+{
+ struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
+ return desc->bcdUSB;
+}
+
char* usb_device_get_serial(struct usb_device *device)
{
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
@@ -591,7 +600,7 @@
int usb_device_bulk_transfer(struct usb_device *device,
int endpoint,
void* buffer,
- int length,
+ unsigned int length,
unsigned int timeout)
{
struct usbdevfs_bulktransfer ctrl;
@@ -608,6 +617,11 @@
return ioctl(device->fd, USBDEVFS_BULK, &ctrl);
}
+int usb_device_reset(struct usb_device *device)
+{
+ return ioctl(device->fd, USBDEVFS_RESET);
+}
+
struct usb_request *usb_request_new(struct usb_device *dev,
const struct usb_endpoint_descriptor *ep_desc)
{
diff --git a/libutils/Android.bp b/libutils/Android.bp
new file mode 100644
index 0000000..0c777b1
--- /dev/null
+++ b/libutils/Android.bp
@@ -0,0 +1,136 @@
+// 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_headers {
+ name: "libutils_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+}
+
+cc_library {
+ name: "libutils",
+ host_supported: true,
+
+ srcs: [
+ "CallStack.cpp",
+ "FileMap.cpp",
+ "JenkinsHash.cpp",
+ "LinearTransform.cpp",
+ "Log.cpp",
+ "NativeHandle.cpp",
+ "Printer.cpp",
+ "PropertyMap.cpp",
+ "RefBase.cpp",
+ "SharedBuffer.cpp",
+ "Static.cpp",
+ "StopWatch.cpp",
+ "String8.cpp",
+ "String16.cpp",
+ "SystemClock.cpp",
+ "Threads.cpp",
+ "Timers.cpp",
+ "Tokenizer.cpp",
+ "Unicode.cpp",
+ "VectorImpl.cpp",
+ "misc.cpp",
+ ],
+
+ cflags: ["-Werror"],
+ include_dirs: ["external/safe-iop/include"],
+ header_libs: ["libutils_headers"],
+ export_header_lib_headers: ["libutils_headers"],
+
+ arch: {
+ mips: {
+ cflags: ["-DALIGN_DOUBLE"],
+ },
+ },
+
+ target: {
+ android: {
+ srcs: [
+ "BlobCache.cpp",
+ "Looper.cpp",
+ "ProcessCallStack.cpp",
+ "Trace.cpp",
+ ],
+
+ cflags: ["-fvisibility=protected"],
+
+ shared_libs: [
+ "libbacktrace",
+ "libcutils",
+ "libdl",
+ "liblog",
+ ],
+
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ },
+
+ host: {
+ cflags: ["-DLIBUTILS_NATIVE=1"],
+
+ shared: {
+ enabled: false,
+ },
+ },
+
+ linux: {
+ srcs: [
+ "Looper.cpp",
+ "ProcessCallStack.cpp",
+ ],
+ },
+ linux_bionic: {
+ enabled: true,
+ srcs: [
+ "Looper.cpp",
+ "ProcessCallStack.cpp",
+ ],
+ },
+
+ darwin: {
+ cflags: ["-Wno-unused-parameter"],
+ },
+
+ // Under MinGW, ctype.h doesn't need multi-byte support
+ windows: {
+ cflags: ["-DMB_CUR_MAX=1"],
+
+ enabled: true,
+ },
+ },
+
+ clang: true,
+}
+
+// Include subdirectory makefiles
+// ============================================================
+
+cc_test {
+ name: "SharedBufferTest",
+ host_supported: true,
+ static_libs: ["libutils"],
+ shared_libs: ["liblog"],
+ srcs: ["SharedBufferTest.cpp"],
+}
+
+subdirs = ["tests"]
diff --git a/libutils/Android.mk b/libutils/Android.mk
deleted file mode 100644
index 8f829f3..0000000
--- a/libutils/Android.mk
+++ /dev/null
@@ -1,113 +0,0 @@
-# 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.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-commonSources:= \
- BasicHashtable.cpp \
- BlobCache.cpp \
- CallStack.cpp \
- FileMap.cpp \
- JenkinsHash.cpp \
- LinearAllocator.cpp \
- LinearTransform.cpp \
- Log.cpp \
- NativeHandle.cpp \
- Printer.cpp \
- ProcessCallStack.cpp \
- PropertyMap.cpp \
- RefBase.cpp \
- SharedBuffer.cpp \
- Static.cpp \
- StopWatch.cpp \
- String8.cpp \
- String16.cpp \
- SystemClock.cpp \
- Threads.cpp \
- Timers.cpp \
- Tokenizer.cpp \
- Unicode.cpp \
- VectorImpl.cpp \
- misc.cpp \
-
-host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -Werror
-
-ifeq ($(HOST_OS),windows)
-ifeq ($(strip $(USE_CYGWIN),),)
-# Under MinGW, ctype.h doesn't need multi-byte support
-host_commonCflags += -DMB_CUR_MAX=1
-endif
-endif
-
-# For the host
-# =====================================================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= $(commonSources)
-ifeq ($(HOST_OS), linux)
-LOCAL_SRC_FILES += Looper.cpp
-endif
-ifeq ($(HOST_OS),darwin)
-LOCAL_CFLAGS += -Wno-unused-parameter
-endif
-LOCAL_MODULE:= libutils
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_CFLAGS += $(host_commonCflags)
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-# For the device, static
-# =====================================================
-include $(CLEAR_VARS)
-
-
-# we have the common sources, plus some device-specific stuff
-LOCAL_SRC_FILES:= \
- $(commonSources) \
- Looper.cpp \
- Trace.cpp
-
-ifeq ($(TARGET_ARCH),mips)
-LOCAL_CFLAGS += -DALIGN_DOUBLE
-endif
-LOCAL_CFLAGS += -Werror
-
-LOCAL_STATIC_LIBRARIES := \
- libcutils
-
-LOCAL_SHARED_LIBRARIES := \
- libbacktrace \
- liblog \
- libdl
-
-LOCAL_MODULE := libutils
-include $(BUILD_STATIC_LIBRARY)
-
-# For the device, shared
-# =====================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE:= libutils
-LOCAL_WHOLE_STATIC_LIBRARIES := libutils
-LOCAL_SHARED_LIBRARIES := \
- libbacktrace \
- libcutils \
- libdl \
- liblog
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_SHARED_LIBRARY)
-
-
-# Build the tests in the tests/ subdirectory.
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/libutils/BasicHashtable.cpp b/libutils/BasicHashtable.cpp
deleted file mode 100644
index 491d9e9..0000000
--- a/libutils/BasicHashtable.cpp
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "BasicHashtable"
-
-#include <math.h>
-
-#include <utils/Log.h>
-#include <utils/BasicHashtable.h>
-#include <utils/misc.h>
-
-namespace android {
-
-BasicHashtableImpl::BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor,
- size_t minimumInitialCapacity, float loadFactor) :
- mBucketSize(entrySize + sizeof(Bucket)), mHasTrivialDestructor(hasTrivialDestructor),
- mLoadFactor(loadFactor), mSize(0),
- mFilledBuckets(0), mBuckets(NULL) {
- determineCapacity(minimumInitialCapacity, mLoadFactor, &mBucketCount, &mCapacity);
-}
-
-BasicHashtableImpl::BasicHashtableImpl(const BasicHashtableImpl& other) :
- mBucketSize(other.mBucketSize), mHasTrivialDestructor(other.mHasTrivialDestructor),
- mCapacity(other.mCapacity), mLoadFactor(other.mLoadFactor),
- mSize(other.mSize), mFilledBuckets(other.mFilledBuckets),
- mBucketCount(other.mBucketCount), mBuckets(other.mBuckets) {
- if (mBuckets) {
- SharedBuffer::bufferFromData(mBuckets)->acquire();
- }
-}
-
-BasicHashtableImpl::~BasicHashtableImpl()
-{
-}
-
-void BasicHashtableImpl::dispose() {
- if (mBuckets) {
- releaseBuckets(mBuckets, mBucketCount);
- }
-}
-
-void BasicHashtableImpl::clone() {
- if (mBuckets) {
- void* newBuckets = allocateBuckets(mBucketCount);
- copyBuckets(mBuckets, newBuckets, mBucketCount);
- releaseBuckets(mBuckets, mBucketCount);
- mBuckets = newBuckets;
- }
-}
-
-void BasicHashtableImpl::setTo(const BasicHashtableImpl& other) {
- if (mBuckets) {
- releaseBuckets(mBuckets, mBucketCount);
- }
-
- mCapacity = other.mCapacity;
- mLoadFactor = other.mLoadFactor;
- mSize = other.mSize;
- mFilledBuckets = other.mFilledBuckets;
- mBucketCount = other.mBucketCount;
- mBuckets = other.mBuckets;
-
- if (mBuckets) {
- SharedBuffer::bufferFromData(mBuckets)->acquire();
- }
-}
-
-void BasicHashtableImpl::clear() {
- if (mBuckets) {
- if (mFilledBuckets) {
- SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets);
- if (sb->onlyOwner()) {
- destroyBuckets(mBuckets, mBucketCount);
- for (size_t i = 0; i < mBucketCount; i++) {
- Bucket& bucket = bucketAt(mBuckets, i);
- bucket.cookie = 0;
- }
- } else {
- releaseBuckets(mBuckets, mBucketCount);
- mBuckets = NULL;
- }
- mFilledBuckets = 0;
- }
- mSize = 0;
- }
-}
-
-ssize_t BasicHashtableImpl::next(ssize_t index) const {
- if (mSize) {
- while (size_t(++index) < mBucketCount) {
- const Bucket& bucket = bucketAt(mBuckets, index);
- if (bucket.cookie & Bucket::PRESENT) {
- return index;
- }
- }
- }
- return -1;
-}
-
-ssize_t BasicHashtableImpl::find(ssize_t index, hash_t hash,
- const void* __restrict__ key) const {
- if (!mSize) {
- return -1;
- }
-
- hash = trimHash(hash);
- if (index < 0) {
- index = chainStart(hash, mBucketCount);
-
- const Bucket& bucket = bucketAt(mBuckets, size_t(index));
- if (bucket.cookie & Bucket::PRESENT) {
- if (compareBucketKey(bucket, key)) {
- return index;
- }
- } else {
- if (!(bucket.cookie & Bucket::COLLISION)) {
- return -1;
- }
- }
- }
-
- size_t inc = chainIncrement(hash, mBucketCount);
- for (;;) {
- index = chainSeek(index, inc, mBucketCount);
-
- const Bucket& bucket = bucketAt(mBuckets, size_t(index));
- if (bucket.cookie & Bucket::PRESENT) {
- if ((bucket.cookie & Bucket::HASH_MASK) == hash
- && compareBucketKey(bucket, key)) {
- return index;
- }
- }
- if (!(bucket.cookie & Bucket::COLLISION)) {
- return -1;
- }
- }
-}
-
-size_t BasicHashtableImpl::add(hash_t hash, const void* entry) {
- if (!mBuckets) {
- mBuckets = allocateBuckets(mBucketCount);
- } else {
- edit();
- }
-
- hash = trimHash(hash);
- for (;;) {
- size_t index = chainStart(hash, mBucketCount);
- Bucket* bucket = &bucketAt(mBuckets, size_t(index));
- if (bucket->cookie & Bucket::PRESENT) {
- size_t inc = chainIncrement(hash, mBucketCount);
- do {
- bucket->cookie |= Bucket::COLLISION;
- index = chainSeek(index, inc, mBucketCount);
- bucket = &bucketAt(mBuckets, size_t(index));
- } while (bucket->cookie & Bucket::PRESENT);
- }
-
- uint32_t collision = bucket->cookie & Bucket::COLLISION;
- if (!collision) {
- if (mFilledBuckets >= mCapacity) {
- rehash(mCapacity * 2, mLoadFactor);
- continue;
- }
- mFilledBuckets += 1;
- }
-
- bucket->cookie = collision | Bucket::PRESENT | hash;
- mSize += 1;
- initializeBucketEntry(*bucket, entry);
- return index;
- }
-}
-
-void BasicHashtableImpl::removeAt(size_t index) {
- edit();
-
- Bucket& bucket = bucketAt(mBuckets, index);
- bucket.cookie &= ~Bucket::PRESENT;
- if (!(bucket.cookie & Bucket::COLLISION)) {
- mFilledBuckets -= 1;
- }
- mSize -= 1;
- if (!mHasTrivialDestructor) {
- destroyBucketEntry(bucket);
- }
-}
-
-void BasicHashtableImpl::rehash(size_t minimumCapacity, float loadFactor) {
- if (minimumCapacity < mSize) {
- minimumCapacity = mSize;
- }
- size_t newBucketCount, newCapacity;
- determineCapacity(minimumCapacity, loadFactor, &newBucketCount, &newCapacity);
-
- if (newBucketCount != mBucketCount || newCapacity != mCapacity) {
- if (mBuckets) {
- void* newBuckets;
- if (mSize) {
- newBuckets = allocateBuckets(newBucketCount);
- for (size_t i = 0; i < mBucketCount; i++) {
- const Bucket& fromBucket = bucketAt(mBuckets, i);
- if (fromBucket.cookie & Bucket::PRESENT) {
- hash_t hash = fromBucket.cookie & Bucket::HASH_MASK;
- size_t index = chainStart(hash, newBucketCount);
- Bucket* toBucket = &bucketAt(newBuckets, size_t(index));
- if (toBucket->cookie & Bucket::PRESENT) {
- size_t inc = chainIncrement(hash, newBucketCount);
- do {
- toBucket->cookie |= Bucket::COLLISION;
- index = chainSeek(index, inc, newBucketCount);
- toBucket = &bucketAt(newBuckets, size_t(index));
- } while (toBucket->cookie & Bucket::PRESENT);
- }
- toBucket->cookie = Bucket::PRESENT | hash;
- initializeBucketEntry(*toBucket, fromBucket.entry);
- }
- }
- } else {
- newBuckets = NULL;
- }
- releaseBuckets(mBuckets, mBucketCount);
- mBuckets = newBuckets;
- mFilledBuckets = mSize;
- }
- mBucketCount = newBucketCount;
- mCapacity = newCapacity;
- }
- mLoadFactor = loadFactor;
-}
-
-void* BasicHashtableImpl::allocateBuckets(size_t count) const {
- size_t bytes = count * mBucketSize;
- SharedBuffer* sb = SharedBuffer::alloc(bytes);
- LOG_ALWAYS_FATAL_IF(!sb, "Could not allocate %u bytes for hashtable with %u buckets.",
- uint32_t(bytes), uint32_t(count));
- void* buckets = sb->data();
- for (size_t i = 0; i < count; i++) {
- Bucket& bucket = bucketAt(buckets, i);
- bucket.cookie = 0;
- }
- return buckets;
-}
-
-void BasicHashtableImpl::releaseBuckets(void* __restrict__ buckets, size_t count) const {
- SharedBuffer* sb = SharedBuffer::bufferFromData(buckets);
- if (sb->release(SharedBuffer::eKeepStorage) == 1) {
- destroyBuckets(buckets, count);
- SharedBuffer::dealloc(sb);
- }
-}
-
-void BasicHashtableImpl::destroyBuckets(void* __restrict__ buckets, size_t count) const {
- if (!mHasTrivialDestructor) {
- for (size_t i = 0; i < count; i++) {
- Bucket& bucket = bucketAt(buckets, i);
- if (bucket.cookie & Bucket::PRESENT) {
- destroyBucketEntry(bucket);
- }
- }
- }
-}
-
-void BasicHashtableImpl::copyBuckets(const void* __restrict__ fromBuckets,
- void* __restrict__ toBuckets, size_t count) const {
- for (size_t i = 0; i < count; i++) {
- const Bucket& fromBucket = bucketAt(fromBuckets, i);
- Bucket& toBucket = bucketAt(toBuckets, i);
- toBucket.cookie = fromBucket.cookie;
- if (fromBucket.cookie & Bucket::PRESENT) {
- initializeBucketEntry(toBucket, fromBucket.entry);
- }
- }
-}
-
-// Table of 31-bit primes where each prime is no less than twice as large
-// as the previous one. Generated by "primes.py".
-static size_t PRIMES[] = {
- 5,
- 11,
- 23,
- 47,
- 97,
- 197,
- 397,
- 797,
- 1597,
- 3203,
- 6421,
- 12853,
- 25717,
- 51437,
- 102877,
- 205759,
- 411527,
- 823117,
- 1646237,
- 3292489,
- 6584983,
- 13169977,
- 26339969,
- 52679969,
- 105359939,
- 210719881,
- 421439783,
- 842879579,
- 1685759167,
- 0,
-};
-
-void BasicHashtableImpl::determineCapacity(size_t minimumCapacity, float loadFactor,
- size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity) {
- LOG_ALWAYS_FATAL_IF(loadFactor <= 0.0f || loadFactor > 1.0f,
- "Invalid load factor %0.3f. Must be in the range (0, 1].", loadFactor);
-
- size_t count = ceilf(minimumCapacity / loadFactor) + 1;
- size_t i = 0;
- while (count > PRIMES[i] && i < NELEM(PRIMES)) {
- i++;
- }
- count = PRIMES[i];
- LOG_ALWAYS_FATAL_IF(!count, "Could not determine required number of buckets for "
- "hashtable with minimum capacity %u and load factor %0.3f.",
- uint32_t(minimumCapacity), loadFactor);
- *outBucketCount = count;
- *outCapacity = ceilf((count - 1) * loadFactor);
-}
-
-}; // namespace android
diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp
index 0ea09cf..126995b 100644
--- a/libutils/BlobCache.cpp
+++ b/libutils/BlobCache.cpp
@@ -25,13 +25,15 @@
#include <utils/Errors.h>
#include <utils/Log.h>
+#include <cutils/properties.h>
+
namespace android {
// BlobCache::Header::mMagicNumber value
static const uint32_t blobCacheMagic = ('_' << 24) + ('B' << 16) + ('b' << 8) + '$';
// BlobCache::Header::mBlobCacheVersion value
-static const uint32_t blobCacheVersion = 2;
+static const uint32_t blobCacheVersion = 3;
// BlobCache::Header::mDeviceVersion value
static const uint32_t blobCacheDeviceVersion = 1;
@@ -165,7 +167,7 @@
}
size_t BlobCache::getFlattenedSize() const {
- size_t size = align4(sizeof(Header));
+ size_t size = align4(sizeof(Header) + PROPERTY_VALUE_MAX);
for (size_t i = 0; i < mCacheEntries.size(); i++) {
const CacheEntry& e(mCacheEntries[i]);
sp<Blob> keyBlob = e.getKey();
@@ -187,10 +189,13 @@
header->mBlobCacheVersion = blobCacheVersion;
header->mDeviceVersion = blobCacheDeviceVersion;
header->mNumEntries = mCacheEntries.size();
+ char buildId[PROPERTY_VALUE_MAX];
+ header->mBuildIdLength = property_get("ro.build.id", buildId, "");
+ memcpy(header->mBuildId, buildId, header->mBuildIdLength);
// Write cache entries
uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
- off_t byteOffset = align4(sizeof(Header));
+ off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
for (size_t i = 0; i < mCacheEntries.size(); i++) {
const CacheEntry& e(mCacheEntries[i]);
sp<Blob> keyBlob = e.getKey();
@@ -239,15 +244,19 @@
ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber);
return BAD_VALUE;
}
+ char buildId[PROPERTY_VALUE_MAX];
+ int len = property_get("ro.build.id", buildId, "");
if (header->mBlobCacheVersion != blobCacheVersion ||
- header->mDeviceVersion != blobCacheDeviceVersion) {
+ header->mDeviceVersion != blobCacheDeviceVersion ||
+ len != header->mBuildIdLength ||
+ strncmp(buildId, header->mBuildId, len)) {
// We treat version mismatches as an empty cache.
return OK;
}
// Read cache entries
const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
- off_t byteOffset = align4(sizeof(Header));
+ off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
size_t numEntries = header->mNumEntries;
for (size_t i = 0; i < numEntries; i++) {
if (byteOffset + sizeof(EntryHeader) > size) {
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 0bfb520..699da74 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,11 +16,12 @@
#define LOG_TAG "CallStack"
+#include <memory>
+
#include <utils/CallStack.h>
#include <utils/Printer.h>
#include <utils/Errors.h>
#include <utils/Log.h>
-#include <UniquePtr.h>
#include <backtrace/Backtrace.h>
@@ -40,7 +41,7 @@
void CallStack::update(int32_t ignoreDepth, pid_t tid) {
mFrameLines.clear();
- UniquePtr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
if (!backtrace->Unwind(ignoreDepth)) {
ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);
}
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 91e45d8..1afa1ec 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -53,6 +53,43 @@
{
}
+// Move Constructor.
+FileMap::FileMap(FileMap&& other)
+ : mFileName(other.mFileName), mBasePtr(other.mBasePtr), mBaseLength(other.mBaseLength),
+ mDataOffset(other.mDataOffset), mDataPtr(other.mDataPtr), mDataLength(other.mDataLength)
+#if defined(__MINGW32__)
+ , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
+#endif
+{
+ other.mFileName = NULL;
+ other.mBasePtr = NULL;
+ other.mDataPtr = NULL;
+#if defined(__MINGW32__)
+ other.mFileHandle = 0;
+ other.mFileMapping = 0;
+#endif
+}
+
+// Move assign operator.
+FileMap& FileMap::operator=(FileMap&& other) {
+ mFileName = other.mFileName;
+ mBasePtr = other.mBasePtr;
+ mBaseLength = other.mBaseLength;
+ mDataOffset = other.mDataOffset;
+ mDataPtr = other.mDataPtr;
+ mDataLength = other.mDataLength;
+ other.mFileName = NULL;
+ other.mBasePtr = NULL;
+ other.mDataPtr = NULL;
+#if defined(__MINGW32__)
+ mFileHandle = other.mFileHandle;
+ mFileMapping = other.mFileMapping;
+ other.mFileHandle = 0;
+ other.mFileMapping = 0;
+#endif
+ return *this;
+}
+
// Destructor.
FileMap::~FileMap(void)
{
@@ -61,7 +98,7 @@
}
#if defined(__MINGW32__)
if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) {
- ALOGD("UnmapViewOfFile(%p) failed, error = %" PRId32 "\n", mBasePtr,
+ ALOGD("UnmapViewOfFile(%p) failed, error = %lu\n", mBasePtr,
GetLastError() );
}
if (mFileMapping != INVALID_HANDLE_VALUE) {
@@ -101,7 +138,7 @@
mFileHandle = (HANDLE) _get_osfhandle(fd);
mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL);
if (mFileMapping == NULL) {
- ALOGE("CreateFileMapping(%p, %" PRIx32 ") failed with error %" PRId32 "\n",
+ ALOGE("CreateFileMapping(%p, %lx) failed with error %lu\n",
mFileHandle, protect, GetLastError() );
return false;
}
@@ -116,7 +153,7 @@
(DWORD)(adjOffset),
adjLength );
if (mBasePtr == NULL) {
- ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %" PRId32 "\n",
+ ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %lu\n",
adjOffset, adjLength, GetLastError() );
CloseHandle(mFileMapping);
mFileMapping = INVALID_HANDLE_VALUE;
diff --git a/libutils/JenkinsHash.cpp b/libutils/JenkinsHash.cpp
index 52c9bb7..ff5d252 100644
--- a/libutils/JenkinsHash.cpp
+++ b/libutils/JenkinsHash.cpp
@@ -19,10 +19,14 @@
* should still be quite good.
**/
+#include <stdlib.h>
#include <utils/JenkinsHash.h>
namespace android {
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
hash_t JenkinsHashWhiten(uint32_t hash) {
hash += (hash << 3);
hash ^= (hash >> 11);
@@ -31,6 +35,9 @@
}
uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) {
+ if (size > UINT32_MAX) {
+ abort();
+ }
hash = JenkinsHashMix(hash, (uint32_t)size);
size_t i;
for (i = 0; i < (size & -4); i += 4) {
@@ -47,6 +54,9 @@
}
uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) {
+ if (size > UINT32_MAX) {
+ abort();
+ }
hash = JenkinsHashMix(hash, (uint32_t)size);
size_t i;
for (i = 0; i < (size & -2); i += 2) {
diff --git a/libutils/LinearAllocator.cpp b/libutils/LinearAllocator.cpp
deleted file mode 100644
index 8b90696..0000000
--- a/libutils/LinearAllocator.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * 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 ``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.
- */
-
-#define LOG_TAG "LinearAllocator"
-#define LOG_NDEBUG 1
-
-#include <stdlib.h>
-#include <utils/LinearAllocator.h>
-#include <utils/Log.h>
-
-
-// The ideal size of a page allocation (these need to be multiples of 8)
-#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb
-#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
-
-// The maximum amount of wasted space we can have per page
-// Allocations exceeding this will have their own dedicated page
-// If this is too low, we will malloc too much
-// Too high, and we may waste too much space
-// Must be smaller than INITIAL_PAGE_SIZE
-#define MAX_WASTE_SIZE ((size_t)1024)
-
-#if ALIGN_DOUBLE
-#define ALIGN_SZ (sizeof(double))
-#else
-#define ALIGN_SZ (sizeof(int))
-#endif
-
-#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1))
-#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
-
-#if LOG_NDEBUG
-#define ADD_ALLOCATION(size)
-#define RM_ALLOCATION(size)
-#else
-#include <utils/Thread.h>
-#include <utils/Timers.h>
-static size_t s_totalAllocations = 0;
-static nsecs_t s_nextLog = 0;
-static android::Mutex s_mutex;
-
-static void _logUsageLocked() {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (now > s_nextLog) {
- s_nextLog = now + milliseconds_to_nanoseconds(10);
- ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
- }
-}
-
-static void _addAllocation(size_t size) {
- android::AutoMutex lock(s_mutex);
- s_totalAllocations += size;
- _logUsageLocked();
-}
-
-#define ADD_ALLOCATION(size) _addAllocation(size);
-#define RM_ALLOCATION(size) _addAllocation(-size);
-#endif
-
-#define min(x,y) (((x) < (y)) ? (x) : (y))
-
-namespace android {
-
-class LinearAllocator::Page {
-public:
- Page* next() { return mNextPage; }
- void setNext(Page* next) { mNextPage = next; }
-
- Page()
- : mNextPage(0)
- {}
-
- void* operator new(size_t /*size*/, void* buf) { return buf; }
-
- void* start() {
- return (void*) (((size_t)this) + sizeof(Page));
- }
-
- void* end(int pageSize) {
- return (void*) (((size_t)start()) + pageSize);
- }
-
-private:
- Page(const Page& /*other*/) {}
- Page* mNextPage;
-};
-
-LinearAllocator::LinearAllocator()
- : mPageSize(INITIAL_PAGE_SIZE)
- , mMaxAllocSize(MAX_WASTE_SIZE)
- , mNext(0)
- , mCurrentPage(0)
- , mPages(0)
- , mTotalAllocated(0)
- , mWastedSpace(0)
- , mPageCount(0)
- , mDedicatedPageCount(0) {}
-
-LinearAllocator::~LinearAllocator(void) {
- Page* p = mPages;
- while (p) {
- Page* next = p->next();
- p->~Page();
- free(p);
- RM_ALLOCATION(mPageSize);
- p = next;
- }
-}
-
-void* LinearAllocator::start(Page* p) {
- return ALIGN_PTR(((size_t*)p) + sizeof(Page));
-}
-
-void* LinearAllocator::end(Page* p) {
- return ((char*)p) + mPageSize;
-}
-
-bool LinearAllocator::fitsInCurrentPage(size_t size) {
- return mNext && ((char*)mNext + size) <= end(mCurrentPage);
-}
-
-void LinearAllocator::ensureNext(size_t size) {
- if (fitsInCurrentPage(size)) return;
-
- if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
- mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
- mPageSize = ALIGN(mPageSize);
- }
- mWastedSpace += mPageSize;
- Page* p = newPage(mPageSize);
- if (mCurrentPage) {
- mCurrentPage->setNext(p);
- }
- mCurrentPage = p;
- if (!mPages) {
- mPages = mCurrentPage;
- }
- mNext = start(mCurrentPage);
-}
-
-void* LinearAllocator::alloc(size_t size) {
- size = ALIGN(size);
- if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
- ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
- // Allocation is too large, create a dedicated page for the allocation
- Page* page = newPage(size);
- mDedicatedPageCount++;
- page->setNext(mPages);
- mPages = page;
- if (!mCurrentPage)
- mCurrentPage = mPages;
- return start(page);
- }
- ensureNext(size);
- void* ptr = mNext;
- mNext = ((char*)mNext) + size;
- mWastedSpace -= size;
- return ptr;
-}
-
-void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
- // Don't bother rewinding across pages
- allocSize = ALIGN(allocSize);
- if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage)
- && ptr == ((char*)mNext - allocSize)) {
- mTotalAllocated -= allocSize;
- mWastedSpace += allocSize;
- mNext = ptr;
- }
-}
-
-LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
- pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
- ADD_ALLOCATION(pageSize);
- mTotalAllocated += pageSize;
- mPageCount++;
- void* buf = malloc(pageSize);
- return new (buf) Page();
-}
-
-static const char* toSize(size_t value, float& result) {
- if (value < 2000) {
- result = value;
- return "B";
- }
- if (value < 2000000) {
- result = value / 1024.0f;
- return "KB";
- }
- result = value / 1048576.0f;
- return "MB";
-}
-
-void LinearAllocator::dumpMemoryStats(const char* prefix) {
- float prettySize;
- const char* prettySuffix;
- prettySuffix = toSize(mTotalAllocated, prettySize);
- ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix);
- prettySuffix = toSize(mWastedSpace, prettySize);
- ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix,
- (float) mWastedSpace / (float) mTotalAllocated * 100.0f);
- ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
-}
-
-}; // namespace android
diff --git a/libutils/LinearTransform.cpp b/libutils/LinearTransform.cpp
index b7d28d4..138ce8b 100644
--- a/libutils/LinearTransform.cpp
+++ b/libutils/LinearTransform.cpp
@@ -21,11 +21,24 @@
#include <utils/LinearTransform.h>
+// disable sanitize as these functions may intentionally overflow (see comments below).
+// the ifdef can be removed when host builds use clang.
+#if defined(__clang__)
+#define ATTRIBUTE_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define ATTRIBUTE_NO_SANITIZE_INTEGER
+#endif
+
namespace android {
-template<class T> static inline T ABS(T x) { return (x < 0) ? -x : x; }
+// sanitize failure with T = int32_t and x = 0x80000000
+template<class T>
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static inline T ABS(T x) { return (x < 0) ? -x : x; }
// Static math methods involving linear transformations
+// remote sanitize failure on overflow case.
+ATTRIBUTE_NO_SANITIZE_INTEGER
static bool scale_u64_to_u64(
uint64_t val,
uint32_t N,
@@ -109,6 +122,8 @@
return true;
}
+// at least one known sanitize failure (see comment below)
+ATTRIBUTE_NO_SANITIZE_INTEGER
static bool linear_transform_s64_to_s64(
int64_t val,
int64_t basis1,
@@ -172,7 +187,7 @@
// (scaled_signbit XOR res_signbit)
if (is_neg)
- scaled = -scaled;
+ scaled = -scaled; // known sanitize failure
res = scaled + basis2;
if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
@@ -250,6 +265,8 @@
template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
+// sanitize failure if *N = 0x80000000
+ATTRIBUTE_NO_SANITIZE_INTEGER
void LinearTransform::reduce(int32_t* N, uint32_t* D) {
if (N && D && *D) {
if (*N < 0) {
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 20c1e92..77e69e4 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -13,16 +13,17 @@
// Debugs callback registration and invocation.
#define DEBUG_CALLBACKS 0
-#include <cutils/log.h>
-#include <utils/Looper.h>
-#include <utils/Timers.h>
-
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <limits.h>
#include <string.h>
+#include <sys/eventfd.h>
#include <unistd.h>
+#include <log/log.h>
+#include <utils/Looper.h>
+#include <utils/Timers.h>
namespace android {
@@ -70,41 +71,21 @@
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
- mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
- int wakeFds[2];
- int result = pipe(wakeFds);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe: %s", strerror(errno));
+ mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
+ mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
+ mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+ LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
+ strerror(errno));
- mWakeReadPipeFd = wakeFds[0];
- mWakeWritePipeFd = wakeFds[1];
-
- result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking: %s",
- strerror(errno));
-
- result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking: %s",
- strerror(errno));
-
- mIdling = false;
-
- // Allocate the epoll instance and register the wake pipe.
- mEpollFd = epoll_create(EPOLL_SIZE_HINT);
- LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
-
- struct epoll_event eventItem;
- memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem.events = EPOLLIN;
- eventItem.data.fd = mWakeReadPipeFd;
- result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance: %s",
- strerror(errno));
+ AutoMutex _l(mLock);
+ rebuildEpollLocked();
}
Looper::~Looper() {
- close(mWakeReadPipeFd);
- close(mWakeWritePipeFd);
- close(mEpollFd);
+ close(mWakeEventFd);
+ if (mEpollFd >= 0) {
+ close(mEpollFd);
+ }
}
void Looper::initTLSKey() {
@@ -158,6 +139,50 @@
return mAllowNonCallbacks;
}
+void Looper::rebuildEpollLocked() {
+ // Close old epoll instance if we have one.
+ if (mEpollFd >= 0) {
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
+#endif
+ close(mEpollFd);
+ }
+
+ // Allocate the new epoll instance and register the wake pipe.
+ mEpollFd = epoll_create(EPOLL_SIZE_HINT);
+ LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
+
+ struct epoll_event eventItem;
+ memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
+ eventItem.events = EPOLLIN;
+ eventItem.data.fd = mWakeEventFd;
+ int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
+ strerror(errno));
+
+ for (size_t i = 0; i < mRequests.size(); i++) {
+ const Request& request = mRequests.valueAt(i);
+ struct epoll_event eventItem;
+ request.initEventItem(&eventItem);
+
+ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
+ if (epollResult < 0) {
+ ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
+ request.fd, strerror(errno));
+ }
+ }
+}
+
+void Looper::scheduleEpollRebuildLocked() {
+ if (!mEpollRebuildRequired) {
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ scheduleEpollRebuildLocked - scheduling epoll set rebuild", this);
+#endif
+ mEpollRebuildRequired = true;
+ wake();
+ }
+}
+
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
@@ -208,7 +233,7 @@
timeoutMillis = messageTimeoutMillis;
}
#if DEBUG_POLL_AND_WAKE
- ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
+ ALOGD("%p ~ pollOnce - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d",
this, mNextMessageUptime - now, timeoutMillis);
#endif
}
@@ -219,17 +244,24 @@
mResponseIndex = 0;
// We are about to idle.
- mIdling = true;
+ mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
- mIdling = false;
+ mPolling = false;
// Acquire lock.
mLock.lock();
+ // Rebuild epoll set if needed.
+ if (mEpollRebuildRequired) {
+ mEpollRebuildRequired = false;
+ rebuildEpollLocked();
+ goto Done;
+ }
+
// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
@@ -257,11 +289,11 @@
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
- if (fd == mWakeReadPipeFd) {
+ if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
- ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
+ ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
@@ -328,10 +360,14 @@
ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
this, response.request.callback.get(), fd, events, data);
#endif
+ // Invoke the callback. Note that the file descriptor may be closed by
+ // the callback (and potentially even reused) before the function returns so
+ // we need to be a little careful when removing the file descriptor afterwards.
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
- removeFd(fd);
+ removeFd(fd, response.request.seq);
}
+
// Clear the callback reference in the response structure promptly because we
// will not clear the response vector itself until the next poll.
response.request.callback.clear();
@@ -372,12 +408,9 @@
ALOGD("%p ~ wake", this);
#endif
- ssize_t nWrite;
- do {
- nWrite = write(mWakeWritePipeFd, "W", 1);
- } while (nWrite == -1 && errno == EINTR);
-
- if (nWrite != 1) {
+ uint64_t inc = 1;
+ ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
+ if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal: %s", strerror(errno));
}
@@ -389,11 +422,8 @@
ALOGD("%p ~ awoken", this);
#endif
- char buffer[16];
- ssize_t nRead;
- do {
- nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
- } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+ uint64_t counter;
+ TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
void Looper::pushResponse(int events, const Request& request) {
@@ -427,23 +457,20 @@
ident = POLL_CALLBACK;
}
- int epollEvents = 0;
- if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
- if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
-
{ // acquire lock
AutoMutex _l(mLock);
Request request;
request.fd = fd;
request.ident = ident;
+ request.events = events;
+ request.seq = mNextRequestSeq++;
request.callback = callback;
request.data = data;
+ if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1
struct epoll_event eventItem;
- memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem.events = epollEvents;
- eventItem.data.fd = fd;
+ request.initEventItem(&eventItem);
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
@@ -456,8 +483,36 @@
} else {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
- ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno));
- return -1;
+ if (errno == ENOENT) {
+ // Tolerate ENOENT because it means that an older file descriptor was
+ // closed before its callback was unregistered and meanwhile a new
+ // file descriptor with the same number has been created and is now
+ // being registered for the first time. This error may occur naturally
+ // when a callback has the side-effect of closing the file descriptor
+ // before returning and unregistering itself. Callback sequence number
+ // checks further ensure that the race is benign.
+ //
+ // Unfortunately due to kernel limitations we need to rebuild the epoll
+ // set from scratch because it may contain an old file handle that we are
+ // now unable to remove since its file descriptor is no longer valid.
+ // No such problem would have occurred if we were using the poll system
+ // call instead, but that approach carries others disadvantages.
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
+ "being recycled, falling back on EPOLL_CTL_ADD: %s",
+ this, strerror(errno));
+#endif
+ epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
+ if (epollResult < 0) {
+ ALOGE("Error modifying or adding epoll events for fd %d: %s",
+ fd, strerror(errno));
+ return -1;
+ }
+ scheduleEpollRebuildLocked();
+ } else {
+ ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno));
+ return -1;
+ }
}
mRequests.replaceValueAt(requestIndex, request);
}
@@ -466,8 +521,12 @@
}
int Looper::removeFd(int fd) {
+ return removeFd(fd, -1);
+}
+
+int Looper::removeFd(int fd, int seq) {
#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - fd=%d", this, fd);
+ ALOGD("%p ~ removeFd - fd=%d, seq=%d", this, fd, seq);
#endif
{ // acquire lock
@@ -477,13 +536,48 @@
return 0;
}
- int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
- if (epollResult < 0) {
- ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
- return -1;
+ // Check the sequence number if one was given.
+ if (seq != -1 && mRequests.valueAt(requestIndex).seq != seq) {
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ removeFd - sequence number mismatch, oldSeq=%d",
+ this, mRequests.valueAt(requestIndex).seq);
+#endif
+ return 0;
}
+ // Always remove the FD from the request map even if an error occurs while
+ // updating the epoll set so that we avoid accidentally leaking callbacks.
mRequests.removeItemsAt(requestIndex);
+
+ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
+ if (epollResult < 0) {
+ if (seq != -1 && (errno == EBADF || errno == ENOENT)) {
+ // Tolerate EBADF or ENOENT when the sequence number is known because it
+ // means that the file descriptor was closed before its callback was
+ // unregistered. This error may occur naturally when a callback has the
+ // side-effect of closing the file descriptor before returning and
+ // unregistering itself.
+ //
+ // Unfortunately due to kernel limitations we need to rebuild the epoll
+ // set from scratch because it may contain an old file handle that we are
+ // now unable to remove since its file descriptor is no longer valid.
+ // No such problem would have occurred if we were using the poll system
+ // call instead, but that approach carries others disadvantages.
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
+ "being closed: %s", this, strerror(errno));
+#endif
+ scheduleEpollRebuildLocked();
+ } else {
+ // Some other error occurred. This is really weird because it means
+ // our list of callbacks got out of sync with the epoll set somehow.
+ // We defensively rebuild the epoll set to avoid getting spurious
+ // notifications with nowhere to go.
+ ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
+ scheduleEpollRebuildLocked();
+ return -1;
+ }
+ }
} // release lock
return 1;
}
@@ -502,7 +596,7 @@
void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
const Message& message) {
#if DEBUG_CALLBACKS
- ALOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d",
+ ALOGD("%p ~ sendMessageAtTime - uptime=%" PRId64 ", handler=%p, what=%d",
this, uptime, handler.get(), message.what);
#endif
@@ -568,8 +662,22 @@
} // release lock
}
-bool Looper::isIdling() const {
- return mIdling;
+bool Looper::isPolling() const {
+ return mPolling;
}
+void Looper::Request::initEventItem(struct epoll_event* eventItem) const {
+ int epollEvents = 0;
+ if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
+ if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
+
+ memset(eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
+ eventItem->events = epollEvents;
+ eventItem->data.fd = fd;
+}
+
+MessageHandler::~MessageHandler() { }
+
+LooperCallback::~LooperCallback() { }
+
} // namespace android
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 1dc8632..98cd2c6 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -44,7 +44,7 @@
char* formattedString;
-#ifndef USE_MINGW
+#ifndef _WIN32
if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
ALOGE("%s: Failed to format string", __FUNCTION__);
return;
@@ -115,7 +115,7 @@
return;
}
-#ifndef USE_MINGW
+#ifndef _WIN32
dprintf(mFd, mFormatString, mPrefix, string);
#endif
}
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index 011c302..73ed4eb 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -21,6 +21,7 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
+#include <memory>
#include <utils/Log.h>
#include <utils/Errors.h>
@@ -130,11 +131,7 @@
}
void ProcessCallStack::update() {
- DIR *dp;
- struct dirent *ep;
- struct dirent entry;
-
- dp = opendir(PATH_SELF_TASK);
+ std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir);
if (dp == NULL) {
ALOGE("%s: Failed to update the process's call stacks: %s",
__FUNCTION__, strerror(errno));
@@ -146,7 +143,6 @@
clear();
// Get current time.
-#ifndef USE_MINGW
{
time_t t = time(NULL);
struct tm tm;
@@ -159,8 +155,8 @@
* Each tid is a directory inside of /proc/self/task
* - Read every file in directory => get every tid
*/
- int code;
- while ((code = readdir_r(dp, &entry, &ep)) == 0 && ep != NULL) {
+ dirent* ep;
+ while ((ep = readdir(dp.get())) != NULL) {
pid_t tid = -1;
sscanf(ep->d_name, "%d", &tid);
@@ -195,13 +191,6 @@
ALOGV("%s: Got call stack for tid %d (size %zu)",
__FUNCTION__, tid, threadInfo.callStack.size());
}
- if (code != 0) { // returns positive error value on error
- ALOGE("%s: Failed to readdir from %s: %s",
- __FUNCTION__, PATH_SELF_TASK, strerror(code));
- }
-#endif
-
- closedir(dp);
}
void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 02907ad..1f8395b 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -27,7 +27,6 @@
#include <utils/RefBase.h>
-#include <utils/Atomic.h>
#include <utils/CallStack.h>
#include <utils/Log.h>
#include <utils/threads.h>
@@ -57,21 +56,103 @@
namespace android {
+// Observations, invariants, etc:
+
+// By default, obects are destroyed when the last strong reference disappears
+// or, if the object never had a strong reference, when the last weak reference
+// disappears.
+//
+// OBJECT_LIFETIME_WEAK changes this behavior to retain the object
+// unconditionally until the last reference of either kind disappears. The
+// client ensures that the extendObjectLifetime call happens before the dec
+// call that would otherwise have deallocated the object, or before an
+// attemptIncStrong call that might rely on it. We do not worry about
+// concurrent changes to the object lifetime.
+//
+// AttemptIncStrong will succeed if the object has a strong reference, or if it
+// has a weak reference and has never had a strong reference.
+// AttemptIncWeak really does succeed only if there is already a WEAK
+// reference, and thus may fail when attemptIncStrong would succeed.
+//
+// mStrong is the strong reference count. mWeak is the weak reference count.
+// Between calls, and ignoring memory ordering effects, mWeak includes strong
+// references, and is thus >= mStrong.
+//
+// A weakref_impl holds all the information, including both reference counts,
+// required to perform wp<> operations. Thus these can continue to be performed
+// after the RefBase object has been destroyed.
+//
+// A weakref_impl is allocated as the value of mRefs in a RefBase object on
+// construction.
+// In the OBJECT_LIFETIME_STRONG case, it is normally deallocated in decWeak,
+// and hence lives as long as the last weak reference. (It can also be
+// deallocated in the RefBase destructor iff the strong reference count was
+// never incremented and the weak count is zero, e.g. if the RefBase object is
+// explicitly destroyed without decrementing the strong count. This should be
+// avoided.) In this case, the RefBase destructor should be invoked from
+// decStrong.
+// In the OBJECT_LIFETIME_WEAK case, the weakref_impl is always deallocated in
+// the RefBase destructor, which is always invoked by decWeak. DecStrong
+// explicitly avoids the deletion in this case.
+//
+// Memory ordering:
+// The client must ensure that every inc() call, together with all other
+// accesses to the object, happens before the corresponding dec() call.
+//
+// We try to keep memory ordering constraints on atomics as weak as possible,
+// since memory fences or ordered memory accesses are likely to be a major
+// performance cost for this code. All accesses to mStrong, mWeak, and mFlags
+// explicitly relax memory ordering in some way.
+//
+// The only operations that are not memory_order_relaxed are reference count
+// decrements. All reference count decrements are release operations. In
+// addition, the final decrement leading the deallocation is followed by an
+// acquire fence, which we can view informally as also turning it into an
+// acquire operation. (See 29.8p4 [atomics.fences] for details. We could
+// alternatively use acq_rel operations for all decrements. This is probably
+// slower on most current (2016) hardware, especially on ARMv7, but that may
+// not be true indefinitely.)
+//
+// This convention ensures that the second-to-last decrement synchronizes with
+// (in the language of 1.10 in the C++ standard) the final decrement of a
+// reference count. Since reference counts are only updated using atomic
+// read-modify-write operations, this also extends to any earlier decrements.
+// (See "release sequence" in 1.10.)
+//
+// Since all operations on an object happen before the corresponding reference
+// count decrement, and all reference count decrements happen before the final
+// one, we are guaranteed that all other object accesses happen before the
+// object is destroyed.
+
+
#define INITIAL_STRONG_VALUE (1<<28)
+#define MAX_COUNT 0xfffff
+
+// Test whether the argument is a clearly invalid strong reference count.
+// Used only for error checking on the value before an atomic decrement.
+// Intended to be very cheap.
+// Note that we cannot just check for excess decrements by comparing to zero
+// since the object would be deallocated before that.
+#define BAD_STRONG(c) \
+ ((c) == 0 || ((c) & (~(MAX_COUNT | INITIAL_STRONG_VALUE))) != 0)
+
+// Same for weak counts.
+#define BAD_WEAK(c) ((c) == 0 || ((c) & (~MAX_COUNT)) != 0)
+
// ---------------------------------------------------------------------------
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
- volatile int32_t mStrong;
- volatile int32_t mWeak;
- RefBase* const mBase;
- volatile int32_t mFlags;
+ std::atomic<int32_t> mStrong;
+ std::atomic<int32_t> mWeak;
+ RefBase* const mBase;
+ std::atomic<int32_t> mFlags;
#if !DEBUG_REFS
- weakref_impl(RefBase* base)
+ explicit weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
@@ -141,7 +222,7 @@
void addStrongRef(const void* id) {
//ALOGD_IF(mTrackEnabled,
// "addStrongRef: RefBase=%p, id=%p", mBase, id);
- addRef(&mStrongRefs, id, mStrong);
+ addRef(&mStrongRefs, id, mStrong.load(std::memory_order_relaxed));
}
void removeStrongRef(const void* id) {
@@ -150,7 +231,7 @@
if (!mRetain) {
removeRef(&mStrongRefs, id);
} else {
- addRef(&mStrongRefs, id, -mStrong);
+ addRef(&mStrongRefs, id, -mStrong.load(std::memory_order_relaxed));
}
}
@@ -162,14 +243,14 @@
}
void addWeakRef(const void* id) {
- addRef(&mWeakRefs, id, mWeak);
+ addRef(&mWeakRefs, id, mWeak.load(std::memory_order_relaxed));
}
void removeWeakRef(const void* id) {
if (!mRetain) {
removeRef(&mWeakRefs, id);
} else {
- addRef(&mWeakRefs, id, -mWeak);
+ addRef(&mWeakRefs, id, -mWeak.load(std::memory_order_relaxed));
}
}
@@ -190,17 +271,22 @@
{
Mutex::Autolock _l(mMutex);
char buf[128];
- sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
+ snprintf(buf, sizeof(buf),
+ "Strong references on RefBase %p (weakref_type %p):\n",
+ mBase, this);
text.append(buf);
printRefsLocked(&text, mStrongRefs);
- sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
+ snprintf(buf, sizeof(buf),
+ "Weak references on RefBase %p (weakref_type %p):\n",
+ mBase, this);
text.append(buf);
printRefsLocked(&text, mWeakRefs);
}
{
char name[100];
- snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
+ snprintf(name, sizeof(name), DEBUG_REFS_CALLSTACK_PATH "/%p.stack",
+ this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
if (rc >= 0) {
write(rc, text.string(), text.length());
@@ -293,8 +379,8 @@
char buf[128];
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
- sprintf(buf, "\t%c ID %p (ref %d):\n",
- inc, refs->id, refs->ref);
+ snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
+ inc, refs->id, refs->ref);
out->append(buf);
#if DEBUG_REFS_CALLSTACK_ENABLED
out->append(refs->stack.toString("\t\t"));
@@ -325,7 +411,7 @@
refs->incWeak(id);
refs->addStrongRef(id);
- const int32_t c = android_atomic_inc(&refs->mStrong);
+ const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
@@ -334,7 +420,10 @@
return;
}
- android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
+ int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
+ std::memory_order_relaxed);
+ // A decStrong() must still happen after us.
+ ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
refs->mBase->onFirstRef();
}
@@ -342,27 +431,39 @@
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id);
- const int32_t c = android_atomic_dec(&refs->mStrong);
+ const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
#if PRINT_REFS
ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
- ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
+ LOG_ALWAYS_FATAL_IF(BAD_STRONG(c), "decStrong() called on %p too many times",
+ refs);
if (c == 1) {
+ std::atomic_thread_fence(std::memory_order_acquire);
refs->mBase->onLastStrongRef(id);
- if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
+ int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
+ if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
delete this;
+ // The destructor does not delete refs in this case.
}
}
+ // Note that even with only strong reference operations, the thread
+ // deallocating this may not be the same as the thread deallocating refs.
+ // That's OK: all accesses to this happen before its deletion here,
+ // and all accesses to refs happen before its deletion in the final decWeak.
+ // The destructor can safely access mRefs because either it's deleting
+ // mRefs itself, or it's running entirely before the final mWeak decrement.
refs->decWeak(id);
}
void RefBase::forceIncStrong(const void* id) const
{
+ // Allows initial mStrong of 0 in addition to INITIAL_STRONG_VALUE.
+ // TODO: Better document assumptions.
weakref_impl* const refs = mRefs;
refs->incWeak(id);
refs->addStrongRef(id);
- const int32_t c = android_atomic_inc(&refs->mStrong);
+ const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
ALOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow",
refs);
#if PRINT_REFS
@@ -371,7 +472,8 @@
switch (c) {
case INITIAL_STRONG_VALUE:
- android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
+ refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
+ std::memory_order_relaxed);
// fall through...
case 0:
refs->mBase->onFirstRef();
@@ -380,7 +482,8 @@
int32_t RefBase::getStrongCount() const
{
- return mRefs->mStrong;
+ // Debugging only; No memory ordering guarantees.
+ return mRefs->mStrong.load(std::memory_order_relaxed);
}
RefBase* RefBase::weakref_type::refBase() const
@@ -392,7 +495,8 @@
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id);
- const int32_t c __unused = android_atomic_inc(&impl->mWeak);
+ const int32_t c __unused = impl->mWeak.fetch_add(1,
+ std::memory_order_relaxed);
ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
@@ -401,31 +505,38 @@
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);
- const int32_t c = android_atomic_dec(&impl->mWeak);
- ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
+ const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
+ LOG_ALWAYS_FATAL_IF(BAD_WEAK(c), "decWeak called on %p too many times",
+ this);
if (c != 1) return;
+ atomic_thread_fence(std::memory_order_acquire);
- if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
+ int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
+ if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
// This is the regular lifetime case. The object is destroyed
// when the last strong reference goes away. Since weakref_impl
- // outlive the object, it is not destroyed in the dtor, and
+ // outlives the object, it is not destroyed in the dtor, and
// we'll have to do it here.
- if (impl->mStrong == INITIAL_STRONG_VALUE) {
- // Special case: we never had a strong reference, so we need to
- // destroy the object now.
- delete impl->mBase;
+ if (impl->mStrong.load(std::memory_order_relaxed)
+ == INITIAL_STRONG_VALUE) {
+ // Decrementing a weak count to zero when object never had a strong
+ // reference. We assume it acquired a weak reference early, e.g.
+ // in the constructor, and will eventually be properly destroyed,
+ // usually via incrementing and decrementing the strong count.
+ // Thus we no longer do anything here. We log this case, since it
+ // seems to be extremely rare, and should not normally occur. We
+ // used to deallocate mBase here, so this may now indicate a leak.
+ ALOGW("RefBase: Object at %p lost last weak reference "
+ "before it had a strong reference", impl->mBase);
} else {
// ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
delete impl;
}
} else {
- // less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER}
+ // This is the OBJECT_LIFETIME_WEAK case. The last weak-reference
+ // is gone, we can destroy the object.
impl->mBase->onLastWeakRef(id);
- if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
- // this is the OBJECT_LIFETIME_WEAK case. The last weak-reference
- // is gone, we can destroy the object.
- delete impl->mBase;
- }
+ delete impl->mBase;
}
}
@@ -434,7 +545,7 @@
incWeak(id);
weakref_impl* const impl = static_cast<weakref_impl*>(this);
- int32_t curCount = impl->mStrong;
+ int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);
ALOG_ASSERT(curCount >= 0,
"attemptIncStrong called on %p after underflow", this);
@@ -442,19 +553,20 @@
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
// we're in the easy/common case of promoting a weak-reference
// from an existing strong reference.
- if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
+ if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
+ std::memory_order_relaxed)) {
break;
}
// the strong count has changed on us, we need to re-assert our
- // situation.
- curCount = impl->mStrong;
+ // situation. curCount was updated by compare_exchange_weak.
}
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
// we're now in the harder case of either:
// - there never was a strong reference on us
// - or, all strong references have been released
- if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
+ int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
+ if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
// this object has a "normal" life-time, i.e.: it gets destroyed
// when the last strong reference goes away
if (curCount <= 0) {
@@ -468,13 +580,13 @@
// there never was a strong-reference, so we can try to
// promote this object; we need to do that atomically.
while (curCount > 0) {
- if (android_atomic_cmpxchg(curCount, curCount + 1,
- &impl->mStrong) == 0) {
+ if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
+ std::memory_order_relaxed)) {
break;
}
// the strong count has changed on us, we need to re-assert our
// situation (e.g.: another thread has inc/decStrong'ed us)
- curCount = impl->mStrong;
+ // curCount has been updated.
}
if (curCount <= 0) {
@@ -494,16 +606,15 @@
}
// grab a strong-reference, which is always safe due to the
// extended life-time.
- curCount = android_atomic_inc(&impl->mStrong);
- }
-
- // If the strong reference count has already been incremented by
- // someone else, the implementor of onIncStrongAttempted() is holding
- // an unneeded reference. So call onLastStrongRef() here to remove it.
- // (No, this is not pretty.) Note that we MUST NOT do this if we
- // are in fact acquiring the first reference.
- if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
- impl->mBase->onLastStrongRef(id);
+ curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed);
+ // If the strong reference count has already been incremented by
+ // someone else, the implementor of onIncStrongAttempted() is holding
+ // an unneeded reference. So call onLastStrongRef() here to remove it.
+ // (No, this is not pretty.) Note that we MUST NOT do this if we
+ // are in fact acquiring the first reference.
+ if (curCount != 0 && curCount != INITIAL_STRONG_VALUE) {
+ impl->mBase->onLastStrongRef(id);
+ }
}
}
@@ -513,21 +624,16 @@
ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
#endif
- // now we need to fix-up the count if it was INITIAL_STRONG_VALUE
- // this must be done safely, i.e.: handle the case where several threads
+ // curCount is the value of mStrong before we incremented it.
+ // Now we need to fix-up the count if it was INITIAL_STRONG_VALUE.
+ // This must be done safely, i.e.: handle the case where several threads
// were here in attemptIncStrong().
- curCount = impl->mStrong;
- while (curCount >= INITIAL_STRONG_VALUE) {
- ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE,
- "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE",
- this);
- if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
- &impl->mStrong) == 0) {
- break;
- }
- // the strong-count changed on us, we need to re-assert the situation,
- // for e.g.: it's possible the fix-up happened in another thread.
- curCount = impl->mStrong;
+ // curCount > INITIAL_STRONG_VALUE is OK, and can happen if we're doing
+ // this in the middle of another incStrong. The subtraction is handled
+ // by the thread that started with INITIAL_STRONG_VALUE.
+ if (curCount == INITIAL_STRONG_VALUE) {
+ impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
+ std::memory_order_relaxed);
}
return true;
@@ -537,14 +643,15 @@
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
- int32_t curCount = impl->mWeak;
+ int32_t curCount = impl->mWeak.load(std::memory_order_relaxed);
ALOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow",
this);
while (curCount > 0) {
- if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) {
+ if (impl->mWeak.compare_exchange_weak(curCount, curCount+1,
+ std::memory_order_relaxed)) {
break;
}
- curCount = impl->mWeak;
+ // curCount has been updated.
}
if (curCount > 0) {
@@ -556,7 +663,9 @@
int32_t RefBase::weakref_type::getWeakCount() const
{
- return static_cast<const weakref_impl*>(this)->mWeak;
+ // Debug only!
+ return static_cast<const weakref_impl*>(this)->mWeak
+ .load(std::memory_order_relaxed);
}
void RefBase::weakref_type::printRefs() const
@@ -587,28 +696,36 @@
RefBase::~RefBase()
{
- if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
- // we never acquired a strong (and/or weak) reference on this object.
- delete mRefs;
- } else {
- // life-time of this object is extended to WEAK or FOREVER, in
- // which case weakref_impl doesn't out-live the object and we
- // can free it now.
- if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
- // It's possible that the weak count is not 0 if the object
- // re-acquired a weak reference in its destructor
- if (mRefs->mWeak == 0) {
- delete mRefs;
- }
+ int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
+ // Life-time of this object is extended to WEAK, in
+ // which case weakref_impl doesn't out-live the object and we
+ // can free it now.
+ if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
+ // It's possible that the weak count is not 0 if the object
+ // re-acquired a weak reference in its destructor
+ if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
+ delete mRefs;
}
+ } else if (mRefs->mStrong.load(std::memory_order_relaxed)
+ == INITIAL_STRONG_VALUE) {
+ // We never acquired a strong reference on this object.
+ LOG_ALWAYS_FATAL_IF(mRefs->mWeak.load() != 0,
+ "RefBase: Explicit destruction with non-zero weak "
+ "reference count");
+ // TODO: Always report if we get here. Currently MediaMetadataRetriever
+ // C++ objects are inconsistently managed and sometimes get here.
+ // There may be other cases, but we believe they should all be fixed.
+ delete mRefs;
}
- // for debugging purposes, clear this.
+ // For debugging purposes, clear mRefs. Ineffective against outstanding wp's.
const_cast<weakref_impl*&>(mRefs) = NULL;
}
void RefBase::extendObjectLifetime(int32_t mode)
{
- android_atomic_or(mode, &mRefs->mFlags);
+ // Must be happens-before ordered with respect to construction or any
+ // operation that could destroy the object.
+ mRefs->mFlags.fetch_or(mode, std::memory_order_relaxed);
}
void RefBase::onFirstRef()
@@ -653,4 +770,6 @@
ref->mRefs->renameWeakRefId(old_id, new_id);
}
+VirtualLightRefBase::~VirtualLightRefBase() {}
+
}; // namespace android
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 3555fb7..957aedb 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -14,11 +14,14 @@
* limitations under the License.
*/
+#define LOG_TAG "sharedbuffer"
+
#include <stdlib.h>
#include <string.h>
-#include <utils/SharedBuffer.h>
-#include <utils/Atomic.h>
+#include <log/log.h>
+
+#include "SharedBuffer.h"
// ---------------------------------------------------------------------------
@@ -26,20 +29,26 @@
SharedBuffer* SharedBuffer::alloc(size_t size)
{
+ // Don't overflow if the combined size of the buffer / header is larger than
+ // size_max.
+ LOG_ALWAYS_FATAL_IF((size >= (SIZE_MAX - sizeof(SharedBuffer))),
+ "Invalid buffer size %zu", size);
+
SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
if (sb) {
- sb->mRefs = 1;
+ // Should be std::atomic_init(&sb->mRefs, 1);
+ // But that generates a warning with some compilers.
+ // The following is OK on Android-supported platforms.
+ sb->mRefs.store(1, std::memory_order_relaxed);
sb->mSize = size;
}
return sb;
}
-ssize_t SharedBuffer::dealloc(const SharedBuffer* released)
+void SharedBuffer::dealloc(const SharedBuffer* released)
{
- if (released->mRefs != 0) return -1; // XXX: invalid operation
free(const_cast<SharedBuffer*>(released));
- return 0;
}
SharedBuffer* SharedBuffer::edit() const
@@ -52,7 +61,7 @@
memcpy(sb->data(), data(), size());
release();
}
- return sb;
+ return sb;
}
SharedBuffer* SharedBuffer::editResize(size_t newSize) const
@@ -60,6 +69,11 @@
if (onlyOwner()) {
SharedBuffer* buf = const_cast<SharedBuffer*>(this);
if (buf->mSize == newSize) return buf;
+ // Don't overflow if the combined size of the new buffer / header is larger than
+ // size_max.
+ LOG_ALWAYS_FATAL_IF((newSize >= (SIZE_MAX - sizeof(SharedBuffer))),
+ "Invalid buffer size %zu", newSize);
+
buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
if (buf != NULL) {
buf->mSize = newSize;
@@ -94,14 +108,16 @@
}
void SharedBuffer::acquire() const {
- android_atomic_inc(&mRefs);
+ mRefs.fetch_add(1, std::memory_order_relaxed);
}
int32_t SharedBuffer::release(uint32_t flags) const
{
int32_t prev = 1;
- if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) {
- mRefs = 0;
+ if (onlyOwner()
+ || (((prev = mRefs.fetch_sub(1, std::memory_order_release)) == 1)
+ && (atomic_thread_fence(std::memory_order_acquire), true))) {
+ mRefs.store(0, std::memory_order_relaxed);
if ((flags & eKeepStorage) == 0) {
free(const_cast<SharedBuffer*>(this));
}
diff --git a/libutils/SharedBuffer.h b/libutils/SharedBuffer.h
new file mode 100644
index 0000000..48358cd
--- /dev/null
+++ b/libutils/SharedBuffer.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * DEPRECATED. DO NOT USE FOR NEW CODE.
+ */
+
+#ifndef ANDROID_SHARED_BUFFER_H
+#define ANDROID_SHARED_BUFFER_H
+
+#include <atomic>
+#include <stdint.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+class SharedBuffer
+{
+public:
+
+ /* flags to use with release() */
+ enum {
+ eKeepStorage = 0x00000001
+ };
+
+ /*! allocate a buffer of size 'size' and acquire() it.
+ * call release() to free it.
+ */
+ static SharedBuffer* alloc(size_t size);
+
+ /*! free the memory associated with the SharedBuffer.
+ * Fails if there are any users associated with this SharedBuffer.
+ * In other words, the buffer must have been release by all its
+ * users.
+ */
+ static void dealloc(const SharedBuffer* released);
+
+ //! access the data for read
+ inline const void* data() const;
+
+ //! access the data for read/write
+ inline void* data();
+
+ //! get size of the buffer
+ inline size_t size() const;
+
+ //! get back a SharedBuffer object from its data
+ static inline SharedBuffer* bufferFromData(void* data);
+
+ //! get back a SharedBuffer object from its data
+ static inline const SharedBuffer* bufferFromData(const void* data);
+
+ //! get the size of a SharedBuffer object from its data
+ static inline size_t sizeFromData(const void* data);
+
+ //! edit the buffer (get a writtable, or non-const, version of it)
+ SharedBuffer* edit() const;
+
+ //! edit the buffer, resizing if needed
+ SharedBuffer* editResize(size_t size) const;
+
+ //! like edit() but fails if a copy is required
+ SharedBuffer* attemptEdit() const;
+
+ //! resize and edit the buffer, loose it's content.
+ SharedBuffer* reset(size_t size) const;
+
+ //! acquire/release a reference on this buffer
+ void acquire() const;
+
+ /*! release a reference on this buffer, with the option of not
+ * freeing the memory associated with it if it was the last reference
+ * returns the previous reference count
+ */
+ int32_t release(uint32_t flags = 0) const;
+
+ //! returns wether or not we're the only owner
+ inline bool onlyOwner() const;
+
+
+private:
+ inline SharedBuffer() { }
+ inline ~SharedBuffer() { }
+ SharedBuffer(const SharedBuffer&);
+ SharedBuffer& operator = (const SharedBuffer&);
+
+ // Must be sized to preserve correct alignment.
+ mutable std::atomic<int32_t> mRefs;
+ size_t mSize;
+ uint32_t mReserved[2];
+};
+
+static_assert(sizeof(SharedBuffer) % 8 == 0
+ && (sizeof(size_t) > 4 || sizeof(SharedBuffer) == 16),
+ "SharedBuffer has unexpected size");
+
+// ---------------------------------------------------------------------------
+
+const void* SharedBuffer::data() const {
+ return this + 1;
+}
+
+void* SharedBuffer::data() {
+ return this + 1;
+}
+
+size_t SharedBuffer::size() const {
+ return mSize;
+}
+
+SharedBuffer* SharedBuffer::bufferFromData(void* data) {
+ return data ? static_cast<SharedBuffer *>(data)-1 : 0;
+}
+
+const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
+ return data ? static_cast<const SharedBuffer *>(data)-1 : 0;
+}
+
+size_t SharedBuffer::sizeFromData(const void* data) {
+ return data ? bufferFromData(data)->mSize : 0;
+}
+
+bool SharedBuffer::onlyOwner() const {
+ return (mRefs.load(std::memory_order_acquire) == 1);
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_H
diff --git a/libutils/SharedBufferTest.cpp b/libutils/SharedBufferTest.cpp
new file mode 100644
index 0000000..33a4e0c
--- /dev/null
+++ b/libutils/SharedBufferTest.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define __STDC_LIMIT_MACROS
+
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <stdint.h>
+
+#include "SharedBuffer.h"
+
+TEST(SharedBufferTest, TestAlloc) {
+ EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX), "");
+ EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer)), "");
+
+ // Make sure we don't die here.
+ // Check that null is returned, as we are asking for the whole address space.
+ android::SharedBuffer* buf =
+ android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer) - 1);
+ ASSERT_EQ(nullptr, buf);
+
+ buf = android::SharedBuffer::alloc(0);
+ ASSERT_NE(nullptr, buf);
+ ASSERT_EQ(0U, buf->size());
+ buf->release();
+}
+
+TEST(SharedBufferTest, TestEditResize) {
+ android::SharedBuffer* buf = android::SharedBuffer::alloc(10);
+ EXPECT_DEATH(buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer)), "");
+ buf = android::SharedBuffer::alloc(10);
+ EXPECT_DEATH(buf->editResize(SIZE_MAX), "");
+
+ buf = android::SharedBuffer::alloc(10);
+ // Make sure we don't die here.
+ // Check that null is returned, as we are asking for the whole address space.
+ buf = buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer) - 1);
+ ASSERT_EQ(nullptr, buf);
+
+ buf = android::SharedBuffer::alloc(10);
+ buf = buf->editResize(0);
+ ASSERT_EQ(0U, buf->size());
+ buf->release();
+}
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 91efdaa..9f5cfea 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -18,13 +18,13 @@
#include <utils/Log.h>
#include <utils/Unicode.h>
-#include <utils/String8.h>
#include <utils/threads.h>
#include <memory.h>
#include <stdio.h>
#include <ctype.h>
+#include "SharedBuffer.h"
namespace android {
@@ -71,12 +71,12 @@
u8cur = (const uint8_t*) u8str;
char16_t* u16str = (char16_t*)buf->data();
- utf8_to_utf16(u8cur, u8len, u16str);
+ utf8_to_utf16(u8cur, u8len, u16str, ((size_t) u16len) + 1);
//printf("Created UTF-16 string from UTF-8 \"%s\":", in);
//printHexData(1, str, buf->size(), 16, 1);
//printf("\n");
-
+
return u16str;
}
@@ -126,7 +126,7 @@
mString = str;
return;
}
-
+
mString = getEmptyString();
}
@@ -141,7 +141,7 @@
mString = str;
return;
}
-
+
mString = getEmptyString();
}
@@ -165,6 +165,11 @@
SharedBuffer::bufferFromData(mString)->release();
}
+size_t String16::size() const
+{
+ return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
+}
+
void String16::setTo(const String16& other)
{
SharedBuffer::bufferFromData(other.mString)->acquire();
@@ -222,7 +227,7 @@
} else if (otherLen == 0) {
return NO_ERROR;
}
-
+
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize((myLen+otherLen+1)*sizeof(char16_t));
if (buf) {
@@ -243,7 +248,7 @@
} else if (otherLen == 0) {
return NO_ERROR;
}
-
+
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize((myLen+otherLen+1)*sizeof(char16_t));
if (buf) {
@@ -339,6 +344,11 @@
return strncmp16(mString, prefix, ps) == 0;
}
+bool String16::contains(const char16_t* chrs) const
+{
+ return strstr16(mString, chrs) != nullptr;
+}
+
status_t String16::makeLower()
{
const size_t N = size();
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 69313ea..cacaf91 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -14,17 +14,21 @@
* limitations under the License.
*/
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
#include <utils/String8.h>
#include <utils/Compat.h>
#include <utils/Log.h>
#include <utils/Unicode.h>
-#include <utils/SharedBuffer.h>
#include <utils/String16.h>
#include <utils/threads.h>
#include <ctype.h>
+#include "SharedBuffer.h"
+
/*
* Functions outside android is below the namespace android, since they use
* functions and constants in android namespace.
@@ -100,20 +104,21 @@
{
if (len == 0) return getEmptyString();
- const ssize_t bytes = utf16_to_utf8_length(in, len);
- if (bytes < 0) {
+ // Allow for closing '\0'
+ const ssize_t resultStrLen = utf16_to_utf8_length(in, len) + 1;
+ if (resultStrLen < 1) {
return getEmptyString();
}
- SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
+ SharedBuffer* buf = SharedBuffer::alloc(resultStrLen);
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (!buf) {
return getEmptyString();
}
- char* str = (char*)buf->data();
- utf16_to_utf8(in, len, str);
- return str;
+ char* resultStr = (char*)buf->data();
+ utf16_to_utf8(in, len, resultStr, resultStrLen);
+ return resultStr;
}
static char* allocFromUTF32(const char32_t* in, size_t len)
@@ -122,21 +127,21 @@
return getEmptyString();
}
- const ssize_t bytes = utf32_to_utf8_length(in, len);
- if (bytes < 0) {
+ const ssize_t resultStrLen = utf32_to_utf8_length(in, len) + 1;
+ if (resultStrLen < 1) {
return getEmptyString();
}
- SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
+ SharedBuffer* buf = SharedBuffer::alloc(resultStrLen);
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (!buf) {
return getEmptyString();
}
- char* str = (char*) buf->data();
- utf32_to_utf8(in, len, str);
+ char* resultStr = (char*) buf->data();
+ utf32_to_utf8(in, len, resultStr, resultStrLen);
- return str;
+ return resultStr;
}
// ---------------------------------------------------------------------------
@@ -211,6 +216,11 @@
SharedBuffer::bufferFromData(mString)->release();
}
+size_t String8::length() const
+{
+ return SharedBuffer::sizeFromData(mString)-1;
+}
+
String8 String8::format(const char* fmt, ...)
{
va_list args;
@@ -353,7 +363,7 @@
status_t String8::real_append(const char* other, size_t otherLen)
{
const size_t myLen = bytes();
-
+
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize(myLen+otherLen+1);
if (buf) {
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 1fca2b2..965e32c 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -19,18 +19,13 @@
* System clock functions.
*/
-#if defined(__ANDROID__)
-#include <linux/ioctl.h>
-#include <linux/rtc.h>
-#include <utils/Atomic.h>
-#include <linux/android_alarm.h>
-#endif
-
#include <sys/time.h>
#include <limits.h>
#include <fcntl.h>
#include <string.h>
+#include <errno.h>
+#include <cutils/compiler.h>
#include <utils/SystemClock.h>
#include <utils/Timers.h>
@@ -56,100 +51,21 @@
return nanoseconds_to_milliseconds(elapsedRealtimeNano());
}
-#define METHOD_CLOCK_GETTIME 0
-#define METHOD_IOCTL 1
-#define METHOD_SYSTEMTIME 2
-
-/*
- * To debug/verify the timestamps returned by the kernel, change
- * DEBUG_TIMESTAMP to 1 and call the timestamp routine from a single thread
- * in the test program. b/10899829
- */
-#define DEBUG_TIMESTAMP 0
-
-#if DEBUG_TIMESTAMP && defined(__arm__)
-static inline void checkTimeStamps(int64_t timestamp,
- int64_t volatile *prevTimestampPtr,
- int volatile *prevMethodPtr,
- int curMethod)
-{
- /*
- * Disable the check for SDK since the prebuilt toolchain doesn't contain
- * gettid, and int64_t is different on the ARM platform
- * (ie long vs long long).
- */
- int64_t prevTimestamp = *prevTimestampPtr;
- int prevMethod = *prevMethodPtr;
-
- if (timestamp < prevTimestamp) {
- static const char *gettime_method_names[] = {
- "clock_gettime",
- "ioctl",
- "systemTime",
- };
-
- ALOGW("time going backwards: prev %lld(%s) vs now %lld(%s), tid=%d",
- prevTimestamp, gettime_method_names[prevMethod],
- timestamp, gettime_method_names[curMethod],
- gettid());
- }
- // NOTE - not atomic and may generate spurious warnings if the 64-bit
- // write is interrupted or not observed as a whole.
- *prevTimestampPtr = timestamp;
- *prevMethodPtr = curMethod;
-}
-#else
-#define checkTimeStamps(timestamp, prevTimestampPtr, prevMethodPtr, curMethod)
-#endif
-
/*
* native public static long elapsedRealtimeNano();
*/
int64_t elapsedRealtimeNano()
{
-#if defined(__ANDROID__)
+#if defined(__linux__)
struct timespec ts;
- int result;
- int64_t timestamp;
-#if DEBUG_TIMESTAMP
- static volatile int64_t prevTimestamp;
- static volatile int prevMethod;
-#endif
-
- static int s_fd = -1;
-
- if (s_fd == -1) {
- int fd = open("/dev/alarm", O_RDONLY);
- if (android_atomic_cmpxchg(-1, fd, &s_fd)) {
- close(fd);
- }
+ int err = clock_gettime(CLOCK_BOOTTIME, &ts);
+ if (CC_UNLIKELY(err)) {
+ // This should never happen, but just in case ...
+ ALOGE("clock_gettime(CLOCK_BOOTTIME) failed: %s", strerror(errno));
+ return 0;
}
- result = ioctl(s_fd,
- ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts);
-
- if (result == 0) {
- timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
- checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, METHOD_IOCTL);
- return timestamp;
- }
-
- // /dev/alarm doesn't exist, fallback to CLOCK_BOOTTIME
- result = clock_gettime(CLOCK_BOOTTIME, &ts);
- if (result == 0) {
- timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
- checkTimeStamps(timestamp, &prevTimestamp, &prevMethod,
- METHOD_CLOCK_GETTIME);
- return timestamp;
- }
-
- // XXX: there was an error, probably because the driver didn't
- // exist ... this should return
- // a real error, like an exception!
- timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- checkTimeStamps(timestamp, &prevTimestamp, &prevMethod,
- METHOD_SYSTEMTIME);
- return timestamp;
+ return seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
#else
return systemTime(SYSTEM_TIME_MONOTONIC);
#endif
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 6dda6b5..def739f 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -668,6 +668,8 @@
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
+ LOG_ALWAYS_FATAL_IF(name == nullptr, "thread name not provided to Thread::run");
+
Mutex::Autolock _l(mLock);
if (mRunning) {
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 6f4b721..f1a41b9 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -14,10 +14,14 @@
* limitations under the License.
*/
-#include <utils/Unicode.h>
+#define LOG_TAG "unicode"
+#include <limits.h>
#include <stddef.h>
+#include <log/log.h>
+#include <utils/Unicode.h>
+
#if defined(_WIN32)
# undef nhtol
# undef htonl
@@ -182,7 +186,7 @@
return ret;
}
-void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst)
+void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len)
{
if (src == NULL || src_len == 0 || dst == NULL) {
return;
@@ -193,9 +197,12 @@
char *cur = dst;
while (cur_utf32 < end_utf32) {
size_t len = utf32_codepoint_utf8_length(*cur_utf32);
+ LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len);
cur += len;
+ dst_len -= len;
}
+ LOG_ALWAYS_FATAL_IF(dst_len < 1, "dst_len < 1: %zu < 1", dst_len);
*cur = '\0';
}
@@ -222,12 +229,17 @@
char16_t ch;
int d = 0;
- while ( n-- ) {
- d = (int)(ch = *s1++) - (int)*s2++;
- if ( d || !ch )
- break;
+ if (n == 0) {
+ return 0;
}
+ do {
+ d = (int)(ch = *s1++) - (int)*s2++;
+ if ( d || !ch ) {
+ break;
+ }
+ } while (--n);
+
return d;
}
@@ -284,6 +296,25 @@
return ss-s;
}
+char16_t* strstr16(const char16_t* src, const char16_t* target)
+{
+ const char16_t needle = *target++;
+ const size_t target_len = strlen16(target);
+ if (needle != '\0') {
+ do {
+ do {
+ if (*src == '\0') {
+ return nullptr;
+ }
+ } while (*src++ != needle);
+ } while (strncmp16(src, target, target_len) != 0);
+ src--;
+ }
+
+ return (char16_t*)src;
+}
+
+
int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)
{
const char16_t* e1 = s1+n1;
@@ -324,7 +355,7 @@
: 0);
}
-void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst)
+void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
{
if (src == NULL || src_len == 0 || dst == NULL) {
return;
@@ -345,9 +376,12 @@
utf32 = (char32_t) *cur_utf16++;
}
const size_t len = utf32_codepoint_utf8_length(utf32);
+ LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len);
cur += len;
+ dst_len -= len;
}
+ LOG_ALWAYS_FATAL_IF(dst_len < 1, "%zu < 1", dst_len);
*cur = '\0';
}
@@ -408,10 +442,10 @@
const char16_t* const end = src + src_len;
while (src < end) {
if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
- && (*++src & 0xFC00) == 0xDC00) {
+ && (*(src + 1) & 0xFC00) == 0xDC00) {
// surrogate pairs are always 4 bytes.
ret += 4;
- src++;
+ src += 2;
} else {
ret += utf32_codepoint_utf8_length((char32_t) *src++);
}
@@ -511,7 +545,7 @@
//printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
}
-ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len)
+ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len, bool overreadIsFatal)
{
const uint8_t* const u8end = u8str + u8len;
const uint8_t* u8cur = u8str;
@@ -521,6 +555,20 @@
while (u8cur < u8end) {
u16measuredLen++;
int u8charLen = utf8_codepoint_len(*u8cur);
+ // Malformed utf8, some characters are beyond the end.
+ // Cases:
+ // If u8charLen == 1, this becomes u8cur >= u8end, which cannot happen as u8cur < u8end,
+ // then this condition fail and we continue, as expected.
+ // If u8charLen == 2, this becomes u8cur + 1 >= u8end, which fails only if
+ // u8cur == u8end - 1, that is, there was only one remaining character to read but we need
+ // 2 of them. This condition holds and we return -1, as expected.
+ if (u8cur + u8charLen - 1 >= u8end) {
+ if (overreadIsFatal) {
+ LOG_ALWAYS_FATAL("Attempt to overread computing length of utf8 string");
+ } else {
+ return -1;
+ }
+ }
uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen);
if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16
u8cur += u8charLen;
@@ -537,38 +585,21 @@
return u16measuredLen;
}
-char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str)
-{
- const uint8_t* const u8end = u8str + u8len;
- const uint8_t* u8cur = u8str;
- char16_t* u16cur = u16str;
-
- while (u8cur < u8end) {
- size_t u8len = utf8_codepoint_len(*u8cur);
- uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len);
-
- // Convert the UTF32 codepoint to one or more UTF16 codepoints
- if (codepoint <= 0xFFFF) {
- // Single UTF16 character
- *u16cur++ = (char16_t) codepoint;
- } else {
- // Multiple UTF16 characters with surrogates
- codepoint = codepoint - 0x10000;
- *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
- *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
- }
-
- u8cur += u8len;
- }
- return u16cur;
-}
-
-void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) {
- char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str);
+char16_t* utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str, size_t u16len) {
+ // A value > SSIZE_MAX is probably a negative value returned as an error and casted.
+ LOG_ALWAYS_FATAL_IF(u16len == 0 || u16len > SSIZE_MAX, "u16len is %zu", u16len);
+ char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str, u16len - 1);
*end = 0;
+ return end;
}
-char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
+char16_t* utf8_to_utf16_no_null_terminator(
+ const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
+ if (dstLen == 0) {
+ return dst;
+ }
+ // A value > SSIZE_MAX is probably a negative value returned as an error and casted.
+ LOG_ALWAYS_FATAL_IF(dstLen > SSIZE_MAX, "dstLen is %zu", dstLen);
const uint8_t* const u8end = src + srcLen;
const uint8_t* u8cur = src;
const char16_t* const u16end = dst + dstLen;
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index 30ca663..f7ca8f4 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -16,16 +16,18 @@
#define LOG_TAG "Vector"
-#include <string.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#include <cutils/log.h>
-
+#include <log/log.h>
#include <utils/Errors.h>
-#include <utils/SharedBuffer.h>
#include <utils/VectorImpl.h>
+#include <safe_iop.h>
+
+#include "SharedBuffer.h"
+
/*****************************************************************************/
@@ -85,14 +87,19 @@
void* VectorImpl::editArrayImpl()
{
if (mStorage) {
- SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit();
- if (sb == 0) {
- sb = SharedBuffer::alloc(capacity() * mItemSize);
- if (sb) {
- _do_copy(sb->data(), mStorage, mCount);
- release_storage();
- mStorage = sb->data();
- }
+ const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
+ SharedBuffer* editable = sb->attemptEdit();
+ if (editable == 0) {
+ // If we're here, we're not the only owner of the buffer.
+ // We must make a copy of it.
+ editable = SharedBuffer::alloc(sb->size());
+ // Fail instead of returning a pointer to storage that's not
+ // editable. Otherwise we'd be editing the contents of a buffer
+ // for which we're not the only owner, which is undefined behaviour.
+ LOG_ALWAYS_FATAL_IF(editable == NULL);
+ _do_copy(editable->data(), mStorage, mCount);
+ release_storage();
+ mStorage = editable->data();
}
}
return mStorage;
@@ -198,7 +205,10 @@
_do_copy(next, curr, 1);
next = curr;
--j;
- curr = reinterpret_cast<char*>(array) + mItemSize*(j);
+ curr = NULL;
+ if (j >= 0) {
+ curr = reinterpret_cast<char*>(array) + mItemSize*(j);
+ }
} while (j>=0 && (cmp(curr, temp, state) > 0));
_do_destroy(next, 1);
@@ -325,13 +335,15 @@
ssize_t VectorImpl::setCapacity(size_t new_capacity)
{
- size_t current_capacity = capacity();
- ssize_t amount = new_capacity - size();
- if (amount <= 0) {
- // we can't reduce the capacity
- return current_capacity;
- }
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ // The capacity must always be greater than or equal to the size
+ // of this vector.
+ if (new_capacity <= size()) {
+ return capacity();
+ }
+
+ size_t new_allocation_size = 0;
+ LOG_ALWAYS_FATAL_IF(!safe_mul(&new_allocation_size, new_capacity, mItemSize));
+ SharedBuffer* sb = SharedBuffer::alloc(new_allocation_size);
if (sb) {
void* array = sb->data();
_do_copy(array, mStorage, size());
@@ -373,9 +385,28 @@
"[%p] _grow: where=%d, amount=%d, count=%d",
this, (int)where, (int)amount, (int)mCount); // caller already checked
- const size_t new_size = mCount + amount;
+ size_t new_size;
+ LOG_ALWAYS_FATAL_IF(!safe_add(&new_size, mCount, amount), "new_size overflow");
+
if (capacity() < new_size) {
- const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
+ // NOTE: This implementation used to resize vectors as per ((3*x + 1) / 2)
+ // (sigh..). Also note, the " + 1" was necessary to handle the special case
+ // where x == 1, where the resized_capacity will be equal to the old
+ // capacity without the +1. The old calculation wouldn't work properly
+ // if x was zero.
+ //
+ // This approximates the old calculation, using (x + (x/2) + 1) instead.
+ size_t new_capacity = 0;
+ LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_size, (new_size / 2)),
+ "new_capacity overflow");
+ LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_capacity, static_cast<size_t>(1u)),
+ "new_capacity overflow");
+ new_capacity = max(kMinVectorCapacity, new_capacity);
+
+ size_t new_alloc_size = 0;
+ LOG_ALWAYS_FATAL_IF(!safe_mul(&new_alloc_size, new_capacity, mItemSize),
+ "new_alloc_size overflow");
+
// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
if ((mStorage) &&
(mCount==where) &&
@@ -383,14 +414,14 @@
(mFlags & HAS_TRIVIAL_DTOR))
{
const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
- SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+ SharedBuffer* sb = cur_sb->editResize(new_alloc_size);
if (sb) {
mStorage = sb->data();
} else {
return NULL;
}
} else {
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size);
if (sb) {
void* array = sb->data();
if (where != 0) {
@@ -432,10 +463,19 @@
"[%p] _shrink: where=%d, amount=%d, count=%d",
this, (int)where, (int)amount, (int)mCount); // caller already checked
- const size_t new_size = mCount - amount;
- if (new_size*3 < capacity()) {
- const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
-// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
+ size_t new_size;
+ LOG_ALWAYS_FATAL_IF(!safe_sub(&new_size, mCount, amount));
+
+ if (new_size < (capacity() / 2)) {
+ // NOTE: (new_size * 2) is safe because capacity didn't overflow and
+ // new_size < (capacity / 2)).
+ const size_t new_capacity = max(kMinVectorCapacity, new_size * 2);
+
+ // NOTE: (new_capacity * mItemSize), (where * mItemSize) and
+ // ((where + amount) * mItemSize) beyond this point are safe because
+ // we are always reducing the capacity of the underlying SharedBuffer.
+ // In other words, (old_capacity * mItemSize) did not overflow, and
+ // where < (where + amount) < new_capacity < old_capacity.
if ((where == new_size) &&
(mFlags & HAS_TRIVIAL_COPY) &&
(mFlags & HAS_TRIVIAL_DTOR))
@@ -551,6 +591,10 @@
ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
{
+ if (order) *order = 0;
+ if (isEmpty()) {
+ return NAME_NOT_FOUND;
+ }
// binary search
ssize_t err = NAME_NOT_FOUND;
ssize_t l = 0;
diff --git a/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
similarity index 100%
rename from include/utils/AndroidThreads.h
rename to libutils/include/utils/AndroidThreads.h
diff --git a/include/utils/Atomic.h b/libutils/include/utils/Atomic.h
similarity index 100%
rename from include/utils/Atomic.h
rename to libutils/include/utils/Atomic.h
diff --git a/include/utils/BitSet.h b/libutils/include/utils/BitSet.h
similarity index 100%
rename from include/utils/BitSet.h
rename to libutils/include/utils/BitSet.h
diff --git a/libutils/include/utils/BlobCache.h b/libutils/include/utils/BlobCache.h
new file mode 100644
index 0000000..65dca9f
--- /dev/null
+++ b/libutils/include/utils/BlobCache.h
@@ -0,0 +1,249 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT 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_BLOB_CACHE_H
+#define ANDROID_BLOB_CACHE_H
+
+#include <stddef.h>
+
+#include <utils/Flattenable.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+// A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache
+// does NOT provide any thread-safety guarantees.
+//
+// The cache contents can be serialized to an in-memory buffer or mmap'd file
+// and then reloaded in a subsequent execution of the program. This
+// serialization is non-portable and the data should only be used by the device
+// that generated it.
+class BlobCache : public RefBase {
+
+public:
+
+ // Create an empty blob cache. The blob cache will cache key/value pairs
+ // with key and value sizes less than or equal to maxKeySize and
+ // maxValueSize, respectively. The total combined size of ALL cache entries
+ // (key sizes plus value sizes) will not exceed maxTotalSize.
+ BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
+
+ // set inserts a new binary value into the cache and associates it with the
+ // given binary key. If the key or value are too large for the cache then
+ // the cache remains unchanged. This includes the case where a different
+ // value was previously associated with the given key - the old value will
+ // remain in the cache. If the given key and value are small enough to be
+ // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
+ // values specified to the BlobCache constructor), then the key/value pair
+ // will be in the cache after set returns. Note, however, that a subsequent
+ // call to set may evict old key/value pairs from the cache.
+ //
+ // Preconditions:
+ // key != NULL
+ // 0 < keySize
+ // value != NULL
+ // 0 < valueSize
+ void set(const void* key, size_t keySize, const void* value,
+ size_t valueSize);
+
+ // get retrieves from the cache the binary value associated with a given
+ // binary key. If the key is present in the cache then the length of the
+ // binary value associated with that key is returned. If the value argument
+ // is non-NULL and the size of the cached value is less than valueSize bytes
+ // then the cached value is copied into the buffer pointed to by the value
+ // argument. If the key is not present in the cache then 0 is returned and
+ // the buffer pointed to by the value argument is not modified.
+ //
+ // Note that when calling get multiple times with the same key, the later
+ // calls may fail, returning 0, even if earlier calls succeeded. The return
+ // value must be checked for each call.
+ //
+ // Preconditions:
+ // key != NULL
+ // 0 < keySize
+ // 0 <= valueSize
+ size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
+
+
+ // getFlattenedSize returns the number of bytes needed to store the entire
+ // serialized cache.
+ size_t getFlattenedSize() const;
+
+ // flatten serializes the current contents of the cache into the memory
+ // pointed to by 'buffer'. The serialized cache contents can later be
+ // loaded into a BlobCache object using the unflatten method. The contents
+ // of the BlobCache object will not be modified.
+ //
+ // Preconditions:
+ // size >= this.getFlattenedSize()
+ status_t flatten(void* buffer, size_t size) const;
+
+ // unflatten replaces the contents of the cache with the serialized cache
+ // contents in the memory pointed to by 'buffer'. The previous contents of
+ // the BlobCache will be evicted from the cache. If an error occurs while
+ // unflattening the serialized cache contents then the BlobCache will be
+ // left in an empty state.
+ //
+ status_t unflatten(void const* buffer, size_t size);
+
+private:
+ // Copying is disallowed.
+ BlobCache(const BlobCache&);
+ void operator=(const BlobCache&);
+
+ // A random function helper to get around MinGW not having nrand48()
+ long int blob_random();
+
+ // clean evicts a randomly chosen set of entries from the cache such that
+ // the total size of all remaining entries is less than mMaxTotalSize/2.
+ void clean();
+
+ // isCleanable returns true if the cache is full enough for the clean method
+ // to have some effect, and false otherwise.
+ bool isCleanable() const;
+
+ // A Blob is an immutable sized unstructured data blob.
+ class Blob : public RefBase {
+ public:
+ Blob(const void* data, size_t size, bool copyData);
+ ~Blob();
+
+ bool operator<(const Blob& rhs) const;
+
+ const void* getData() const;
+ size_t getSize() const;
+
+ private:
+ // Copying is not allowed.
+ Blob(const Blob&);
+ void operator=(const Blob&);
+
+ // mData points to the buffer containing the blob data.
+ const void* mData;
+
+ // mSize is the size of the blob data in bytes.
+ size_t mSize;
+
+ // mOwnsData indicates whether or not this Blob object should free the
+ // memory pointed to by mData when the Blob gets destructed.
+ bool mOwnsData;
+ };
+
+ // A CacheEntry is a single key/value pair in the cache.
+ class CacheEntry {
+ public:
+ CacheEntry();
+ CacheEntry(const sp<Blob>& key, const sp<Blob>& value);
+ CacheEntry(const CacheEntry& ce);
+
+ bool operator<(const CacheEntry& rhs) const;
+ const CacheEntry& operator=(const CacheEntry&);
+
+ sp<Blob> getKey() const;
+ sp<Blob> getValue() const;
+
+ void setValue(const sp<Blob>& value);
+
+ private:
+
+ // mKey is the key that identifies the cache entry.
+ sp<Blob> mKey;
+
+ // mValue is the cached data associated with the key.
+ sp<Blob> mValue;
+ };
+
+ // A Header is the header for the entire BlobCache serialization format. No
+ // need to make this portable, so we simply write the struct out.
+ struct Header {
+ // mMagicNumber is the magic number that identifies the data as
+ // serialized BlobCache contents. It must always contain 'Blb$'.
+ uint32_t mMagicNumber;
+
+ // mBlobCacheVersion is the serialization format version.
+ uint32_t mBlobCacheVersion;
+
+ // mDeviceVersion is the device-specific version of the cache. This can
+ // be used to invalidate the cache.
+ uint32_t mDeviceVersion;
+
+ // mNumEntries is number of cache entries following the header in the
+ // data.
+ size_t mNumEntries;
+
+ // mBuildId is the build id of the device when the cache was created.
+ // When an update to the build happens (via an OTA or other update) this
+ // is used to invalidate the cache.
+ int mBuildIdLength;
+ char mBuildId[];
+ };
+
+ // An EntryHeader is the header for a serialized cache entry. No need to
+ // make this portable, so we simply write the struct out. Each EntryHeader
+ // is followed imediately by the key data and then the value data.
+ //
+ // The beginning of each serialized EntryHeader is 4-byte aligned, so the
+ // number of bytes that a serialized cache entry will occupy is:
+ //
+ // ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
+ //
+ struct EntryHeader {
+ // mKeySize is the size of the entry key in bytes.
+ size_t mKeySize;
+
+ // mValueSize is the size of the entry value in bytes.
+ size_t mValueSize;
+
+ // mData contains both the key and value data for the cache entry. The
+ // key comes first followed immediately by the value.
+ uint8_t mData[];
+ };
+
+ // mMaxKeySize is the maximum key size that will be cached. Calls to
+ // BlobCache::set with a keySize parameter larger than mMaxKeySize will
+ // simply not add the key/value pair to the cache.
+ const size_t mMaxKeySize;
+
+ // mMaxValueSize is the maximum value size that will be cached. Calls to
+ // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
+ // simply not add the key/value pair to the cache.
+ const size_t mMaxValueSize;
+
+ // mMaxTotalSize is the maximum size that all cache entries can occupy. This
+ // includes space for both keys and values. When a call to BlobCache::set
+ // would otherwise cause this limit to be exceeded, either the key/value
+ // pair passed to BlobCache::set will not be cached or other cache entries
+ // will be evicted from the cache to make room for the new entry.
+ const size_t mMaxTotalSize;
+
+ // mTotalSize is the total combined size of all keys and values currently in
+ // the cache.
+ size_t mTotalSize;
+
+ // mRandState is the pseudo-random number generator state. It is passed to
+ // nrand48 to generate random numbers when needed.
+ unsigned short mRandState[3];
+
+ // mCacheEntries stores all the cache entries that are resident in memory.
+ // Cache entries are added to it by the 'set' method.
+ SortedVector<CacheEntry> mCacheEntries;
+};
+
+}
+
+#endif // ANDROID_BLOB_CACHE_H
diff --git a/include/utils/ByteOrder.h b/libutils/include/utils/ByteOrder.h
similarity index 100%
rename from include/utils/ByteOrder.h
rename to libutils/include/utils/ByteOrder.h
diff --git a/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
similarity index 100%
rename from include/utils/CallStack.h
rename to libutils/include/utils/CallStack.h
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
new file mode 100644
index 0000000..2709e3b
--- /dev/null
+++ b/libutils/include/utils/Compat.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LIB_UTILS_COMPAT_H
+#define __LIB_UTILS_COMPAT_H
+
+#include <unistd.h>
+
+#if defined(__APPLE__)
+
+/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+
+typedef off_t off64_t;
+
+static inline off64_t lseek64(int fd, off64_t offset, int whence) {
+ return lseek(fd, offset, whence);
+}
+
+static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) {
+ return pread(fd, buf, nbytes, offset);
+}
+
+static inline ssize_t pwrite64(int fd, const void* buf, size_t nbytes, off64_t offset) {
+ return pwrite(fd, buf, nbytes, offset);
+}
+
+#endif /* __APPLE__ */
+
+#if defined(_WIN32)
+#define O_CLOEXEC O_NOINHERIT
+#define O_NOFOLLOW 0
+#define DEFFILEMODE 0666
+#endif /* _WIN32 */
+
+#define ZD "%zd"
+#define ZD_TYPE ssize_t
+
+/*
+ * Needed for cases where something should be constexpr if possible, but not
+ * being constexpr is fine if in pre-C++11 code (such as a const static float
+ * member variable).
+ */
+#if __cplusplus >= 201103L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+#if defined(_WIN32)
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
+#endif
+
+#endif /* __LIB_UTILS_COMPAT_H */
diff --git a/libutils/include/utils/Condition.h b/libutils/include/utils/Condition.h
new file mode 100644
index 0000000..25a53aa
--- /dev/null
+++ b/libutils/include/utils/Condition.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_CONDITION_H
+#define _LIBS_UTILS_CONDITION_H
+
+#include <limits.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+/*
+ * Condition variable class. The implementation is system-dependent.
+ *
+ * Condition variables are paired up with mutexes. Lock the mutex,
+ * call wait(), then either re-wait() if things aren't quite what you want,
+ * or unlock the mutex and continue. All threads calling wait() must
+ * use the same mutex for a given Condition.
+ */
+class Condition {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ enum WakeUpType {
+ WAKE_UP_ONE = 0,
+ WAKE_UP_ALL = 1
+ };
+
+ Condition();
+ explicit Condition(int type);
+ ~Condition();
+ // Wait on the condition variable. Lock the mutex before calling.
+ status_t wait(Mutex& mutex);
+ // same with relative timeout
+ status_t waitRelative(Mutex& mutex, nsecs_t reltime);
+ // Signal the condition variable, allowing exactly one thread to continue.
+ void signal();
+ // Signal the condition variable, allowing one or all threads to continue.
+ void signal(WakeUpType type) {
+ if (type == WAKE_UP_ONE) {
+ signal();
+ } else {
+ broadcast();
+ }
+ }
+ // Signal the condition variable, allowing all threads to continue.
+ void broadcast();
+
+private:
+#if !defined(_WIN32)
+ pthread_cond_t mCond;
+#else
+ void* mState;
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+#if !defined(_WIN32)
+
+inline Condition::Condition() {
+ pthread_cond_init(&mCond, NULL);
+}
+inline Condition::Condition(int type) {
+ if (type == SHARED) {
+ pthread_condattr_t attr;
+ pthread_condattr_init(&attr);
+ pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_cond_init(&mCond, &attr);
+ pthread_condattr_destroy(&attr);
+ } else {
+ pthread_cond_init(&mCond, NULL);
+ }
+}
+inline Condition::~Condition() {
+ pthread_cond_destroy(&mCond);
+}
+inline status_t Condition::wait(Mutex& mutex) {
+ return -pthread_cond_wait(&mCond, &mutex.mMutex);
+}
+inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
+ struct timespec ts;
+#if defined(__linux__)
+ clock_gettime(CLOCK_REALTIME, &ts);
+#else // __APPLE__
+ // Apple doesn't support POSIX clocks.
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ ts.tv_sec = t.tv_sec;
+ ts.tv_nsec = t.tv_usec*1000;
+#endif
+
+ // On 32-bit devices, tv_sec is 32-bit, but `reltime` is 64-bit.
+ int64_t reltime_sec = reltime/1000000000;
+
+ ts.tv_nsec += static_cast<long>(reltime%1000000000);
+ if (reltime_sec < INT64_MAX && ts.tv_nsec >= 1000000000) {
+ ts.tv_nsec -= 1000000000;
+ ++reltime_sec;
+ }
+
+ int64_t time_sec = ts.tv_sec;
+ if (time_sec > INT64_MAX - reltime_sec) {
+ time_sec = INT64_MAX;
+ } else {
+ time_sec += reltime_sec;
+ }
+
+ ts.tv_sec = (time_sec > LONG_MAX) ? LONG_MAX : static_cast<long>(time_sec);
+
+ return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
+}
+inline void Condition::signal() {
+ /*
+ * POSIX says pthread_cond_signal wakes up "one or more" waiting threads.
+ * However bionic follows the glibc guarantee which wakes up "exactly one"
+ * waiting thread.
+ *
+ * man 3 pthread_cond_signal
+ * pthread_cond_signal restarts one of the threads that are waiting on
+ * the condition variable cond. If no threads are waiting on cond,
+ * nothing happens. If several threads are waiting on cond, exactly one
+ * is restarted, but it is not specified which.
+ */
+ pthread_cond_signal(&mCond);
+}
+inline void Condition::broadcast() {
+ pthread_cond_broadcast(&mCond);
+}
+
+#endif // !defined(_WIN32)
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_CONDITON_H
diff --git a/include/utils/Debug.h b/libutils/include/utils/Debug.h
similarity index 100%
rename from include/utils/Debug.h
rename to libutils/include/utils/Debug.h
diff --git a/include/utils/Endian.h b/libutils/include/utils/Endian.h
similarity index 100%
rename from include/utils/Endian.h
rename to libutils/include/utils/Endian.h
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
new file mode 100644
index 0000000..16e1fa2
--- /dev/null
+++ b/libutils/include/utils/Errors.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_ERRORS_H
+#define ANDROID_ERRORS_H
+
+#include <sys/types.h>
+#include <errno.h>
+
+namespace android {
+
+// use this type to return error codes
+#ifdef _WIN32
+typedef int status_t;
+#else
+typedef int32_t status_t;
+#endif
+
+/* the MS C runtime lacks a few error codes */
+
+/*
+ * Error codes.
+ * All error codes are negative values.
+ */
+
+// Win32 #defines NO_ERROR as well. It has the same value, so there's no
+// real conflict, though it's a bit awkward.
+#ifdef _WIN32
+# undef NO_ERROR
+#endif
+
+enum {
+ OK = 0, // Everything's swell.
+ NO_ERROR = 0, // No errors.
+
+ UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value
+
+ NO_MEMORY = -ENOMEM,
+ INVALID_OPERATION = -ENOSYS,
+ BAD_VALUE = -EINVAL,
+ BAD_TYPE = (UNKNOWN_ERROR + 1),
+ NAME_NOT_FOUND = -ENOENT,
+ PERMISSION_DENIED = -EPERM,
+ NO_INIT = -ENODEV,
+ ALREADY_EXISTS = -EEXIST,
+ DEAD_OBJECT = -EPIPE,
+ FAILED_TRANSACTION = (UNKNOWN_ERROR + 2),
+#if !defined(_WIN32)
+ BAD_INDEX = -EOVERFLOW,
+ NOT_ENOUGH_DATA = -ENODATA,
+ WOULD_BLOCK = -EWOULDBLOCK,
+ TIMED_OUT = -ETIMEDOUT,
+ UNKNOWN_TRANSACTION = -EBADMSG,
+#else
+ BAD_INDEX = -E2BIG,
+ NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3),
+ WOULD_BLOCK = (UNKNOWN_ERROR + 4),
+ TIMED_OUT = (UNKNOWN_ERROR + 5),
+ UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
+#endif
+ FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7),
+ UNEXPECTED_NULL = (UNKNOWN_ERROR + 8),
+};
+
+// Restore define; enumeration is in "android" namespace, so the value defined
+// there won't work for Win32 code in a different namespace.
+#ifdef _WIN32
+# define NO_ERROR 0L
+#endif
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_ERRORS_H
diff --git a/libutils/include/utils/FastStrcmp.h b/libutils/include/utils/FastStrcmp.h
new file mode 100644
index 0000000..3844e7d
--- /dev/null
+++ b/libutils/include/utils/FastStrcmp.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_UTILS_FASTSTRCMP_H__
+#define _ANDROID_UTILS_FASTSTRCMP_H__
+
+#ifdef __cplusplus
+
+// Optimized for instruction cache locality
+//
+// Template class fastcmp used to create more time-efficient str*cmp
+// functions by pre-checking the first character before resorting
+// to calling the underlying string function. Profiled with a
+// measurable speedup when used in hot code. Usage is of the form:
+//
+// fastcmp<strncmp>(str1, str2, len)
+//
+// NB: Does not work for the case insensitive str*cmp functions.
+// NB: Returns boolean, do not use if expecting to check negative value.
+// Thus not semantically identical to the expected function behavior.
+
+template <int (*cmp)(const char *l, const char *r, const size_t s)>
+static inline int fastcmp(const char *l, const char *r, const size_t s) {
+ return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+}
+
+template <int (*cmp)(const void *l, const void *r, const size_t s)>
+static inline int fastcmp(const void *lv, const void *rv, const size_t s) {
+ const char *l = static_cast<const char *>(lv);
+ const char *r = static_cast<const char *>(rv);
+ return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+}
+
+template <int (*cmp)(const char *l, const char *r)>
+static inline int fastcmp(const char *l, const char *r) {
+ return (*l != *r) || cmp(l + 1, r + 1);
+}
+
+#endif
+
+#endif // _ANDROID_UTILS_FASTSTRCMP_H__
diff --git a/libutils/include/utils/FileMap.h b/libutils/include/utils/FileMap.h
new file mode 100644
index 0000000..7d372e1
--- /dev/null
+++ b/libutils/include/utils/FileMap.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Encapsulate a shared file mapping.
+//
+#ifndef __LIBS_FILE_MAP_H
+#define __LIBS_FILE_MAP_H
+
+#include <sys/types.h>
+
+#include <utils/Compat.h>
+
+#if defined(__MINGW32__)
+// Ensure that we always pull in winsock2.h before windows.h
+#if defined(_WIN32)
+#include <winsock2.h>
+#endif
+#include <windows.h>
+#endif
+
+namespace android {
+
+/*
+ * This represents a memory-mapped file. It might be the entire file or
+ * only part of it. This requires a little bookkeeping because the mapping
+ * needs to be aligned on page boundaries, and in some cases we'd like to
+ * have multiple references to the mapped area without creating additional
+ * maps.
+ *
+ * This always uses MAP_SHARED.
+ *
+ * TODO: we should be able to create a new FileMap that is a subset of
+ * an existing FileMap and shares the underlying mapped pages. Requires
+ * completing the refcounting stuff and possibly introducing the notion
+ * of a FileMap hierarchy.
+ */
+class FileMap {
+public:
+ FileMap(void);
+
+ FileMap(FileMap&& f);
+ FileMap& operator=(FileMap&& f);
+
+ /*
+ * Create a new mapping on an open file.
+ *
+ * Closing the file descriptor does not unmap the pages, so we don't
+ * claim ownership of the fd.
+ *
+ * Returns "false" on failure.
+ */
+ bool create(const char* origFileName, int fd,
+ off64_t offset, size_t length, bool readOnly);
+
+ ~FileMap(void);
+
+ /*
+ * Return the name of the file this map came from, if known.
+ */
+ const char* getFileName(void) const { return mFileName; }
+
+ /*
+ * Get a pointer to the piece of the file we requested.
+ */
+ void* getDataPtr(void) const { return mDataPtr; }
+
+ /*
+ * Get the length we requested.
+ */
+ size_t getDataLength(void) const { return mDataLength; }
+
+ /*
+ * Get the data offset used to create this map.
+ */
+ off64_t getDataOffset(void) const { return mDataOffset; }
+
+ /*
+ * This maps directly to madvise() values, but allows us to avoid
+ * including <sys/mman.h> everywhere.
+ */
+ enum MapAdvice {
+ NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED
+ };
+
+ /*
+ * Apply an madvise() call to the entire file.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+ int advise(MapAdvice advice);
+
+protected:
+
+private:
+ // these are not implemented
+ FileMap(const FileMap& src);
+ const FileMap& operator=(const FileMap& src);
+
+ char* mFileName; // original file name, if known
+ void* mBasePtr; // base of mmap area; page aligned
+ size_t mBaseLength; // length, measured from "mBasePtr"
+ off64_t mDataOffset; // offset used when map was created
+ void* mDataPtr; // start of requested data, offset from base
+ size_t mDataLength; // length, measured from "mDataPtr"
+#if defined(__MINGW32__)
+ HANDLE mFileHandle; // Win32 file handle
+ HANDLE mFileMapping; // Win32 file mapping handle
+#endif
+
+ static long mPageSize;
+};
+
+}; // namespace android
+
+#endif // __LIBS_FILE_MAP_H
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
new file mode 100644
index 0000000..22b811a
--- /dev/null
+++ b/libutils/include/utils/Flattenable.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_FLATTENABLE_H
+#define ANDROID_UTILS_FLATTENABLE_H
+
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/Debug.h>
+
+#include <type_traits>
+
+namespace android {
+
+
+class FlattenableUtils {
+public:
+ template<size_t N>
+ static size_t align(size_t size) {
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
+ return (size + (N-1)) & ~(N-1);
+ }
+
+ template<size_t N>
+ static size_t align(void const*& buffer) {
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
+ uintptr_t b = uintptr_t(buffer);
+ buffer = reinterpret_cast<void*>((uintptr_t(buffer) + (N-1)) & ~(N-1));
+ return size_t(uintptr_t(buffer) - b);
+ }
+
+ template<size_t N>
+ static size_t align(void*& buffer) {
+ return align<N>( const_cast<void const*&>(buffer) );
+ }
+
+ static void advance(void*& buffer, size_t& size, size_t offset) {
+ buffer = reinterpret_cast<void*>( uintptr_t(buffer) + offset );
+ size -= offset;
+ }
+
+ static void advance(void const*& buffer, size_t& size, size_t offset) {
+ buffer = reinterpret_cast<void const*>( uintptr_t(buffer) + offset );
+ size -= offset;
+ }
+
+ // write a POD structure
+ template<typename T>
+ static void write(void*& buffer, size_t& size, const T& value) {
+ static_assert(std::is_trivially_copyable<T>::value,
+ "Cannot flatten a non-trivially-copyable type");
+ memcpy(buffer, &value, sizeof(T));
+ advance(buffer, size, sizeof(T));
+ }
+
+ // read a POD structure
+ template<typename T>
+ static void read(void const*& buffer, size_t& size, T& value) {
+ static_assert(std::is_trivially_copyable<T>::value,
+ "Cannot unflatten a non-trivially-copyable type");
+ memcpy(&value, buffer, sizeof(T));
+ advance(buffer, size, sizeof(T));
+ }
+};
+
+
+/*
+ * The Flattenable protocol allows an object to serialize itself out
+ * to a byte-buffer and an array of file descriptors.
+ * Flattenable objects must implement this protocol.
+ */
+
+template <typename T>
+class Flattenable {
+public:
+ // size in bytes of the flattened object
+ inline size_t getFlattenedSize() const;
+
+ // number of file descriptors to flatten
+ inline size_t getFdCount() const;
+
+ // flattens the object into buffer.
+ // size should be at least of getFlattenedSize()
+ // file descriptors are written in the fds[] array but ownership is
+ // not transfered (ie: they must be dupped by the caller of
+ // flatten() if needed).
+ inline status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+
+ // unflattens the object from buffer.
+ // size should be equal to the value of getFlattenedSize() when the
+ // object was flattened.
+ // unflattened file descriptors are found in the fds[] array and
+ // don't need to be dupped(). ie: the caller of unflatten doesn't
+ // keep ownership. If a fd is not retained by unflatten() it must be
+ // explicitly closed.
+ inline status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+};
+
+template<typename T>
+inline size_t Flattenable<T>::getFlattenedSize() const {
+ return static_cast<T const*>(this)->T::getFlattenedSize();
+}
+template<typename T>
+inline size_t Flattenable<T>::getFdCount() const {
+ return static_cast<T const*>(this)->T::getFdCount();
+}
+template<typename T>
+inline status_t Flattenable<T>::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ return static_cast<T const*>(this)->T::flatten(buffer, size, fds, count);
+}
+template<typename T>
+inline status_t Flattenable<T>::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ return static_cast<T*>(this)->T::unflatten(buffer, size, fds, count);
+}
+
+/*
+ * LightFlattenable is a protocol allowing object to serialize themselves out
+ * to a byte-buffer. Because it doesn't handle file-descriptors,
+ * LightFlattenable is usually more size efficient than Flattenable.
+ * LightFlattenable objects must implement this protocol.
+ */
+template <typename T>
+class LightFlattenable {
+public:
+ // returns whether this object always flatten into the same size.
+ // for efficiency, this should always be inline.
+ inline bool isFixedSize() const;
+
+ // returns size in bytes of the flattened object. must be a constant.
+ inline size_t getFlattenedSize() const;
+
+ // flattens the object into buffer.
+ inline status_t flatten(void* buffer, size_t size) const;
+
+ // unflattens the object from buffer of given size.
+ inline status_t unflatten(void const* buffer, size_t size);
+};
+
+template <typename T>
+inline bool LightFlattenable<T>::isFixedSize() const {
+ return static_cast<T const*>(this)->T::isFixedSize();
+}
+template <typename T>
+inline size_t LightFlattenable<T>::getFlattenedSize() const {
+ return static_cast<T const*>(this)->T::getFlattenedSize();
+}
+template <typename T>
+inline status_t LightFlattenable<T>::flatten(void* buffer, size_t size) const {
+ return static_cast<T const*>(this)->T::flatten(buffer, size);
+}
+template <typename T>
+inline status_t LightFlattenable<T>::unflatten(void const* buffer, size_t size) {
+ return static_cast<T*>(this)->T::unflatten(buffer, size);
+}
+
+/*
+ * LightFlattenablePod is an implementation of the LightFlattenable protocol
+ * for POD (plain-old-data) objects.
+ * Simply derive from LightFlattenablePod<Foo> to make Foo flattenable; no
+ * need to implement any methods; obviously Foo must be a POD structure.
+ */
+template <typename T>
+class LightFlattenablePod : public LightFlattenable<T> {
+public:
+ inline bool isFixedSize() const {
+ return true;
+ }
+
+ inline size_t getFlattenedSize() const {
+ return sizeof(T);
+ }
+ inline status_t flatten(void* buffer, size_t size) const {
+ if (size < sizeof(T)) return NO_MEMORY;
+ *reinterpret_cast<T*>(buffer) = *static_cast<T const*>(this);
+ return NO_ERROR;
+ }
+ inline status_t unflatten(void const* buffer, size_t) {
+ *static_cast<T*>(this) = *reinterpret_cast<T const*>(buffer);
+ return NO_ERROR;
+ }
+};
+
+
+}; // namespace android
+
+
+#endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/include/utils/Functor.h b/libutils/include/utils/Functor.h
similarity index 100%
rename from include/utils/Functor.h
rename to libutils/include/utils/Functor.h
diff --git a/libutils/include/utils/JenkinsHash.h b/libutils/include/utils/JenkinsHash.h
new file mode 100644
index 0000000..027c10c
--- /dev/null
+++ b/libutils/include/utils/JenkinsHash.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Implementation of Jenkins one-at-a-time hash function. These choices are
+ * optimized for code size and portability, rather than raw speed. But speed
+ * should still be quite good.
+ **/
+
+#ifndef ANDROID_JENKINS_HASH_H
+#define ANDROID_JENKINS_HASH_H
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+/* The Jenkins hash of a sequence of 32 bit words A, B, C is:
+ * Whiten(Mix(Mix(Mix(0, A), B), C)) */
+
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
+inline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) {
+ hash += data;
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ return hash;
+}
+
+hash_t JenkinsHashWhiten(uint32_t hash);
+
+/* Helpful utility functions for hashing data in 32 bit chunks */
+uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size);
+
+uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size);
+
+}
+
+#endif // ANDROID_JENKINS_HASH_H
diff --git a/libutils/include/utils/KeyedVector.h b/libutils/include/utils/KeyedVector.h
new file mode 100644
index 0000000..f93ad6e
--- /dev/null
+++ b/libutils/include/utils/KeyedVector.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_KEYED_VECTOR_H
+#define ANDROID_KEYED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/SortedVector.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+template <typename KEY, typename VALUE>
+class KeyedVector
+{
+public:
+ typedef KEY key_type;
+ typedef VALUE value_type;
+
+ inline KeyedVector();
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { mVector.clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return mVector.size(); }
+ //! returns whether or not the vector is empty
+ inline bool isEmpty() const { return mVector.isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return mVector.capacity(); }
+ //! sets the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); }
+
+ // returns true if the arguments is known to be identical to this vector
+ inline bool isIdenticalTo(const KeyedVector& rhs) const;
+
+ /*!
+ * accessors
+ */
+ const VALUE& valueFor(const KEY& key) const;
+ const VALUE& valueAt(size_t index) const;
+ const KEY& keyAt(size_t index) const;
+ ssize_t indexOfKey(const KEY& key) const;
+ const VALUE& operator[] (size_t index) const;
+
+ /*!
+ * modifying the array
+ */
+
+ VALUE& editValueFor(const KEY& key);
+ VALUE& editValueAt(size_t index);
+
+ /*!
+ * add/insert/replace items
+ */
+
+ ssize_t add(const KEY& key, const VALUE& item);
+ ssize_t replaceValueFor(const KEY& key, const VALUE& item);
+ ssize_t replaceValueAt(size_t index, const VALUE& item);
+
+ /*!
+ * remove items
+ */
+
+ ssize_t removeItem(const KEY& key);
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+
+private:
+ SortedVector< key_value_pair_t<KEY, VALUE> > mVector;
+};
+
+// ---------------------------------------------------------------------------
+
+/**
+ * Variation of KeyedVector that holds a default value to return when
+ * valueFor() is called with a key that doesn't exist.
+ */
+template <typename KEY, typename VALUE>
+class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
+{
+public:
+ inline DefaultKeyedVector(const VALUE& defValue = VALUE());
+ const VALUE& valueFor(const KEY& key) const;
+
+private:
+ VALUE mDefault;
+};
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+KeyedVector<KEY,VALUE>::KeyedVector()
+{
+}
+
+template<typename KEY, typename VALUE> inline
+bool KeyedVector<KEY,VALUE>::isIdenticalTo(const KeyedVector<KEY,VALUE>& rhs) const {
+ return mVector.array() == rhs.mVector.array();
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
+ return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = this->indexOfKey(key);
+ LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__);
+ return mVector.itemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
+ return mVector.itemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::operator[] (size_t index) const {
+ return valueAt(index);
+}
+
+template<typename KEY, typename VALUE> inline
+const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
+ return mVector.itemAt(index).key;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
+ ssize_t i = this->indexOfKey(key);
+ LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__);
+ return mVector.editItemAt(static_cast<size_t>(i)).value;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
+ return mVector.editItemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
+ return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
+ key_value_pair_t<KEY,VALUE> pair(key, value);
+ mVector.remove(pair);
+ return mVector.add(pair);
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
+ if (index<size()) {
+ mVector.editItemAt(index).value = item;
+ return static_cast<ssize_t>(index);
+ }
+ return BAD_INDEX;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
+ return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
+ return mVector.removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
+ : mDefault(defValue)
+{
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = this->indexOfKey(key);
+ return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_KEYED_VECTOR_H
diff --git a/include/utils/LinearTransform.h b/libutils/include/utils/LinearTransform.h
similarity index 100%
rename from include/utils/LinearTransform.h
rename to libutils/include/utils/LinearTransform.h
diff --git a/include/utils/List.h b/libutils/include/utils/List.h
similarity index 100%
rename from include/utils/List.h
rename to libutils/include/utils/List.h
diff --git a/libutils/include/utils/Log.h b/libutils/include/utils/Log.h
new file mode 100644
index 0000000..5276a49
--- /dev/null
+++ b/libutils/include/utils/Log.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// C/C++ logging functions. See the logging documentation for API details.
+//
+// We'd like these to be available from C code (in case we import some from
+// somewhere), so this has a C interface.
+//
+// The output will be correct when the log file is shared between multiple
+// threads and/or multiple processes so long as the operating system
+// supports O_APPEND. These calls have mutex-protected data structures
+// and so are NOT reentrant. Do not use LOG in a signal handler.
+//
+#ifndef _LIBS_UTILS_LOG_H
+#define _LIBS_UTILS_LOG_H
+
+#include <sys/types.h>
+
+#include <log/log.h>
+
+#ifdef __cplusplus
+
+namespace android {
+
+/*
+ * A very simple utility that yells in the log when an operation takes too long.
+ */
+class LogIfSlow {
+public:
+ LogIfSlow(const char* tag, android_LogPriority priority,
+ int timeoutMillis, const char* message);
+ ~LogIfSlow();
+
+private:
+ const char* const mTag;
+ const android_LogPriority mPriority;
+ const int mTimeoutMillis;
+ const char* const mMessage;
+ const int64_t mStart;
+};
+
+/*
+ * Writes the specified debug log message if this block takes longer than the
+ * specified number of milliseconds to run. Includes the time actually taken.
+ *
+ * {
+ * ALOGD_IF_SLOW(50, "Excessive delay doing something.");
+ * doSomething();
+ * }
+ */
+#define ALOGD_IF_SLOW(timeoutMillis, message) \
+ android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message);
+
+} // namespace android
+
+#endif // __cplusplus
+
+#endif // _LIBS_UTILS_LOG_H
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
new file mode 100644
index 0000000..a62e67f
--- /dev/null
+++ b/libutils/include/utils/Looper.h
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_LOOPER_H
+#define UTILS_LOOPER_H
+
+#include <utils/threads.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/Timers.h>
+
+#include <sys/epoll.h>
+
+namespace android {
+
+/*
+ * NOTE: Since Looper is used to implement the NDK ALooper, the Looper
+ * enums and the signature of Looper_callbackFunc need to align with
+ * that implementation.
+ */
+
+/**
+ * For callback-based event loops, this is the prototype of the function
+ * that is called when a file descriptor event occurs.
+ * It is given the file descriptor it is associated with,
+ * a bitmask of the poll events that were triggered (typically EVENT_INPUT),
+ * and the data pointer that was originally supplied.
+ *
+ * Implementations should return 1 to continue receiving callbacks, or 0
+ * to have this file descriptor and callback unregistered from the looper.
+ */
+typedef int (*Looper_callbackFunc)(int fd, int events, void* data);
+
+/**
+ * A message that can be posted to a Looper.
+ */
+struct Message {
+ Message() : what(0) { }
+ Message(int w) : what(w) { }
+
+ /* The message type. (interpretation is left up to the handler) */
+ int what;
+};
+
+
+/**
+ * Interface for a Looper message handler.
+ *
+ * The Looper holds a strong reference to the message handler whenever it has
+ * a message to deliver to it. Make sure to call Looper::removeMessages
+ * to remove any pending messages destined for the handler so that the handler
+ * can be destroyed.
+ */
+class MessageHandler : public virtual RefBase {
+protected:
+ virtual ~MessageHandler();
+
+public:
+ /**
+ * Handles a message.
+ */
+ virtual void handleMessage(const Message& message) = 0;
+};
+
+
+/**
+ * A simple proxy that holds a weak reference to a message handler.
+ */
+class WeakMessageHandler : public MessageHandler {
+protected:
+ virtual ~WeakMessageHandler();
+
+public:
+ WeakMessageHandler(const wp<MessageHandler>& handler);
+ virtual void handleMessage(const Message& message);
+
+private:
+ wp<MessageHandler> mHandler;
+};
+
+
+/**
+ * A looper callback.
+ */
+class LooperCallback : public virtual RefBase {
+protected:
+ virtual ~LooperCallback();
+
+public:
+ /**
+ * Handles a poll event for the given file descriptor.
+ * It is given the file descriptor it is associated with,
+ * a bitmask of the poll events that were triggered (typically EVENT_INPUT),
+ * and the data pointer that was originally supplied.
+ *
+ * Implementations should return 1 to continue receiving callbacks, or 0
+ * to have this file descriptor and callback unregistered from the looper.
+ */
+ virtual int handleEvent(int fd, int events, void* data) = 0;
+};
+
+/**
+ * Wraps a Looper_callbackFunc function pointer.
+ */
+class SimpleLooperCallback : public LooperCallback {
+protected:
+ virtual ~SimpleLooperCallback();
+
+public:
+ SimpleLooperCallback(Looper_callbackFunc callback);
+ virtual int handleEvent(int fd, int events, void* data);
+
+private:
+ Looper_callbackFunc mCallback;
+};
+
+/**
+ * A polling loop that supports monitoring file descriptor events, optionally
+ * using callbacks. The implementation uses epoll() internally.
+ *
+ * A looper can be associated with a thread although there is no requirement that it must be.
+ */
+class Looper : public RefBase {
+protected:
+ virtual ~Looper();
+
+public:
+ enum {
+ /**
+ * Result from Looper_pollOnce() and Looper_pollAll():
+ * The poll was awoken using wake() before the timeout expired
+ * and no callbacks were executed and no other file descriptors were ready.
+ */
+ POLL_WAKE = -1,
+
+ /**
+ * Result from Looper_pollOnce() and Looper_pollAll():
+ * One or more callbacks were executed.
+ */
+ POLL_CALLBACK = -2,
+
+ /**
+ * Result from Looper_pollOnce() and Looper_pollAll():
+ * The timeout expired.
+ */
+ POLL_TIMEOUT = -3,
+
+ /**
+ * Result from Looper_pollOnce() and Looper_pollAll():
+ * An error occurred.
+ */
+ POLL_ERROR = -4,
+ };
+
+ /**
+ * Flags for file descriptor events that a looper can monitor.
+ *
+ * These flag bits can be combined to monitor multiple events at once.
+ */
+ enum {
+ /**
+ * The file descriptor is available for read operations.
+ */
+ EVENT_INPUT = 1 << 0,
+
+ /**
+ * The file descriptor is available for write operations.
+ */
+ EVENT_OUTPUT = 1 << 1,
+
+ /**
+ * The file descriptor has encountered an error condition.
+ *
+ * The looper always sends notifications about errors; it is not necessary
+ * to specify this event flag in the requested event set.
+ */
+ EVENT_ERROR = 1 << 2,
+
+ /**
+ * The file descriptor was hung up.
+ * For example, indicates that the remote end of a pipe or socket was closed.
+ *
+ * The looper always sends notifications about hangups; it is not necessary
+ * to specify this event flag in the requested event set.
+ */
+ EVENT_HANGUP = 1 << 3,
+
+ /**
+ * The file descriptor is invalid.
+ * For example, the file descriptor was closed prematurely.
+ *
+ * The looper always sends notifications about invalid file descriptors; it is not necessary
+ * to specify this event flag in the requested event set.
+ */
+ EVENT_INVALID = 1 << 4,
+ };
+
+ enum {
+ /**
+ * Option for Looper_prepare: this looper will accept calls to
+ * Looper_addFd() that do not have a callback (that is provide NULL
+ * for the callback). In this case the caller of Looper_pollOnce()
+ * or Looper_pollAll() MUST check the return from these functions to
+ * discover when data is available on such fds and process it.
+ */
+ PREPARE_ALLOW_NON_CALLBACKS = 1<<0
+ };
+
+ /**
+ * Creates a looper.
+ *
+ * If allowNonCallbaks is true, the looper will allow file descriptors to be
+ * registered without associated callbacks. This assumes that the caller of
+ * pollOnce() is prepared to handle callback-less events itself.
+ */
+ Looper(bool allowNonCallbacks);
+
+ /**
+ * Returns whether this looper instance allows the registration of file descriptors
+ * using identifiers instead of callbacks.
+ */
+ bool getAllowNonCallbacks() const;
+
+ /**
+ * Waits for events to be available, with optional timeout in milliseconds.
+ * Invokes callbacks for all file descriptors on which an event occurred.
+ *
+ * If the timeout is zero, returns immediately without blocking.
+ * If the timeout is negative, waits indefinitely until an event appears.
+ *
+ * Returns POLL_WAKE if the poll was awoken using wake() before
+ * the timeout expired and no callbacks were invoked and no other file
+ * descriptors were ready.
+ *
+ * Returns POLL_CALLBACK if one or more callbacks were invoked.
+ *
+ * Returns POLL_TIMEOUT if there was no data before the given
+ * timeout expired.
+ *
+ * Returns POLL_ERROR if an error occurred.
+ *
+ * Returns a value >= 0 containing an identifier if its file descriptor has data
+ * and it has no callback function (requiring the caller here to handle it).
+ * In this (and only this) case outFd, outEvents and outData will contain the poll
+ * events and data associated with the fd, otherwise they will be set to NULL.
+ *
+ * This method does not return until it has finished invoking the appropriate callbacks
+ * for all file descriptors that were signalled.
+ */
+ int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
+ inline int pollOnce(int timeoutMillis) {
+ return pollOnce(timeoutMillis, NULL, NULL, NULL);
+ }
+
+ /**
+ * Like pollOnce(), but performs all pending callbacks until all
+ * data has been consumed or a file descriptor is available with no callback.
+ * This function will never return POLL_CALLBACK.
+ */
+ int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData);
+ inline int pollAll(int timeoutMillis) {
+ return pollAll(timeoutMillis, NULL, NULL, NULL);
+ }
+
+ /**
+ * Wakes the poll asynchronously.
+ *
+ * This method can be called on any thread.
+ * This method returns immediately.
+ */
+ void wake();
+
+ /**
+ * Adds a new file descriptor to be polled by the looper.
+ * If the same file descriptor was previously added, it is replaced.
+ *
+ * "fd" is the file descriptor to be added.
+ * "ident" is an identifier for this event, which is returned from pollOnce().
+ * The identifier must be >= 0, or POLL_CALLBACK if providing a non-NULL callback.
+ * "events" are the poll events to wake up on. Typically this is EVENT_INPUT.
+ * "callback" is the function to call when there is an event on the file descriptor.
+ * "data" is a private data pointer to supply to the callback.
+ *
+ * There are two main uses of this function:
+ *
+ * (1) If "callback" is non-NULL, then this function will be called when there is
+ * data on the file descriptor. It should execute any events it has pending,
+ * appropriately reading from the file descriptor. The 'ident' is ignored in this case.
+ *
+ * (2) If "callback" is NULL, the 'ident' will be returned by Looper_pollOnce
+ * when its file descriptor has data available, requiring the caller to take
+ * care of processing it.
+ *
+ * Returns 1 if the file descriptor was added, 0 if the arguments were invalid.
+ *
+ * This method can be called on any thread.
+ * This method may block briefly if it needs to wake the poll.
+ *
+ * The callback may either be specified as a bare function pointer or as a smart
+ * pointer callback object. The smart pointer should be preferred because it is
+ * easier to avoid races when the callback is removed from a different thread.
+ * See removeFd() for details.
+ */
+ int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data);
+ int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
+
+ /**
+ * Removes a previously added file descriptor from the looper.
+ *
+ * When this method returns, it is safe to close the file descriptor since the looper
+ * will no longer have a reference to it. However, it is possible for the callback to
+ * already be running or for it to run one last time if the file descriptor was already
+ * signalled. Calling code is responsible for ensuring that this case is safely handled.
+ * For example, if the callback takes care of removing itself during its own execution either
+ * by returning 0 or by calling this method, then it can be guaranteed to not be invoked
+ * again at any later time unless registered anew.
+ *
+ * A simple way to avoid this problem is to use the version of addFd() that takes
+ * a sp<LooperCallback> instead of a bare function pointer. The LooperCallback will
+ * be released at the appropriate time by the Looper.
+ *
+ * Returns 1 if the file descriptor was removed, 0 if none was previously registered.
+ *
+ * This method can be called on any thread.
+ * This method may block briefly if it needs to wake the poll.
+ */
+ int removeFd(int fd);
+
+ /**
+ * Enqueues a message to be processed by the specified handler.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessage(const sp<MessageHandler>& handler, const Message& message);
+
+ /**
+ * Enqueues a message to be processed by the specified handler after all pending messages
+ * after the specified delay.
+ *
+ * The time delay is specified in uptime nanoseconds.
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
+ const Message& message);
+
+ /**
+ * Enqueues a message to be processed by the specified handler after all pending messages
+ * at the specified time.
+ *
+ * The time is specified in uptime nanoseconds.
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
+ const Message& message);
+
+ /**
+ * Removes all messages for the specified handler from the queue.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void removeMessages(const sp<MessageHandler>& handler);
+
+ /**
+ * Removes all messages of a particular type for the specified handler from the queue.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void removeMessages(const sp<MessageHandler>& handler, int what);
+
+ /**
+ * Returns whether this looper's thread is currently polling for more work to do.
+ * This is a good signal that the loop is still alive rather than being stuck
+ * handling a callback. Note that this method is intrinsically racy, since the
+ * state of the loop can change before you get the result back.
+ */
+ bool isPolling() const;
+
+ /**
+ * Prepares a looper associated with the calling thread, and returns it.
+ * If the thread already has a looper, it is returned. Otherwise, a new
+ * one is created, associated with the thread, and returned.
+ *
+ * The opts may be PREPARE_ALLOW_NON_CALLBACKS or 0.
+ */
+ static sp<Looper> prepare(int opts);
+
+ /**
+ * Sets the given looper to be associated with the calling thread.
+ * If another looper is already associated with the thread, it is replaced.
+ *
+ * If "looper" is NULL, removes the currently associated looper.
+ */
+ static void setForThread(const sp<Looper>& looper);
+
+ /**
+ * Returns the looper associated with the calling thread, or NULL if
+ * there is not one.
+ */
+ static sp<Looper> getForThread();
+
+private:
+ struct Request {
+ int fd;
+ int ident;
+ int events;
+ int seq;
+ sp<LooperCallback> callback;
+ void* data;
+
+ void initEventItem(struct epoll_event* eventItem) const;
+ };
+
+ struct Response {
+ int events;
+ Request request;
+ };
+
+ struct MessageEnvelope {
+ MessageEnvelope() : uptime(0) { }
+
+ MessageEnvelope(nsecs_t u, const sp<MessageHandler> h,
+ const Message& m) : uptime(u), handler(h), message(m) {
+ }
+
+ nsecs_t uptime;
+ sp<MessageHandler> handler;
+ Message message;
+ };
+
+ const bool mAllowNonCallbacks; // immutable
+
+ int mWakeEventFd; // immutable
+ Mutex mLock;
+
+ Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
+ bool mSendingMessage; // guarded by mLock
+
+ // Whether we are currently waiting for work. Not protected by a lock,
+ // any use of it is racy anyway.
+ volatile bool mPolling;
+
+ int mEpollFd; // guarded by mLock but only modified on the looper thread
+ bool mEpollRebuildRequired; // guarded by mLock
+
+ // Locked list of file descriptor monitoring requests.
+ KeyedVector<int, Request> mRequests; // guarded by mLock
+ int mNextRequestSeq;
+
+ // This state is only used privately by pollOnce and does not require a lock since
+ // it runs on a single thread.
+ Vector<Response> mResponses;
+ size_t mResponseIndex;
+ nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
+
+ int pollInner(int timeoutMillis);
+ int removeFd(int fd, int seq);
+ void awoken();
+ void pushResponse(int events, const Request& request);
+ void rebuildEpollLocked();
+ void scheduleEpollRebuildLocked();
+
+ static void initTLSKey();
+ static void threadDestructor(void *st);
+ static void initEpollEvent(struct epoll_event* eventItem);
+};
+
+} // namespace android
+
+#endif // UTILS_LOOPER_H
diff --git a/libutils/include/utils/LruCache.h b/libutils/include/utils/LruCache.h
new file mode 100644
index 0000000..89dccd6
--- /dev/null
+++ b/libutils/include/utils/LruCache.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_UTILS_LRU_CACHE_H
+#define ANDROID_UTILS_LRU_CACHE_H
+
+#include <memory>
+#include <unordered_set>
+
+#include "utils/TypeHelpers.h" // hash_t
+
+namespace android {
+
+/**
+ * GenerationCache callback used when an item is removed
+ */
+template<typename EntryKey, typename EntryValue>
+class OnEntryRemoved {
+public:
+ virtual ~OnEntryRemoved() { };
+ virtual void operator()(EntryKey& key, EntryValue& value) = 0;
+}; // class OnEntryRemoved
+
+template <typename TKey, typename TValue>
+class LruCache {
+public:
+ explicit LruCache(uint32_t maxCapacity);
+ virtual ~LruCache();
+
+ enum Capacity {
+ kUnlimitedCapacity,
+ };
+
+ void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener);
+ size_t size() const;
+ const TValue& get(const TKey& key);
+ bool put(const TKey& key, const TValue& value);
+ bool remove(const TKey& key);
+ bool removeOldest();
+ void clear();
+ const TValue& peekOldestValue();
+
+private:
+ LruCache(const LruCache& that); // disallow copy constructor
+
+ // Super class so that we can have entries having only a key reference, for searches.
+ class KeyedEntry {
+ public:
+ virtual const TKey& getKey() const = 0;
+ // Make sure the right destructor is executed so that keys and values are deleted.
+ virtual ~KeyedEntry() {}
+ };
+
+ class Entry final : public KeyedEntry {
+ public:
+ TKey key;
+ TValue value;
+ Entry* parent;
+ Entry* child;
+
+ Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(NULL), child(NULL) {
+ }
+ const TKey& getKey() const final { return key; }
+ };
+
+ class EntryForSearch : public KeyedEntry {
+ public:
+ const TKey& key;
+ EntryForSearch(const TKey& key_) : key(key_) {
+ }
+ const TKey& getKey() const final { return key; }
+ };
+
+ struct HashForEntry : public std::unary_function<KeyedEntry*, hash_t> {
+ size_t operator() (const KeyedEntry* entry) const {
+ return hash_type(entry->getKey());
+ };
+ };
+
+ struct EqualityForHashedEntries : public std::unary_function<KeyedEntry*, hash_t> {
+ bool operator() (const KeyedEntry* lhs, const KeyedEntry* rhs) const {
+ return lhs->getKey() == rhs->getKey();
+ };
+ };
+
+ // All entries in the set will be Entry*. Using the weaker KeyedEntry as to allow entries
+ // that have only a key reference, for searching.
+ typedef std::unordered_set<KeyedEntry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;
+
+ void attachToCache(Entry& entry);
+ void detachFromCache(Entry& entry);
+
+ typename LruCacheSet::iterator findByKey(const TKey& key) {
+ EntryForSearch entryForSearch(key);
+ typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
+ return result;
+ }
+
+ std::unique_ptr<LruCacheSet> mSet;
+ OnEntryRemoved<TKey, TValue>* mListener;
+ Entry* mOldest;
+ Entry* mYoungest;
+ uint32_t mMaxCapacity;
+ TValue mNullValue;
+
+public:
+ // To be used like:
+ // while (it.next()) {
+ // it.value(); it.key();
+ // }
+ class Iterator {
+ public:
+ Iterator(const LruCache<TKey, TValue>& cache):
+ mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
+ }
+
+ bool next() {
+ if (mIterator == mCache.mSet->end()) {
+ return false;
+ }
+ if (!mBeginReturned) {
+ // mIterator has been initialized to the beginning and
+ // hasn't been returned. Do not advance:
+ mBeginReturned = true;
+ } else {
+ std::advance(mIterator, 1);
+ }
+ bool ret = (mIterator != mCache.mSet->end());
+ return ret;
+ }
+
+ const TValue& value() const {
+ // All the elements in the set are of type Entry. See comment in the definition
+ // of LruCacheSet above.
+ return reinterpret_cast<Entry *>(*mIterator)->value;
+ }
+
+ const TKey& key() const {
+ return (*mIterator)->getKey();
+ }
+ private:
+ const LruCache<TKey, TValue>& mCache;
+ typename LruCacheSet::iterator mIterator;
+ bool mBeginReturned;
+ };
+};
+
+// Implementation is here, because it's fully templated
+template <typename TKey, typename TValue>
+LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
+ : mSet(new LruCacheSet())
+ , mListener(NULL)
+ , mOldest(NULL)
+ , mYoungest(NULL)
+ , mMaxCapacity(maxCapacity)
+ , mNullValue(0) {
+ mSet->max_load_factor(1.0);
+};
+
+template <typename TKey, typename TValue>
+LruCache<TKey, TValue>::~LruCache() {
+ // Need to delete created entries.
+ clear();
+};
+
+template<typename K, typename V>
+void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
+ mListener = listener;
+}
+
+template <typename TKey, typename TValue>
+size_t LruCache<TKey, TValue>::size() const {
+ return mSet->size();
+}
+
+template <typename TKey, typename TValue>
+const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
+ typename LruCacheSet::const_iterator find_result = findByKey(key);
+ if (find_result == mSet->end()) {
+ return mNullValue;
+ }
+ // All the elements in the set are of type Entry. See comment in the definition
+ // of LruCacheSet above.
+ Entry *entry = reinterpret_cast<Entry*>(*find_result);
+ detachFromCache(*entry);
+ attachToCache(*entry);
+ return entry->value;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
+ if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
+ removeOldest();
+ }
+
+ if (findByKey(key) != mSet->end()) {
+ return false;
+ }
+
+ Entry* newEntry = new Entry(key, value);
+ mSet->insert(newEntry);
+ attachToCache(*newEntry);
+ return true;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::remove(const TKey& key) {
+ typename LruCacheSet::const_iterator find_result = findByKey(key);
+ if (find_result == mSet->end()) {
+ return false;
+ }
+ // All the elements in the set are of type Entry. See comment in the definition
+ // of LruCacheSet above.
+ Entry* entry = reinterpret_cast<Entry*>(*find_result);
+ mSet->erase(entry);
+ if (mListener) {
+ (*mListener)(entry->key, entry->value);
+ }
+ detachFromCache(*entry);
+ delete entry;
+ return true;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::removeOldest() {
+ if (mOldest != NULL) {
+ return remove(mOldest->key);
+ // TODO: should probably abort if false
+ }
+ return false;
+}
+
+template <typename TKey, typename TValue>
+const TValue& LruCache<TKey, TValue>::peekOldestValue() {
+ if (mOldest) {
+ return mOldest->value;
+ }
+ return mNullValue;
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::clear() {
+ if (mListener) {
+ for (Entry* p = mOldest; p != NULL; p = p->child) {
+ (*mListener)(p->key, p->value);
+ }
+ }
+ mYoungest = NULL;
+ mOldest = NULL;
+ for (auto entry : *mSet.get()) {
+ delete entry;
+ }
+ mSet->clear();
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
+ if (mYoungest == NULL) {
+ mYoungest = mOldest = &entry;
+ } else {
+ entry.parent = mYoungest;
+ mYoungest->child = &entry;
+ mYoungest = &entry;
+ }
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
+ if (entry.parent != NULL) {
+ entry.parent->child = entry.child;
+ } else {
+ mOldest = entry.child;
+ }
+ if (entry.child != NULL) {
+ entry.child->parent = entry.parent;
+ } else {
+ mYoungest = entry.parent;
+ }
+
+ entry.parent = NULL;
+ entry.child = NULL;
+}
+
+}
+#endif // ANDROID_UTILS_LRU_CACHE_H
diff --git a/libutils/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
new file mode 100644
index 0000000..d106185
--- /dev/null
+++ b/libutils/include/utils/Mutex.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_MUTEX_H
+#define _LIBS_UTILS_MUTEX_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class Condition;
+
+/*
+ * NOTE: This class is for code that builds on Win32. Its usage is
+ * deprecated for code which doesn't build for Win32. New code which
+ * doesn't build for Win32 should use std::mutex and std::lock_guard instead.
+ *
+ * Simple mutex class. The implementation is system-dependent.
+ *
+ * The mutex must be unlocked by the thread that locked it. They are not
+ * recursive, i.e. the same thread can't lock it multiple times.
+ */
+class Mutex {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ Mutex();
+ explicit Mutex(const char* name);
+ explicit Mutex(int type, const char* name = NULL);
+ ~Mutex();
+
+ // lock or unlock the mutex
+ status_t lock();
+ void unlock();
+
+ // lock if possible; returns 0 on success, error otherwise
+ status_t tryLock();
+
+#if defined(__ANDROID__)
+ // Lock the mutex, but don't wait longer than timeoutNs (relative time).
+ // Returns 0 on success, TIMED_OUT for failure due to timeout expiration.
+ //
+ // OSX doesn't have pthread_mutex_timedlock() or equivalent. To keep
+ // capabilities consistent across host OSes, this method is only available
+ // when building Android binaries.
+ //
+ // FIXME?: pthread_mutex_timedlock is based on CLOCK_REALTIME,
+ // which is subject to NTP adjustments, and includes time during suspend,
+ // so a timeout may occur even though no processes could run.
+ // Not holding a partial wakelock may lead to a system suspend.
+ status_t timedLock(nsecs_t timeoutNs);
+#endif
+
+ // Manages the mutex automatically. It'll be locked when Autolock is
+ // constructed and released when Autolock goes out of scope.
+ class Autolock {
+ public:
+ inline explicit Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
+ inline explicit Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
+ inline ~Autolock() { mLock.unlock(); }
+ private:
+ Mutex& mLock;
+ };
+
+private:
+ friend class Condition;
+
+ // A mutex cannot be copied
+ Mutex(const Mutex&);
+ Mutex& operator = (const Mutex&);
+
+#if !defined(_WIN32)
+ pthread_mutex_t mMutex;
+#else
+ void _init();
+ void* mState;
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+#if !defined(_WIN32)
+
+inline Mutex::Mutex() {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(__attribute__((unused)) const char* name) {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
+ if (type == SHARED) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&mMutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+ } else {
+ pthread_mutex_init(&mMutex, NULL);
+ }
+}
+inline Mutex::~Mutex() {
+ pthread_mutex_destroy(&mMutex);
+}
+inline status_t Mutex::lock() {
+ return -pthread_mutex_lock(&mMutex);
+}
+inline void Mutex::unlock() {
+ pthread_mutex_unlock(&mMutex);
+}
+inline status_t Mutex::tryLock() {
+ return -pthread_mutex_trylock(&mMutex);
+}
+#if defined(__ANDROID__)
+inline status_t Mutex::timedLock(nsecs_t timeoutNs) {
+ timeoutNs += systemTime(SYSTEM_TIME_REALTIME);
+ const struct timespec ts = {
+ /* .tv_sec = */ static_cast<time_t>(timeoutNs / 1000000000),
+ /* .tv_nsec = */ static_cast<long>(timeoutNs % 1000000000),
+ };
+ return -pthread_mutex_timedlock(&mMutex, &ts);
+}
+#endif
+
+#endif // !defined(_WIN32)
+
+// ---------------------------------------------------------------------------
+
+/*
+ * Automatic mutex. Declare one of these at the top of a function.
+ * When the function returns, it will go out of scope, and release the
+ * mutex.
+ */
+
+typedef Mutex::Autolock AutoMutex;
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_MUTEX_H
diff --git a/include/utils/NativeHandle.h b/libutils/include/utils/NativeHandle.h
similarity index 100%
rename from include/utils/NativeHandle.h
rename to libutils/include/utils/NativeHandle.h
diff --git a/include/utils/Printer.h b/libutils/include/utils/Printer.h
similarity index 100%
rename from include/utils/Printer.h
rename to libutils/include/utils/Printer.h
diff --git a/include/utils/ProcessCallStack.h b/libutils/include/utils/ProcessCallStack.h
similarity index 100%
rename from include/utils/ProcessCallStack.h
rename to libutils/include/utils/ProcessCallStack.h
diff --git a/include/utils/PropertyMap.h b/libutils/include/utils/PropertyMap.h
similarity index 100%
rename from include/utils/PropertyMap.h
rename to libutils/include/utils/PropertyMap.h
diff --git a/libutils/include/utils/RWLock.h b/libutils/include/utils/RWLock.h
new file mode 100644
index 0000000..d5b81d3
--- /dev/null
+++ b/libutils/include/utils/RWLock.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_RWLOCK_H
+#define _LIBS_UTILS_RWLOCK_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+#include <utils/ThreadDefs.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+#if !defined(_WIN32)
+
+/*
+ * Simple mutex class. The implementation is system-dependent.
+ *
+ * The mutex must be unlocked by the thread that locked it. They are not
+ * recursive, i.e. the same thread can't lock it multiple times.
+ */
+class RWLock {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ RWLock();
+ explicit RWLock(const char* name);
+ explicit RWLock(int type, const char* name = NULL);
+ ~RWLock();
+
+ status_t readLock();
+ status_t tryReadLock();
+ status_t writeLock();
+ status_t tryWriteLock();
+ void unlock();
+
+ class AutoRLock {
+ public:
+ inline explicit AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); }
+ inline ~AutoRLock() { mLock.unlock(); }
+ private:
+ RWLock& mLock;
+ };
+
+ class AutoWLock {
+ public:
+ inline explicit AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); }
+ inline ~AutoWLock() { mLock.unlock(); }
+ private:
+ RWLock& mLock;
+ };
+
+private:
+ // A RWLock cannot be copied
+ RWLock(const RWLock&);
+ RWLock& operator = (const RWLock&);
+
+ pthread_rwlock_t mRWLock;
+};
+
+inline RWLock::RWLock() {
+ pthread_rwlock_init(&mRWLock, NULL);
+}
+inline RWLock::RWLock(__attribute__((unused)) const char* name) {
+ pthread_rwlock_init(&mRWLock, NULL);
+}
+inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) {
+ if (type == SHARED) {
+ pthread_rwlockattr_t attr;
+ pthread_rwlockattr_init(&attr);
+ pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_rwlock_init(&mRWLock, &attr);
+ pthread_rwlockattr_destroy(&attr);
+ } else {
+ pthread_rwlock_init(&mRWLock, NULL);
+ }
+}
+inline RWLock::~RWLock() {
+ pthread_rwlock_destroy(&mRWLock);
+}
+inline status_t RWLock::readLock() {
+ return -pthread_rwlock_rdlock(&mRWLock);
+}
+inline status_t RWLock::tryReadLock() {
+ return -pthread_rwlock_tryrdlock(&mRWLock);
+}
+inline status_t RWLock::writeLock() {
+ return -pthread_rwlock_wrlock(&mRWLock);
+}
+inline status_t RWLock::tryWriteLock() {
+ return -pthread_rwlock_trywrlock(&mRWLock);
+}
+inline void RWLock::unlock() {
+ pthread_rwlock_unlock(&mRWLock);
+}
+
+#endif // !defined(_WIN32)
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_RWLOCK_H
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
new file mode 100644
index 0000000..36016cd
--- /dev/null
+++ b/libutils/include/utils/RefBase.h
@@ -0,0 +1,731 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// SOME COMMENTS ABOUT USAGE:
+
+// This provides primarily wp<> weak pointer types and RefBase, which work
+// together with sp<> from <StrongPointer.h>.
+
+// sp<> (and wp<>) are a type of smart pointer that use a well defined protocol
+// to operate. As long as the object they are templated with implements that
+// protocol, these smart pointers work. In several places the platform
+// instantiates sp<> with non-RefBase objects; the two are not tied to each
+// other.
+
+// RefBase is such an implementation and it supports strong pointers, weak
+// pointers and some magic features for the binder.
+
+// So, when using RefBase objects, you have the ability to use strong and weak
+// pointers through sp<> and wp<>.
+
+// Normally, when the last strong pointer goes away, the object is destroyed,
+// i.e. it's destructor is called. HOWEVER, parts of its associated memory is not
+// freed until the last weak pointer is released.
+
+// Weak pointers are essentially "safe" pointers. They are always safe to
+// access through promote(). They may return nullptr if the object was
+// destroyed because it ran out of strong pointers. This makes them good candidates
+// for keys in a cache for instance.
+
+// Weak pointers remain valid for comparison purposes even after the underlying
+// object has been destroyed. Even if object A is destroyed and its memory reused
+// for B, A remaining weak pointer to A will not compare equal to one to B.
+// This again makes them attractive for use as keys.
+
+// How is this supposed / intended to be used?
+
+// Our recommendation is to use strong references (sp<>) when there is an
+// ownership relation. e.g. when an object "owns" another one, use a strong
+// ref. And of course use strong refs as arguments of functions (it's extremely
+// rare that a function will take a wp<>).
+
+// Typically a newly allocated object will immediately be used to initialize
+// a strong pointer, which may then be used to construct or assign to other
+// strong and weak pointers.
+
+// Use weak references when there are no ownership relation. e.g. the keys in a
+// cache (you cannot use plain pointers because there is no safe way to acquire
+// a strong reference from a vanilla pointer).
+
+// This implies that two objects should never (or very rarely) have sp<> on
+// each other, because they can't both own each other.
+
+
+// Caveats with reference counting
+
+// Obviously, circular strong references are a big problem; this creates leaks
+// and it's hard to debug -- except it's in fact really easy because RefBase has
+// tons of debugging code for that. It can basically tell you exactly where the
+// leak is.
+
+// Another problem has to do with destructors with side effects. You must
+// assume that the destructor of reference counted objects can be called AT ANY
+// TIME. For instance code as simple as this:
+
+// void setStuff(const sp<Stuff>& stuff) {
+// std::lock_guard<std::mutex> lock(mMutex);
+// mStuff = stuff;
+// }
+
+// is very dangerous. This code WILL deadlock one day or another.
+
+// What isn't obvious is that ~Stuff() can be called as a result of the
+// assignment. And it gets called with the lock held. First of all, the lock is
+// protecting mStuff, not ~Stuff(). Secondly, if ~Stuff() uses its own internal
+// mutex, now you have mutex ordering issues. Even worse, if ~Stuff() is
+// virtual, now you're calling into "user" code (potentially), by that, I mean,
+// code you didn't even write.
+
+// A correct way to write this code is something like:
+
+// void setStuff(const sp<Stuff>& stuff) {
+// std::unique_lock<std::mutex> lock(mMutex);
+// sp<Stuff> hold = mStuff;
+// mStuff = stuff;
+// lock.unlock();
+// }
+
+// More importantly, reference counted objects should do as little work as
+// possible in their destructor, or at least be mindful that their destructor
+// could be called from very weird and unintended places.
+
+// Other more specific restrictions for wp<> and sp<>:
+
+// Do not construct a strong pointer to "this" in an object's constructor.
+// The onFirstRef() callback would be made on an incompletely constructed
+// object.
+// Construction of a weak pointer to "this" in an object's constructor is also
+// discouraged. But the implementation was recently changed so that, in the
+// absence of extendObjectLifetime() calls, weak pointers no longer impact
+// object lifetime, and hence this no longer risks premature deallocation,
+// and hence usually works correctly.
+
+// Such strong or weak pointers can be safely created in the RefBase onFirstRef()
+// callback.
+
+// Use of wp::unsafe_get() for any purpose other than debugging is almost
+// always wrong. Unless you somehow know that there is a longer-lived sp<> to
+// the same object, it may well return a pointer to a deallocated object that
+// has since been reallocated for a different purpose. (And if you know there
+// is a longer-lived sp<>, why not use an sp<> directly?) A wp<> should only be
+// dereferenced by using promote().
+
+// Any object inheriting from RefBase should always be destroyed as the result
+// of a reference count decrement, not via any other means. Such objects
+// should never be stack allocated, or appear directly as data members in other
+// objects. Objects inheriting from RefBase should have their strong reference
+// count incremented as soon as possible after construction. Usually this
+// will be done via construction of an sp<> to the object, but may instead
+// involve other means of calling RefBase::incStrong().
+// Explicitly deleting or otherwise destroying a RefBase object with outstanding
+// wp<> or sp<> pointers to it will result in an abort or heap corruption.
+
+// It is particularly important not to mix sp<> and direct storage management
+// since the sp from raw pointer constructor is implicit. Thus if a RefBase-
+// -derived object of type T is managed without ever incrementing its strong
+// count, and accidentally passed to f(sp<T>), a strong pointer to the object
+// will be temporarily constructed and destroyed, prematurely deallocating the
+// object, and resulting in heap corruption. None of this would be easily
+// visible in the source.
+
+// Extra Features:
+
+// RefBase::extendObjectLifetime() can be used to prevent destruction of the
+// object while there are still weak references. This is really special purpose
+// functionality to support Binder.
+
+// Wp::promote(), implemented via the attemptIncStrong() member function, is
+// used to try to convert a weak pointer back to a strong pointer. It's the
+// normal way to try to access the fields of an object referenced only through
+// a wp<>. Binder code also sometimes uses attemptIncStrong() directly.
+
+// RefBase provides a number of additional callbacks for certain reference count
+// events, as well as some debugging facilities.
+
+// Debugging support can be enabled by turning on DEBUG_REFS in RefBase.cpp.
+// Otherwise little checking is provided.
+
+// Thread safety:
+
+// Like std::shared_ptr, sp<> and wp<> allow concurrent accesses to DIFFERENT
+// sp<> and wp<> instances that happen to refer to the same underlying object.
+// They do NOT support concurrent access (where at least one access is a write)
+// to THE SAME sp<> or wp<>. In effect, their thread-safety properties are
+// exactly like those of T*, NOT atomic<T*>.
+
+#ifndef ANDROID_REF_BASE_H
+#define ANDROID_REF_BASE_H
+
+#include <atomic>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class TextOutput;
+TextOutput& printWeakPointer(TextOutput& to, const void* val);
+
+// ---------------------------------------------------------------------------
+
+#define COMPARE_WEAK(_op_) \
+inline bool operator _op_ (const sp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+} \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return m_ptr _op_ o; \
+}
+
+// ---------------------------------------------------------------------------
+
+// RefererenceRenamer is pure abstract, there is no virtual method
+// implementation to put in a translation unit in order to silence the
+// weak vtables warning.
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+class ReferenceRenamer {
+protected:
+ // destructor is purposedly not virtual so we avoid code overhead from
+ // subclasses; we have to make it protected to guarantee that it
+ // cannot be called from this base class (and to make strict compilers
+ // happy).
+ ~ReferenceRenamer() { }
+public:
+ virtual void operator()(size_t i) const = 0;
+};
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+// ---------------------------------------------------------------------------
+
+class RefBase
+{
+public:
+ void incStrong(const void* id) const;
+ void decStrong(const void* id) const;
+
+ void forceIncStrong(const void* id) const;
+
+ //! DEBUGGING ONLY: Get current strong ref count.
+ int32_t getStrongCount() const;
+
+ class weakref_type
+ {
+ public:
+ RefBase* refBase() const;
+
+ void incWeak(const void* id);
+ void decWeak(const void* id);
+
+ // acquires a strong reference if there is already one.
+ bool attemptIncStrong(const void* id);
+
+ // acquires a weak reference if there is already one.
+ // This is not always safe. see ProcessState.cpp and BpBinder.cpp
+ // for proper use.
+ bool attemptIncWeak(const void* id);
+
+ //! DEBUGGING ONLY: Get current weak ref count.
+ int32_t getWeakCount() const;
+
+ //! DEBUGGING ONLY: Print references held on object.
+ void printRefs() const;
+
+ //! DEBUGGING ONLY: Enable tracking for this object.
+ // enable -- enable/disable tracking
+ // retain -- when tracking is enable, if true, then we save a stack trace
+ // for each reference and dereference; when retain == false, we
+ // match up references and dereferences and keep only the
+ // outstanding ones.
+
+ void trackMe(bool enable, bool retain);
+ };
+
+ weakref_type* createWeak(const void* id) const;
+
+ weakref_type* getWeakRefs() const;
+
+ //! DEBUGGING ONLY: Print references held on object.
+ inline void printRefs() const { getWeakRefs()->printRefs(); }
+
+ //! DEBUGGING ONLY: Enable tracking of object.
+ inline void trackMe(bool enable, bool retain)
+ {
+ getWeakRefs()->trackMe(enable, retain);
+ }
+
+ typedef RefBase basetype;
+
+protected:
+ RefBase();
+ virtual ~RefBase();
+
+ //! Flags for extendObjectLifetime()
+ enum {
+ OBJECT_LIFETIME_STRONG = 0x0000,
+ OBJECT_LIFETIME_WEAK = 0x0001,
+ OBJECT_LIFETIME_MASK = 0x0001
+ };
+
+ void extendObjectLifetime(int32_t mode);
+
+ //! Flags for onIncStrongAttempted()
+ enum {
+ FIRST_INC_STRONG = 0x0001
+ };
+
+ // Invoked after creation of initial strong pointer/reference.
+ virtual void onFirstRef();
+ // Invoked when either the last strong reference goes away, or we need to undo
+ // the effect of an unnecessary onIncStrongAttempted.
+ virtual void onLastStrongRef(const void* id);
+ // Only called in OBJECT_LIFETIME_WEAK case. Returns true if OK to promote to
+ // strong reference. May have side effects if it returns true.
+ // The first flags argument is always FIRST_INC_STRONG.
+ // TODO: Remove initial flag argument.
+ virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
+ // Invoked in the OBJECT_LIFETIME_WEAK case when the last reference of either
+ // kind goes away. Unused.
+ // TODO: Remove.
+ virtual void onLastWeakRef(const void* id);
+
+private:
+ friend class weakref_type;
+ class weakref_impl;
+
+ RefBase(const RefBase& o);
+ RefBase& operator=(const RefBase& o);
+
+private:
+ friend class ReferenceMover;
+
+ static void renameRefs(size_t n, const ReferenceRenamer& renamer);
+
+ static void renameRefId(weakref_type* ref,
+ const void* old_id, const void* new_id);
+
+ static void renameRefId(RefBase* ref,
+ const void* old_id, const void* new_id);
+
+ weakref_impl* const mRefs;
+};
+
+// ---------------------------------------------------------------------------
+
+template <class T>
+class LightRefBase
+{
+public:
+ inline LightRefBase() : mCount(0) { }
+ inline void incStrong(__attribute__((unused)) const void* id) const {
+ mCount.fetch_add(1, std::memory_order_relaxed);
+ }
+ inline void decStrong(__attribute__((unused)) const void* id) const {
+ if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
+ std::atomic_thread_fence(std::memory_order_acquire);
+ delete static_cast<const T*>(this);
+ }
+ }
+ //! DEBUGGING ONLY: Get current strong ref count.
+ inline int32_t getStrongCount() const {
+ return mCount.load(std::memory_order_relaxed);
+ }
+
+ typedef LightRefBase<T> basetype;
+
+protected:
+ inline ~LightRefBase() { }
+
+private:
+ friend class ReferenceMover;
+ inline static void renameRefs(size_t /*n*/,
+ const ReferenceRenamer& /*renamer*/) { }
+ inline static void renameRefId(T* /*ref*/,
+ const void* /*old_id*/ , const void* /*new_id*/) { }
+
+private:
+ mutable std::atomic<int32_t> mCount;
+};
+
+// This is a wrapper around LightRefBase that simply enforces a virtual
+// destructor to eliminate the template requirement of LightRefBase
+class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
+public:
+ virtual ~VirtualLightRefBase();
+};
+
+// ---------------------------------------------------------------------------
+
+template <typename T>
+class wp
+{
+public:
+ typedef typename RefBase::weakref_type weakref_type;
+
+ inline wp() : m_ptr(0) { }
+
+ wp(T* other); // NOLINT(implicit)
+ wp(const wp<T>& other);
+ explicit wp(const sp<T>& other);
+ template<typename U> wp(U* other); // NOLINT(implicit)
+ template<typename U> wp(const sp<U>& other); // NOLINT(implicit)
+ template<typename U> wp(const wp<U>& other); // NOLINT(implicit)
+
+ ~wp();
+
+ // Assignment
+
+ wp& operator = (T* other);
+ wp& operator = (const wp<T>& other);
+ wp& operator = (const sp<T>& other);
+
+ template<typename U> wp& operator = (U* other);
+ template<typename U> wp& operator = (const wp<U>& other);
+ template<typename U> wp& operator = (const sp<U>& other);
+
+ void set_object_and_refs(T* other, weakref_type* refs);
+
+ // promotion to sp
+
+ sp<T> promote() const;
+
+ // Reset
+
+ void clear();
+
+ // Accessors
+
+ inline weakref_type* get_refs() const { return m_refs; }
+
+ inline T* unsafe_get() const { return m_ptr; }
+
+ // Operators
+
+ COMPARE_WEAK(==)
+ COMPARE_WEAK(!=)
+ COMPARE_WEAK(>)
+ COMPARE_WEAK(<)
+ COMPARE_WEAK(<=)
+ COMPARE_WEAK(>=)
+
+ inline bool operator == (const wp<T>& o) const {
+ return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
+ }
+ template<typename U>
+ inline bool operator == (const wp<U>& o) const {
+ return m_ptr == o.m_ptr;
+ }
+
+ inline bool operator > (const wp<T>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ }
+ template<typename U>
+ inline bool operator > (const wp<U>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ }
+
+ inline bool operator < (const wp<T>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+ }
+ template<typename U>
+ inline bool operator < (const wp<U>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+ }
+ inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
+ template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
+ inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
+ template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
+ inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
+ template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
+
+private:
+ template<typename Y> friend class sp;
+ template<typename Y> friend class wp;
+
+ T* m_ptr;
+ weakref_type* m_refs;
+};
+
+template <typename T>
+TextOutput& operator<<(TextOutput& to, const wp<T>& val);
+
+#undef COMPARE_WEAK
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts below here.
+
+template<typename T>
+wp<T>::wp(T* other)
+ : m_ptr(other)
+{
+ if (other) m_refs = other->createWeak(this);
+}
+
+template<typename T>
+wp<T>::wp(const wp<T>& other)
+ : m_ptr(other.m_ptr), m_refs(other.m_refs)
+{
+ if (m_ptr) m_refs->incWeak(this);
+}
+
+template<typename T>
+wp<T>::wp(const sp<T>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) {
+ m_refs = m_ptr->createWeak(this);
+ }
+}
+
+template<typename T> template<typename U>
+wp<T>::wp(U* other)
+ : m_ptr(other)
+{
+ if (other) m_refs = other->createWeak(this);
+}
+
+template<typename T> template<typename U>
+wp<T>::wp(const wp<U>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) {
+ m_refs = other.m_refs;
+ m_refs->incWeak(this);
+ }
+}
+
+template<typename T> template<typename U>
+wp<T>::wp(const sp<U>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) {
+ m_refs = m_ptr->createWeak(this);
+ }
+}
+
+template<typename T>
+wp<T>::~wp()
+{
+ if (m_ptr) m_refs->decWeak(this);
+}
+
+template<typename T>
+wp<T>& wp<T>::operator = (T* other)
+{
+ weakref_type* newRefs =
+ other ? other->createWeak(this) : 0;
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T>
+wp<T>& wp<T>::operator = (const wp<T>& other)
+{
+ weakref_type* otherRefs(other.m_refs);
+ T* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
+ return *this;
+}
+
+template<typename T>
+wp<T>& wp<T>::operator = (const sp<T>& other)
+{
+ weakref_type* newRefs =
+ other != NULL ? other->createWeak(this) : 0;
+ T* otherPtr(other.m_ptr);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (U* other)
+{
+ weakref_type* newRefs =
+ other ? other->createWeak(this) : 0;
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (const wp<U>& other)
+{
+ weakref_type* otherRefs(other.m_refs);
+ U* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (const sp<U>& other)
+{
+ weakref_type* newRefs =
+ other != NULL ? other->createWeak(this) : 0;
+ U* otherPtr(other.m_ptr);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T>
+void wp<T>::set_object_and_refs(T* other, weakref_type* refs)
+{
+ if (other) refs->incWeak(this);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = refs;
+}
+
+template<typename T>
+sp<T> wp<T>::promote() const
+{
+ sp<T> result;
+ if (m_ptr && m_refs->attemptIncStrong(&result)) {
+ result.set_pointer(m_ptr);
+ }
+ return result;
+}
+
+template<typename T>
+void wp<T>::clear()
+{
+ if (m_ptr) {
+ m_refs->decWeak(this);
+ m_ptr = 0;
+ }
+}
+
+template <typename T>
+inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
+{
+ return printWeakPointer(to, val.unsafe_get());
+}
+
+// ---------------------------------------------------------------------------
+
+// this class just serves as a namespace so TYPE::moveReferences can stay
+// private.
+class ReferenceMover {
+public:
+ // it would be nice if we could make sure no extra code is generated
+ // for sp<TYPE> or wp<TYPE> when TYPE is a descendant of RefBase:
+ // Using a sp<RefBase> override doesn't work; it's a bit like we wanted
+ // a template<typename TYPE inherits RefBase> template...
+
+ template<typename TYPE> static inline
+ void move_references(sp<TYPE>* dest, sp<TYPE> const* src, size_t n) {
+
+ class Renamer : public ReferenceRenamer {
+ sp<TYPE>* d_;
+ sp<TYPE> const* s_;
+ virtual void operator()(size_t i) const {
+ // The id are known to be the sp<>'s this pointer
+ TYPE::renameRefId(d_[i].get(), &s_[i], &d_[i]);
+ }
+ public:
+ Renamer(sp<TYPE>* d, sp<TYPE> const* s) : d_(d), s_(s) { }
+ virtual ~Renamer() { }
+ };
+
+ memmove(dest, src, n*sizeof(sp<TYPE>));
+ TYPE::renameRefs(n, Renamer(dest, src));
+ }
+
+
+ template<typename TYPE> static inline
+ void move_references(wp<TYPE>* dest, wp<TYPE> const* src, size_t n) {
+
+ class Renamer : public ReferenceRenamer {
+ wp<TYPE>* d_;
+ wp<TYPE> const* s_;
+ virtual void operator()(size_t i) const {
+ // The id are known to be the wp<>'s this pointer
+ TYPE::renameRefId(d_[i].get_refs(), &s_[i], &d_[i]);
+ }
+ public:
+ Renamer(wp<TYPE>* rd, wp<TYPE> const* rs) : d_(rd), s_(rs) { }
+ virtual ~Renamer() { }
+ };
+
+ memmove(dest, src, n*sizeof(wp<TYPE>));
+ TYPE::renameRefs(n, Renamer(dest, src));
+ }
+};
+
+// specialization for moving sp<> and wp<> types.
+// these are used by the [Sorted|Keyed]Vector<> implementations
+// sp<> and wp<> need to be handled specially, because they do not
+// have trivial copy operation in the general case (see RefBase.cpp
+// when DEBUG ops are enabled), but can be implemented very
+// efficiently in most cases.
+
+template<typename TYPE> inline
+void move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_REF_BASE_H
diff --git a/libutils/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
new file mode 100644
index 0000000..7cc4c18
--- /dev/null
+++ b/libutils/include/utils/Singleton.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_SINGLETON_H
+#define ANDROID_UTILS_SINGLETON_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+#include <cutils/compiler.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+template <typename TYPE>
+class ANDROID_API Singleton
+{
+public:
+ static TYPE& getInstance() {
+ Mutex::Autolock _l(sLock);
+ TYPE* instance = sInstance;
+ if (instance == 0) {
+ instance = new TYPE();
+ sInstance = instance;
+ }
+ return *instance;
+ }
+
+ static bool hasInstance() {
+ Mutex::Autolock _l(sLock);
+ return sInstance != 0;
+ }
+
+protected:
+ ~Singleton() { }
+ Singleton() { }
+
+private:
+ Singleton(const Singleton&);
+ Singleton& operator = (const Singleton&);
+ static Mutex sLock;
+ static TYPE* sInstance;
+};
+
+template <typename TYPE>
+Mutex Singleton<TYPE>::sLock;
+
+template <typename TYPE>
+TYPE* Singleton<TYPE>::sInstance;
+
+/*
+ * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file
+ * (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes,
+ * and avoid to have a copy of them in each compilation units Singleton<TYPE>
+ * is used.
+ * NOTE: we use a version of Mutex ctor that takes a parameter, because
+ * for some unknown reason using the default ctor doesn't emit the variable!
+ */
+
+#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \
+ template<> ::android::Mutex \
+ (::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE); \
+ template<> TYPE* ::android::Singleton< TYPE >::sInstance(0); \
+ template class ::android::Singleton< TYPE >;
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_UTILS_SINGLETON_H
+
diff --git a/libutils/include/utils/SortedVector.h b/libutils/include/utils/SortedVector.h
new file mode 100644
index 0000000..86f3496
--- /dev/null
+++ b/libutils/include/utils/SortedVector.h
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_SORTED_VECTOR_H
+#define ANDROID_SORTED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <utils/TypeHelpers.h>
+#include <utils/Vector.h>
+#include <utils/VectorImpl.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+template <class TYPE>
+class SortedVector : private SortedVectorImpl
+{
+ friend class Vector<TYPE>;
+
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ SortedVector();
+ SortedVector(const SortedVector<TYPE>& rhs);
+ virtual ~SortedVector();
+
+ /*! copy operator */
+ const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
+ SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns whether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! sets the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+
+ //! read-write C-style access. BE VERY CAREFUL when modifying the array
+ //! you must keep it sorted! You usually don't use this function.
+ TYPE* editArray();
+
+ //! finds the index of an item
+ ssize_t indexOf(const TYPE& item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const TYPE& item) const;
+
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+
+ /*!
+ * modifying the array
+ */
+
+ //! add an item in the right place (and replace the one that is there)
+ ssize_t add(const TYPE& item);
+
+ //! editItemAt() MUST NOT change the order of this item
+ TYPE& editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
+ }
+
+ //! merges a vector into this one
+ ssize_t merge(const Vector<TYPE>& vector);
+ ssize_t merge(const SortedVector<TYPE>& vector);
+
+ //! removes an item
+ ssize_t remove(const TYPE&);
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+ virtual int do_compare(const void* lhs, const void* rhs) const;
+};
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector()
+ : SortedVectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
+ : SortedVectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::~SortedVector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+ SortedVectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
+ SortedVectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* SortedVector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* SortedVector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
+ LOG_FATAL_IF(index>=size(),
+ "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__,
+ int(index), int(size()));
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::add(const TYPE& item) {
+ return SortedVectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
+ return SortedVectorImpl::indexOf(&item);
+}
+
+template<class TYPE> inline
+size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
+ return SortedVectorImpl::orderOf(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
+ return SortedVectorImpl::remove(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
+ return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
+}
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SORTED_VECTOR_H
diff --git a/include/utils/StopWatch.h b/libutils/include/utils/StopWatch.h
similarity index 100%
rename from include/utils/StopWatch.h
rename to libutils/include/utils/StopWatch.h
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
new file mode 100644
index 0000000..07c4de7
--- /dev/null
+++ b/libutils/include/utils/String16.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_STRING16_H
+#define ANDROID_STRING16_H
+
+#include <string> // for std::string
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+
+extern "C" {
+
+}
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class SharedBuffer;
+class String8;
+class TextOutput;
+
+//! This is a string holding UTF-16 characters.
+class String16
+{
+public:
+ /* use String16(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String16, e.g.:
+ *
+ * static String16 sAStaticEmptyString(String16::kEmptyString);
+ * static String16 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
+ String16();
+ explicit String16(StaticLinkage);
+ String16(const String16& o);
+ String16(const String16& o,
+ size_t len,
+ size_t begin=0);
+ explicit String16(const char16_t* o);
+ explicit String16(const char16_t* o, size_t len);
+ explicit String16(const String8& o);
+ explicit String16(const char* o);
+ explicit String16(const char* o, size_t len);
+
+ ~String16();
+
+ inline const char16_t* string() const;
+
+ static inline std::string std_string(const String16& str);
+ size_t size() const;
+ void setTo(const String16& other);
+ status_t setTo(const char16_t* other);
+ status_t setTo(const char16_t* other, size_t len);
+ status_t setTo(const String16& other,
+ size_t len,
+ size_t begin=0);
+
+ status_t append(const String16& other);
+ status_t append(const char16_t* other, size_t len);
+
+ inline String16& operator=(const String16& other);
+
+ inline String16& operator+=(const String16& other);
+ inline String16 operator+(const String16& other) const;
+
+ status_t insert(size_t pos, const char16_t* chrs);
+ status_t insert(size_t pos,
+ const char16_t* chrs, size_t len);
+
+ ssize_t findFirst(char16_t c) const;
+ ssize_t findLast(char16_t c) const;
+
+ bool startsWith(const String16& prefix) const;
+ bool startsWith(const char16_t* prefix) const;
+
+ bool contains(const char16_t* chrs) const;
+
+ status_t makeLower();
+
+ status_t replaceAll(char16_t replaceThis,
+ char16_t withThis);
+
+ status_t remove(size_t len, size_t begin=0);
+
+ inline int compare(const String16& other) const;
+
+ inline bool operator<(const String16& other) const;
+ inline bool operator<=(const String16& other) const;
+ inline bool operator==(const String16& other) const;
+ inline bool operator!=(const String16& other) const;
+ inline bool operator>=(const String16& other) const;
+ inline bool operator>(const String16& other) const;
+
+ inline bool operator<(const char16_t* other) const;
+ inline bool operator<=(const char16_t* other) const;
+ inline bool operator==(const char16_t* other) const;
+ inline bool operator!=(const char16_t* other) const;
+ inline bool operator>=(const char16_t* other) const;
+ inline bool operator>(const char16_t* other) const;
+
+ inline operator const char16_t*() const;
+
+private:
+ const char16_t* mString;
+};
+
+// String16 can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+ANDROID_TRIVIAL_MOVE_TRAIT(String16)
+
+// ---------------------------------------------------------------------------
+// No user servicable parts below.
+
+inline int compare_type(const String16& lhs, const String16& rhs)
+{
+ return lhs.compare(rhs);
+}
+
+inline int strictly_order_type(const String16& lhs, const String16& rhs)
+{
+ return compare_type(lhs, rhs) < 0;
+}
+
+inline const char16_t* String16::string() const
+{
+ return mString;
+}
+
+inline std::string String16::std_string(const String16& str)
+{
+ return std::string(String8(str).string());
+}
+
+inline String16& String16::operator=(const String16& other)
+{
+ setTo(other);
+ return *this;
+}
+
+inline String16& String16::operator+=(const String16& other)
+{
+ append(other);
+ return *this;
+}
+
+inline String16 String16::operator+(const String16& other) const
+{
+ String16 tmp(*this);
+ tmp += other;
+ return tmp;
+}
+
+inline int String16::compare(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size());
+}
+
+inline bool String16::operator<(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) < 0;
+}
+
+inline bool String16::operator<=(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) <= 0;
+}
+
+inline bool String16::operator==(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) == 0;
+}
+
+inline bool String16::operator!=(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) != 0;
+}
+
+inline bool String16::operator>=(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) >= 0;
+}
+
+inline bool String16::operator>(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) > 0;
+}
+
+inline bool String16::operator<(const char16_t* other) const
+{
+ return strcmp16(mString, other) < 0;
+}
+
+inline bool String16::operator<=(const char16_t* other) const
+{
+ return strcmp16(mString, other) <= 0;
+}
+
+inline bool String16::operator==(const char16_t* other) const
+{
+ return strcmp16(mString, other) == 0;
+}
+
+inline bool String16::operator!=(const char16_t* other) const
+{
+ return strcmp16(mString, other) != 0;
+}
+
+inline bool String16::operator>=(const char16_t* other) const
+{
+ return strcmp16(mString, other) >= 0;
+}
+
+inline bool String16::operator>(const char16_t* other) const
+{
+ return strcmp16(mString, other) > 0;
+}
+
+inline String16::operator const char16_t*() const
+{
+ return mString;
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRING16_H
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
new file mode 100644
index 0000000..1d12994
--- /dev/null
+++ b/libutils/include/utils/String8.h
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_STRING8_H
+#define ANDROID_STRING8_H
+
+#include <string> // for std::string
+
+#include <utils/Errors.h>
+#include <utils/Unicode.h>
+#include <utils/TypeHelpers.h>
+
+#include <string.h> // for strcmp
+#include <stdarg.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+class String16;
+class TextOutput;
+
+//! This is a string holding UTF-8 characters. Does not allow the value more
+// than 0x10FFFF, which is not valid unicode codepoint.
+class String8
+{
+public:
+ /* use String8(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String8, e.g.:
+ *
+ * static String8 sAStaticEmptyString(String8::kEmptyString);
+ * static String8 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
+ String8();
+ explicit String8(StaticLinkage);
+ String8(const String8& o);
+ explicit String8(const char* o);
+ explicit String8(const char* o, size_t numChars);
+
+ explicit String8(const String16& o);
+ explicit String8(const char16_t* o);
+ explicit String8(const char16_t* o, size_t numChars);
+ explicit String8(const char32_t* o);
+ explicit String8(const char32_t* o, size_t numChars);
+ ~String8();
+
+ static inline const String8 empty();
+
+ static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
+ static String8 formatV(const char* fmt, va_list args);
+
+ inline const char* string() const;
+ static inline std::string std_string(const String8& str);
+ inline size_t size() const;
+ inline size_t bytes() const;
+ inline bool isEmpty() const;
+
+ size_t length() const;
+
+ void clear();
+
+ void setTo(const String8& other);
+ status_t setTo(const char* other);
+ status_t setTo(const char* other, size_t numChars);
+ status_t setTo(const char16_t* other, size_t numChars);
+ status_t setTo(const char32_t* other,
+ size_t length);
+
+ status_t append(const String8& other);
+ status_t append(const char* other);
+ status_t append(const char* other, size_t numChars);
+
+ status_t appendFormat(const char* fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+ status_t appendFormatV(const char* fmt, va_list args);
+
+ // Note that this function takes O(N) time to calculate the value.
+ // No cache value is stored.
+ size_t getUtf32Length() const;
+ int32_t getUtf32At(size_t index,
+ size_t *next_index) const;
+ void getUtf32(char32_t* dst) const;
+
+ inline String8& operator=(const String8& other);
+ inline String8& operator=(const char* other);
+
+ inline String8& operator+=(const String8& other);
+ inline String8 operator+(const String8& other) const;
+
+ inline String8& operator+=(const char* other);
+ inline String8 operator+(const char* other) const;
+
+ inline int compare(const String8& other) const;
+
+ inline bool operator<(const String8& other) const;
+ inline bool operator<=(const String8& other) const;
+ inline bool operator==(const String8& other) const;
+ inline bool operator!=(const String8& other) const;
+ inline bool operator>=(const String8& other) const;
+ inline bool operator>(const String8& other) const;
+
+ inline bool operator<(const char* other) const;
+ inline bool operator<=(const char* other) const;
+ inline bool operator==(const char* other) const;
+ inline bool operator!=(const char* other) const;
+ inline bool operator>=(const char* other) const;
+ inline bool operator>(const char* other) const;
+
+ inline operator const char*() const;
+
+ char* lockBuffer(size_t size);
+ void unlockBuffer();
+ status_t unlockBuffer(size_t size);
+
+ // return the index of the first byte of other in this at or after
+ // start, or -1 if not found
+ ssize_t find(const char* other, size_t start = 0) const;
+
+ // return true if this string contains the specified substring
+ inline bool contains(const char* other) const;
+
+ // removes all occurrence of the specified substring
+ // returns true if any were found and removed
+ bool removeAll(const char* other);
+
+ void toLower();
+ void toLower(size_t start, size_t numChars);
+ void toUpper();
+ void toUpper(size_t start, size_t numChars);
+
+
+ /*
+ * These methods operate on the string as if it were a path name.
+ */
+
+ /*
+ * Set the filename field to a specific value.
+ *
+ * Normalizes the filename, removing a trailing '/' if present.
+ */
+ void setPathName(const char* name);
+ void setPathName(const char* name, size_t numChars);
+
+ /*
+ * Get just the filename component.
+ *
+ * "/tmp/foo/bar.c" --> "bar.c"
+ */
+ String8 getPathLeaf(void) const;
+
+ /*
+ * Remove the last (file name) component, leaving just the directory
+ * name.
+ *
+ * "/tmp/foo/bar.c" --> "/tmp/foo"
+ * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
+ * "bar.c" --> ""
+ */
+ String8 getPathDir(void) const;
+
+ /*
+ * Retrieve the front (root dir) component. Optionally also return the
+ * remaining components.
+ *
+ * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
+ * "/tmp" --> "tmp" (remain = "")
+ * "bar.c" --> "bar.c" (remain = "")
+ */
+ String8 walkPath(String8* outRemains = NULL) const;
+
+ /*
+ * Return the filename extension. This is the last '.' and any number
+ * of characters that follow it. The '.' is included in case we
+ * decide to expand our definition of what constitutes an extension.
+ *
+ * "/tmp/foo/bar.c" --> ".c"
+ * "/tmp" --> ""
+ * "/tmp/foo.bar/baz" --> ""
+ * "foo.jpeg" --> ".jpeg"
+ * "foo." --> ""
+ */
+ String8 getPathExtension(void) const;
+
+ /*
+ * Return the path without the extension. Rules for what constitutes
+ * an extension are described in the comment for getPathExtension().
+ *
+ * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
+ */
+ String8 getBasePath(void) const;
+
+ /*
+ * Add a component to the pathname. We guarantee that there is
+ * exactly one path separator between the old path and the new.
+ * If there is no existing name, we just copy the new name in.
+ *
+ * If leaf is a fully qualified path (i.e. starts with '/', it
+ * replaces whatever was there before.
+ */
+ String8& appendPath(const char* leaf);
+ String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); }
+
+ /*
+ * Like appendPath(), but does not affect this string. Returns a new one instead.
+ */
+ String8 appendPathCopy(const char* leaf) const
+ { String8 p(*this); p.appendPath(leaf); return p; }
+ String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); }
+
+ /*
+ * Converts all separators in this string to /, the default path separator.
+ *
+ * If the default OS separator is backslash, this converts all
+ * backslashes to slashes, in-place. Otherwise it does nothing.
+ * Returns self.
+ */
+ String8& convertToResPath();
+
+private:
+ status_t real_append(const char* other, size_t numChars);
+ char* find_extension(void) const;
+
+ const char* mString;
+};
+
+// String8 can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+ANDROID_TRIVIAL_MOVE_TRAIT(String8)
+
+// ---------------------------------------------------------------------------
+// No user servicable parts below.
+
+inline int compare_type(const String8& lhs, const String8& rhs)
+{
+ return lhs.compare(rhs);
+}
+
+inline int strictly_order_type(const String8& lhs, const String8& rhs)
+{
+ return compare_type(lhs, rhs) < 0;
+}
+
+inline const String8 String8::empty() {
+ return String8();
+}
+
+inline const char* String8::string() const
+{
+ return mString;
+}
+
+inline std::string String8::std_string(const String8& str)
+{
+ return std::string(str.string());
+}
+
+inline size_t String8::size() const
+{
+ return length();
+}
+
+inline bool String8::isEmpty() const
+{
+ return length() == 0;
+}
+
+inline size_t String8::bytes() const
+{
+ return length();
+}
+
+inline bool String8::contains(const char* other) const
+{
+ return find(other) >= 0;
+}
+
+inline String8& String8::operator=(const String8& other)
+{
+ setTo(other);
+ return *this;
+}
+
+inline String8& String8::operator=(const char* other)
+{
+ setTo(other);
+ return *this;
+}
+
+inline String8& String8::operator+=(const String8& other)
+{
+ append(other);
+ return *this;
+}
+
+inline String8 String8::operator+(const String8& other) const
+{
+ String8 tmp(*this);
+ tmp += other;
+ return tmp;
+}
+
+inline String8& String8::operator+=(const char* other)
+{
+ append(other);
+ return *this;
+}
+
+inline String8 String8::operator+(const char* other) const
+{
+ String8 tmp(*this);
+ tmp += other;
+ return tmp;
+}
+
+inline int String8::compare(const String8& other) const
+{
+ return strcmp(mString, other.mString);
+}
+
+inline bool String8::operator<(const String8& other) const
+{
+ return strcmp(mString, other.mString) < 0;
+}
+
+inline bool String8::operator<=(const String8& other) const
+{
+ return strcmp(mString, other.mString) <= 0;
+}
+
+inline bool String8::operator==(const String8& other) const
+{
+ return strcmp(mString, other.mString) == 0;
+}
+
+inline bool String8::operator!=(const String8& other) const
+{
+ return strcmp(mString, other.mString) != 0;
+}
+
+inline bool String8::operator>=(const String8& other) const
+{
+ return strcmp(mString, other.mString) >= 0;
+}
+
+inline bool String8::operator>(const String8& other) const
+{
+ return strcmp(mString, other.mString) > 0;
+}
+
+inline bool String8::operator<(const char* other) const
+{
+ return strcmp(mString, other) < 0;
+}
+
+inline bool String8::operator<=(const char* other) const
+{
+ return strcmp(mString, other) <= 0;
+}
+
+inline bool String8::operator==(const char* other) const
+{
+ return strcmp(mString, other) == 0;
+}
+
+inline bool String8::operator!=(const char* other) const
+{
+ return strcmp(mString, other) != 0;
+}
+
+inline bool String8::operator>=(const char* other) const
+{
+ return strcmp(mString, other) >= 0;
+}
+
+inline bool String8::operator>(const char* other) const
+{
+ return strcmp(mString, other) > 0;
+}
+
+inline String8::operator const char*() const
+{
+ return mString;
+}
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRING8_H
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
new file mode 100644
index 0000000..294e6b6
--- /dev/null
+++ b/libutils/include/utils/StrongPointer.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_STRONG_POINTER_H
+#define ANDROID_STRONG_POINTER_H
+
+#include <cutils/atomic.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+template<typename T> class wp;
+
+// ---------------------------------------------------------------------------
+
+#define COMPARE(_op_) \
+inline bool operator _op_ (const sp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+} \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return m_ptr _op_ o; \
+} \
+inline bool operator _op_ (const wp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const wp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+}
+
+// ---------------------------------------------------------------------------
+
+template<typename T>
+class sp {
+public:
+ inline sp() : m_ptr(0) { }
+
+ sp(T* other); // NOLINT(implicit)
+ sp(const sp<T>& other);
+ sp(sp<T>&& other);
+ template<typename U> sp(U* other); // NOLINT(implicit)
+ template<typename U> sp(const sp<U>& other); // NOLINT(implicit)
+ template<typename U> sp(sp<U>&& other); // NOLINT(implicit)
+
+ ~sp();
+
+ // Assignment
+
+ sp& operator = (T* other);
+ sp& operator = (const sp<T>& other);
+ sp& operator = (sp<T>&& other);
+
+ template<typename U> sp& operator = (const sp<U>& other);
+ template<typename U> sp& operator = (sp<U>&& other);
+ template<typename U> sp& operator = (U* other);
+
+ //! Special optimization for use by ProcessState (and nobody else).
+ void force_set(T* other);
+
+ // Reset
+
+ void clear();
+
+ // Accessors
+
+ inline T& operator* () const { return *m_ptr; }
+ inline T* operator-> () const { return m_ptr; }
+ inline T* get() const { return m_ptr; }
+
+ // Operators
+
+ COMPARE(==)
+ COMPARE(!=)
+ COMPARE(>)
+ COMPARE(<)
+ COMPARE(<=)
+ COMPARE(>=)
+
+private:
+ template<typename Y> friend class sp;
+ template<typename Y> friend class wp;
+ void set_pointer(T* ptr);
+ T* m_ptr;
+};
+
+#undef COMPARE
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts below here.
+
+template<typename T>
+sp<T>::sp(T* other)
+ : m_ptr(other) {
+ if (other)
+ other->incStrong(this);
+}
+
+template<typename T>
+sp<T>::sp(const sp<T>& other)
+ : m_ptr(other.m_ptr) {
+ if (m_ptr)
+ m_ptr->incStrong(this);
+}
+
+template<typename T>
+sp<T>::sp(sp<T>&& other)
+ : m_ptr(other.m_ptr) {
+ other.m_ptr = nullptr;
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(U* other)
+ : m_ptr(other) {
+ if (other)
+ (static_cast<T*>(other))->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(const sp<U>& other)
+ : m_ptr(other.m_ptr) {
+ if (m_ptr)
+ m_ptr->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(sp<U>&& other)
+ : m_ptr(other.m_ptr) {
+ other.m_ptr = nullptr;
+}
+
+template<typename T>
+sp<T>::~sp() {
+ if (m_ptr)
+ m_ptr->decStrong(this);
+}
+
+template<typename T>
+sp<T>& sp<T>::operator =(const sp<T>& other) {
+ T* otherPtr(other.m_ptr);
+ if (otherPtr)
+ otherPtr->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = otherPtr;
+ return *this;
+}
+
+template<typename T>
+sp<T>& sp<T>::operator =(sp<T>&& other) {
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other.m_ptr;
+ other.m_ptr = nullptr;
+ return *this;
+}
+
+template<typename T>
+sp<T>& sp<T>::operator =(T* other) {
+ if (other)
+ other->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(const sp<U>& other) {
+ T* otherPtr(other.m_ptr);
+ if (otherPtr)
+ otherPtr->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = otherPtr;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(sp<U>&& other) {
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other.m_ptr;
+ other.m_ptr = nullptr;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(U* other) {
+ if (other)
+ (static_cast<T*>(other))->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T>
+void sp<T>::force_set(T* other) {
+ other->forceIncStrong(this);
+ m_ptr = other;
+}
+
+template<typename T>
+void sp<T>::clear() {
+ if (m_ptr) {
+ m_ptr->decStrong(this);
+ m_ptr = 0;
+ }
+}
+
+template<typename T>
+void sp<T>::set_pointer(T* ptr) {
+ m_ptr = ptr;
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRONG_POINTER_H
diff --git a/include/utils/SystemClock.h b/libutils/include/utils/SystemClock.h
similarity index 100%
rename from include/utils/SystemClock.h
rename to libutils/include/utils/SystemClock.h
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
new file mode 100644
index 0000000..a261fc8
--- /dev/null
+++ b/libutils/include/utils/Thread.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_THREAD_H
+#define _LIBS_UTILS_THREAD_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/ThreadDefs.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class Thread : virtual public RefBase
+{
+public:
+ // Create a Thread object, but doesn't create or start the associated
+ // thread. See the run() method.
+ explicit Thread(bool canCallJava = true);
+ virtual ~Thread();
+
+ // Start the thread in threadLoop() which needs to be implemented.
+ virtual status_t run( const char* name,
+ int32_t priority = PRIORITY_DEFAULT,
+ size_t stack = 0);
+
+ // Ask this object's thread to exit. This function is asynchronous, when the
+ // function returns the thread might still be running. Of course, this
+ // function can be called from a different thread.
+ virtual void requestExit();
+
+ // Good place to do one-time initializations
+ virtual status_t readyToRun();
+
+ // Call requestExit() and wait until this object's thread exits.
+ // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call
+ // this function from this object's thread. Will return WOULD_BLOCK in
+ // that case.
+ status_t requestExitAndWait();
+
+ // Wait until this object's thread exits. Returns immediately if not yet running.
+ // Do not call from this object's thread; will return WOULD_BLOCK in that case.
+ status_t join();
+
+ // Indicates whether this thread is running or not.
+ bool isRunning() const;
+
+#if defined(__ANDROID__)
+ // Return the thread's kernel ID, same as the thread itself calling gettid(),
+ // or -1 if the thread is not running.
+ pid_t getTid() const;
+#endif
+
+protected:
+ // exitPending() returns true if requestExit() has been called.
+ bool exitPending() const;
+
+private:
+ // Derived class must implement threadLoop(). The thread starts its life
+ // here. There are two ways of using the Thread object:
+ // 1) loop: if threadLoop() returns true, it will be called again if
+ // requestExit() wasn't called.
+ // 2) once: if threadLoop() returns false, the thread will exit upon return.
+ virtual bool threadLoop() = 0;
+
+private:
+ Thread& operator=(const Thread&);
+ static int _threadLoop(void* user);
+ const bool mCanCallJava;
+ // always hold mLock when reading or writing
+ thread_id_t mThread;
+ mutable Mutex mLock;
+ Condition mThreadExitedCondition;
+ status_t mStatus;
+ // note that all accesses of mExitPending and mRunning need to hold mLock
+ volatile bool mExitPending;
+ volatile bool mRunning;
+ sp<Thread> mHoldSelf;
+#if defined(__ANDROID__)
+ // legacy for debugging, not used by getTid() as it is set by the child thread
+ // and so is not initialized until the child reaches that point
+ pid_t mTid;
+#endif
+};
+
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+#endif // _LIBS_UTILS_THREAD_H
+// ---------------------------------------------------------------------------
diff --git a/libutils/include/utils/ThreadDefs.h b/libutils/include/utils/ThreadDefs.h
new file mode 100644
index 0000000..ae091e4
--- /dev/null
+++ b/libutils/include/utils/ThreadDefs.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_THREAD_DEFS_H
+#define _LIBS_UTILS_THREAD_DEFS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <system/graphics.h>
+#include <system/thread_defs.h>
+
+// ---------------------------------------------------------------------------
+// C API
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+typedef uint32_t android_thread_id_t;
+#else
+typedef void* android_thread_id_t;
+#endif
+
+typedef int (*android_thread_func_t)(void*);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+// ---------------------------------------------------------------------------
+// C++ API
+#ifdef __cplusplus
+namespace android {
+// ---------------------------------------------------------------------------
+
+typedef android_thread_id_t thread_id_t;
+typedef android_thread_func_t thread_func_t;
+
+enum {
+ PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST,
+ PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND,
+ PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL,
+ PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND,
+ PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY,
+ PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY,
+ PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO,
+ PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO,
+ PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST,
+ PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT,
+ PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE,
+ PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE,
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+#endif // __cplusplus
+// ---------------------------------------------------------------------------
+
+
+#endif // _LIBS_UTILS_THREAD_DEFS_H
diff --git a/include/utils/Timers.h b/libutils/include/utils/Timers.h
similarity index 100%
rename from include/utils/Timers.h
rename to libutils/include/utils/Timers.h
diff --git a/include/utils/Tokenizer.h b/libutils/include/utils/Tokenizer.h
similarity index 100%
rename from include/utils/Tokenizer.h
rename to libutils/include/utils/Tokenizer.h
diff --git a/libutils/include/utils/Trace.h b/libutils/include/utils/Trace.h
new file mode 100644
index 0000000..eeba40d
--- /dev/null
+++ b/libutils/include/utils/Trace.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_TRACE_H
+#define ANDROID_TRACE_H
+
+#if defined(__ANDROID__)
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/compiler.h>
+#include <utils/threads.h>
+#include <cutils/trace.h>
+
+// See <cutils/trace.h> for more ATRACE_* macros.
+
+// ATRACE_NAME traces from its location until the end of its enclosing scope.
+#define _PASTE(x, y) x ## y
+#define PASTE(x, y) _PASTE(x,y)
+#define ATRACE_NAME(name) android::ScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
+#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
+
+namespace android {
+
+class ScopedTrace {
+public:
+inline ScopedTrace(uint64_t tag, const char* name)
+ : mTag(tag) {
+ atrace_begin(mTag,name);
+}
+
+inline ~ScopedTrace() {
+ atrace_end(mTag);
+}
+
+private:
+ uint64_t mTag;
+};
+
+}; // namespace android
+
+#else // !__ANDROID__
+
+#define ATRACE_NAME(...)
+#define ATRACE_CALL()
+
+#endif // __ANDROID__
+
+#endif // ANDROID_TRACE_H
diff --git a/libutils/include/utils/TypeHelpers.h b/libutils/include/utils/TypeHelpers.h
new file mode 100644
index 0000000..2a25227
--- /dev/null
+++ b/libutils/include/utils/TypeHelpers.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_TYPE_HELPERS_H
+#define ANDROID_TYPE_HELPERS_H
+
+#include <new>
+#include <type_traits>
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+/*
+ * Types traits
+ */
+
+template <typename T> struct trait_trivial_ctor { enum { value = false }; };
+template <typename T> struct trait_trivial_dtor { enum { value = false }; };
+template <typename T> struct trait_trivial_copy { enum { value = false }; };
+template <typename T> struct trait_trivial_move { enum { value = false }; };
+template <typename T> struct trait_pointer { enum { value = false }; };
+template <typename T> struct trait_pointer<T*> { enum { value = true }; };
+
+template <typename TYPE>
+struct traits {
+ enum {
+ // whether this type is a pointer
+ is_pointer = trait_pointer<TYPE>::value,
+ // whether this type's constructor is a no-op
+ has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
+ // whether this type's destructor is a no-op
+ has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
+ // whether this type type can be copy-constructed with memcpy
+ has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
+ // whether this type can be moved with memmove
+ has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value
+ };
+};
+
+template <typename T, typename U>
+struct aggregate_traits {
+ enum {
+ is_pointer = false,
+ has_trivial_ctor =
+ traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
+ has_trivial_dtor =
+ traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
+ has_trivial_copy =
+ traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
+ has_trivial_move =
+ traits<T>::has_trivial_move && traits<U>::has_trivial_move
+ };
+};
+
+#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
+ template<> struct trait_trivial_ctor< T > { enum { value = true }; };
+
+#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
+ template<> struct trait_trivial_dtor< T > { enum { value = true }; };
+
+#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \
+ template<> struct trait_trivial_copy< T > { enum { value = true }; };
+
+#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \
+ template<> struct trait_trivial_move< T > { enum { value = true }; };
+
+#define ANDROID_BASIC_TYPES_TRAITS( T ) \
+ ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
+ ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
+ ANDROID_TRIVIAL_COPY_TRAIT( T ) \
+ ANDROID_TRIVIAL_MOVE_TRAIT( T )
+
+// ---------------------------------------------------------------------------
+
+/*
+ * basic types traits
+ */
+
+ANDROID_BASIC_TYPES_TRAITS( void )
+ANDROID_BASIC_TYPES_TRAITS( bool )
+ANDROID_BASIC_TYPES_TRAITS( char )
+ANDROID_BASIC_TYPES_TRAITS( unsigned char )
+ANDROID_BASIC_TYPES_TRAITS( short )
+ANDROID_BASIC_TYPES_TRAITS( unsigned short )
+ANDROID_BASIC_TYPES_TRAITS( int )
+ANDROID_BASIC_TYPES_TRAITS( unsigned int )
+ANDROID_BASIC_TYPES_TRAITS( long )
+ANDROID_BASIC_TYPES_TRAITS( unsigned long )
+ANDROID_BASIC_TYPES_TRAITS( long long )
+ANDROID_BASIC_TYPES_TRAITS( unsigned long long )
+ANDROID_BASIC_TYPES_TRAITS( float )
+ANDROID_BASIC_TYPES_TRAITS( double )
+
+// ---------------------------------------------------------------------------
+
+
+/*
+ * compare and order types
+ */
+
+template<typename TYPE> inline
+int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
+ return (lhs < rhs) ? 1 : 0;
+}
+
+template<typename TYPE> inline
+int compare_type(const TYPE& lhs, const TYPE& rhs) {
+ return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
+}
+
+/*
+ * create, destroy, copy and move types...
+ */
+
+template<typename TYPE> inline
+void construct_type(TYPE* p, size_t n) {
+ if (!traits<TYPE>::has_trivial_ctor) {
+ while (n > 0) {
+ n--;
+ new(p++) TYPE;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void destroy_type(TYPE* p, size_t n) {
+ if (!traits<TYPE>::has_trivial_dtor) {
+ while (n > 0) {
+ n--;
+ p->~TYPE();
+ p++;
+ }
+ }
+}
+
+template<typename TYPE>
+typename std::enable_if<traits<TYPE>::has_trivial_copy>::type
+inline
+copy_type(TYPE* d, const TYPE* s, size_t n) {
+ memcpy(d,s,n*sizeof(TYPE));
+}
+
+template<typename TYPE>
+typename std::enable_if<!traits<TYPE>::has_trivial_copy>::type
+inline
+copy_type(TYPE* d, const TYPE* s, size_t n) {
+ while (n > 0) {
+ n--;
+ new(d) TYPE(*s);
+ d++, s++;
+ }
+}
+
+template<typename TYPE> inline
+void splat_type(TYPE* where, const TYPE* what, size_t n) {
+ if (!traits<TYPE>::has_trivial_copy) {
+ while (n > 0) {
+ n--;
+ new(where) TYPE(*what);
+ where++;
+ }
+ } else {
+ while (n > 0) {
+ n--;
+ *where++ = *what;
+ }
+ }
+}
+
+template<typename TYPE>
+struct use_trivial_move : public std::integral_constant<bool,
+ (traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
+ || traits<TYPE>::has_trivial_move
+> {};
+
+template<typename TYPE>
+typename std::enable_if<use_trivial_move<TYPE>::value>::type
+inline
+move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ memmove(d, s, n*sizeof(TYPE));
+}
+
+template<typename TYPE>
+typename std::enable_if<!use_trivial_move<TYPE>::value>::type
+inline
+move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ d += n;
+ s += n;
+ while (n > 0) {
+ n--;
+ --d, --s;
+ if (!traits<TYPE>::has_trivial_copy) {
+ new(d) TYPE(*s);
+ } else {
+ *d = *s;
+ }
+ if (!traits<TYPE>::has_trivial_dtor) {
+ s->~TYPE();
+ }
+ }
+}
+
+template<typename TYPE>
+typename std::enable_if<use_trivial_move<TYPE>::value>::type
+inline
+move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ memmove(d, s, n*sizeof(TYPE));
+}
+
+template<typename TYPE>
+typename std::enable_if<!use_trivial_move<TYPE>::value>::type
+inline
+move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ while (n > 0) {
+ n--;
+ if (!traits<TYPE>::has_trivial_copy) {
+ new(d) TYPE(*s);
+ } else {
+ *d = *s;
+ }
+ if (!traits<TYPE>::has_trivial_dtor) {
+ s->~TYPE();
+ }
+ d++, s++;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+/*
+ * a key/value pair
+ */
+
+template <typename KEY, typename VALUE>
+struct key_value_pair_t {
+ typedef KEY key_t;
+ typedef VALUE value_t;
+
+ KEY key;
+ VALUE value;
+ key_value_pair_t() { }
+ key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
+ key_value_pair_t& operator=(const key_value_pair_t& o) {
+ key = o.key;
+ value = o.value;
+ return *this;
+ }
+ key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
+ explicit key_value_pair_t(const KEY& k) : key(k) { }
+ inline bool operator < (const key_value_pair_t& o) const {
+ return strictly_order_type(key, o.key);
+ }
+ inline const KEY& getKey() const {
+ return key;
+ }
+ inline const VALUE& getValue() const {
+ return value;
+ }
+};
+
+template <typename K, typename V>
+struct trait_trivial_ctor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
+template <typename K, typename V>
+struct trait_trivial_dtor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
+template <typename K, typename V>
+struct trait_trivial_copy< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
+template <typename K, typename V>
+struct trait_trivial_move< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_move }; };
+
+// ---------------------------------------------------------------------------
+
+/*
+ * Hash codes.
+ */
+typedef uint32_t hash_t;
+
+template <typename TKey>
+hash_t hash_type(const TKey& key);
+
+/* Built-in hash code specializations */
+#define ANDROID_INT32_HASH(T) \
+ template <> inline hash_t hash_type(const T& value) { return hash_t(value); }
+#define ANDROID_INT64_HASH(T) \
+ template <> inline hash_t hash_type(const T& value) { \
+ return hash_t((value >> 32) ^ value); }
+#define ANDROID_REINTERPRET_HASH(T, R) \
+ template <> inline hash_t hash_type(const T& value) { \
+ R newValue; \
+ static_assert(sizeof(newValue) == sizeof(value), "size mismatch"); \
+ memcpy(&newValue, &value, sizeof(newValue)); \
+ return hash_type(newValue); \
+ }
+
+ANDROID_INT32_HASH(bool)
+ANDROID_INT32_HASH(int8_t)
+ANDROID_INT32_HASH(uint8_t)
+ANDROID_INT32_HASH(int16_t)
+ANDROID_INT32_HASH(uint16_t)
+ANDROID_INT32_HASH(int32_t)
+ANDROID_INT32_HASH(uint32_t)
+ANDROID_INT64_HASH(int64_t)
+ANDROID_INT64_HASH(uint64_t)
+ANDROID_REINTERPRET_HASH(float, uint32_t)
+ANDROID_REINTERPRET_HASH(double, uint64_t)
+
+template <typename T> inline hash_t hash_type(T* const & value) {
+ return hash_type(uintptr_t(value));
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_TYPE_HELPERS_H
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
new file mode 100644
index 0000000..666b70f
--- /dev/null
+++ b/libutils/include/utils/Unicode.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_UNICODE_H
+#define ANDROID_UNICODE_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern "C" {
+
+// Standard string functions on char16_t strings.
+int strcmp16(const char16_t *, const char16_t *);
+int strncmp16(const char16_t *s1, const char16_t *s2, size_t n);
+size_t strlen16(const char16_t *);
+size_t strnlen16(const char16_t *, size_t);
+char16_t *strcpy16(char16_t *, const char16_t *);
+char16_t *strncpy16(char16_t *, const char16_t *, size_t);
+char16_t *strstr16(const char16_t*, const char16_t*);
+
+// Version of comparison that supports embedded NULs.
+// This is different than strncmp() because we don't stop
+// at a nul character and consider the strings to be different
+// if the lengths are different (thus we need to supply the
+// lengths of both strings). This can also be used when
+// your string is not nul-terminated as it will have the
+// equivalent result as strcmp16 (unlike strncmp16).
+int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
+
+// Version of strzcmp16 for comparing strings in different endianness.
+int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
+
+// Standard string functions on char32_t strings.
+size_t strlen32(const char32_t *);
+size_t strnlen32(const char32_t *, size_t);
+
+/**
+ * Measure the length of a UTF-32 string in UTF-8. If the string is invalid
+ * such as containing a surrogate character, -1 will be returned.
+ */
+ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len);
+
+/**
+ * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not
+ * large enough to store the string, the part of the "src" string is stored
+ * into "dst" as much as possible. See the examples for more detail.
+ * Returns the size actually used for storing the string.
+ * dst" is not nul-terminated when dst_len is fully used (like strncpy).
+ *
+ * \code
+ * Example 1
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" >= 7
+ * ->
+ * Returned value == 6
+ * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0
+ * (note that "dst" is nul-terminated)
+ *
+ * Example 2
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" == 5
+ * ->
+ * Returned value == 3
+ * "dst" becomes \xE3\x81\x82\0
+ * (note that "dst" is nul-terminated, but \u3044 is not stored in "dst"
+ * since "dst" does not have enough size to store the character)
+ *
+ * Example 3
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" == 6
+ * ->
+ * Returned value == 6
+ * "dst" becomes \xE3\x81\x82\xE3\x81\x84
+ * (note that "dst" is NOT nul-terminated, like strncpy)
+ * \endcode
+ */
+void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len);
+
+/**
+ * Returns the unicode value at "index".
+ * Returns -1 when the index is invalid (equals to or more than "src_len").
+ * If returned value is positive, it is able to be converted to char32_t, which
+ * is unsigned. Then, if "next_index" is not NULL, the next index to be used is
+ * stored in "next_index". "next_index" can be NULL.
+ */
+int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index);
+
+
+/**
+ * Returns the UTF-8 length of UTF-16 string "src".
+ */
+ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len);
+
+/**
+ * Converts a UTF-16 string to UTF-8. The destination buffer must be large
+ * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added
+ * NUL terminator.
+ */
+void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len);
+
+/**
+ * Returns the length of "src" when "src" is valid UTF-8 string.
+ * Returns 0 if src is NULL or 0-length string. Returns -1 when the source
+ * is an invalid string.
+ *
+ * This function should be used to determine whether "src" is valid UTF-8
+ * characters with valid unicode codepoints. "src" must be nul-terminated.
+ *
+ * If you are going to use other utf8_to_... functions defined in this header
+ * with string which may not be valid UTF-8 with valid codepoint (form 0 to
+ * 0x10FFFF), you should use this function before calling others, since the
+ * other functions do not check whether the string is valid UTF-8 or not.
+ *
+ * If you do not care whether "src" is valid UTF-8 or not, you should use
+ * strlen() as usual, which should be much faster.
+ */
+ssize_t utf8_length(const char *src);
+
+/**
+ * Measure the length of a UTF-32 string.
+ */
+size_t utf8_to_utf32_length(const char *src, size_t src_len);
+
+/**
+ * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large
+ * enough to store the entire converted string as measured by
+ * utf8_to_utf32_length plus space for a NUL terminator.
+ */
+void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst);
+
+/**
+ * Returns the UTF-16 length of UTF-8 string "src". Returns -1 in case
+ * it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you
+ * can ask to log a message and fail in case the invalid utf8 could have caused an override if no
+ * bound checks were used (otherwise -1 is returned).
+ */
+ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen, bool overreadIsFatal = false);
+
+/**
+ * Convert UTF-8 to UTF-16 including surrogate pairs.
+ * Returns a pointer to the end of the string (where a NUL terminator might go
+ * if you wanted to add one). At most dstLen characters are written; it won't emit half a surrogate
+ * pair. If dstLen == 0 nothing is written and dst is returned. If dstLen > SSIZE_MAX it aborts
+ * (this being probably a negative number returned as an error and casted to unsigned).
+ */
+char16_t* utf8_to_utf16_no_null_terminator(
+ const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);
+
+/**
+ * Convert UTF-8 to UTF-16 including surrogate pairs. At most dstLen - 1
+ * characters are written; it won't emit half a surrogate pair; and a NUL terminator is appended
+ * after. dstLen - 1 can be measured beforehand using utf8_to_utf16_length. Aborts if dstLen == 0
+ * (at least one character is needed for the NUL terminator) or dstLen > SSIZE_MAX (the latter
+ * case being likely a negative number returned as an error and casted to unsigned) . Returns a
+ * pointer to the NUL terminator.
+ */
+char16_t *utf8_to_utf16(
+ const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);
+
+}
+
+#endif
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
new file mode 100644
index 0000000..28a77b8
--- /dev/null
+++ b/libutils/include/utils/Vector.h
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_VECTOR_H
+#define ANDROID_VECTOR_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <new>
+
+#include <log/log.h>
+#include <utils/TypeHelpers.h>
+#include <utils/VectorImpl.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+template <typename TYPE>
+class SortedVector;
+
+/*!
+ * The main templated vector class ensuring type safety
+ * while making use of VectorImpl.
+ * This is the class users want to use.
+ */
+
+template <class TYPE>
+class Vector : private VectorImpl
+{
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ Vector();
+ Vector(const Vector<TYPE>& rhs);
+ explicit Vector(const SortedVector<TYPE>& rhs);
+ virtual ~Vector();
+
+ /*! copy operator */
+ const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
+ Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
+
+ const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
+ Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns whether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! sets the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * set the size of the vector. items are appended with the default
+ * constructor, or removed from the end as needed.
+ */
+ inline ssize_t resize(size_t size) { return VectorImpl::resize(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+ //! read-write C-style access
+ TYPE* editArray();
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+
+ /*!
+ * modifying the array
+ */
+
+ //! copy-on write support, grants write access to an item
+ TYPE& editItemAt(size_t index);
+ //! grants right access to the top of the stack (last element)
+ TYPE& editTop();
+
+ /*!
+ * append/insert another vector
+ */
+
+ //! insert another vector at a given index
+ ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index);
+
+ //! append another vector at the end of this one
+ ssize_t appendVector(const Vector<TYPE>& vector);
+
+
+ //! insert an array at a given index
+ ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length);
+
+ //! append an array at the end of this vector
+ ssize_t appendArray(const TYPE* array, size_t length);
+
+ /*!
+ * add/insert/replace items
+ */
+
+ //! insert one or several items initialized with their default constructor
+ inline ssize_t insertAt(size_t index, size_t numItems = 1);
+ //! insert one or several items initialized from a prototype item
+ ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
+ //! pop the top of the stack (removes the last element). No-op if the stack's empty
+ inline void pop();
+ //! pushes an item initialized with its default constructor
+ inline void push();
+ //! pushes an item on the top of the stack
+ void push(const TYPE& item);
+ //! same as push() but returns the index the item was added at (or an error)
+ inline ssize_t add();
+ //! same as push() but returns the index the item was added at (or an error)
+ ssize_t add(const TYPE& item);
+ //! replace an item with a new one initialized with its default constructor
+ inline ssize_t replaceAt(size_t index);
+ //! replace an item with a new one
+ ssize_t replaceAt(const TYPE& item, size_t index);
+
+ /*!
+ * remove items
+ */
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+ /*!
+ * sort (stable) the array
+ */
+
+ typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
+ typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
+
+ inline status_t sort(compar_t cmp);
+ inline status_t sort(compar_r_t cmp, void* state);
+
+ // for debugging only
+ inline size_t getItemSize() const { return itemSize(); }
+
+
+ /*
+ * these inlines add some level of compatibility with STL. eventually
+ * we should probably turn things around.
+ */
+ typedef TYPE* iterator;
+ typedef TYPE const* const_iterator;
+
+ inline iterator begin() { return editArray(); }
+ inline iterator end() { return editArray() + size(); }
+ inline const_iterator begin() const { return array(); }
+ inline const_iterator end() const { return array() + size(); }
+ inline void reserve(size_t n) { setCapacity(n); }
+ inline bool empty() const{ return isEmpty(); }
+ inline void push_back(const TYPE& item) { insertAt(item, size(), 1); }
+ inline void push_front(const TYPE& item) { insertAt(item, 0, 1); }
+ inline iterator erase(iterator pos) {
+ ssize_t index = removeItemsAt(static_cast<size_t>(pos-array()));
+ return begin() + index;
+ }
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+};
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+Vector<TYPE>::Vector()
+ : VectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
+ : VectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+Vector<TYPE>::Vector(const SortedVector<TYPE>& rhs)
+ : VectorImpl(static_cast<const VectorImpl&>(rhs)) {
+}
+
+template<class TYPE> inline
+Vector<TYPE>::~Vector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
+ VectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
+ VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
+ return *this;
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+ VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
+ return *this;
+}
+
+template<class TYPE> inline
+const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
+ VectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* Vector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* Vector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::operator[](size_t index) const {
+ LOG_FATAL_IF(index>=size(),
+ "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__,
+ int(index), int(size()));
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(editItemLocation(index)) );
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editTop() {
+ return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
+ return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
+ return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) {
+ return VectorImpl::insertArrayAt(array, index, length);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) {
+ return VectorImpl::appendArray(array, length);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
+ return VectorImpl::insertAt(&item, index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push(const TYPE& item) {
+ return VectorImpl::push(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add(const TYPE& item) {
+ return VectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
+ return VectorImpl::replaceAt(&item, index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
+ return VectorImpl::insertAt(index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::pop() {
+ VectorImpl::pop();
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push() {
+ VectorImpl::push();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add() {
+ return VectorImpl::add();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(size_t index) {
+ return VectorImpl::replaceAt(index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+template<class TYPE> inline
+status_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) {
+ return VectorImpl::sort(reinterpret_cast<VectorImpl::compar_t>(cmp));
+}
+
+template<class TYPE> inline
+status_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) {
+ return VectorImpl::sort(reinterpret_cast<VectorImpl::compar_r_t>(cmp), state);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void Vector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_H
diff --git a/libutils/include/utils/VectorImpl.h b/libutils/include/utils/VectorImpl.h
new file mode 100644
index 0000000..4dd91fd
--- /dev/null
+++ b/libutils/include/utils/VectorImpl.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_VECTOR_IMPL_H
+#define ANDROID_VECTOR_IMPL_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts in here...
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+/*!
+ * Implementation of the guts of the vector<> class
+ * this ensures backward binary compatibility and
+ * reduces code size.
+ * For performance reasons, we expose mStorage and mCount
+ * so these fields are set in stone.
+ *
+ */
+
+class VectorImpl
+{
+public:
+ enum { // flags passed to the ctor
+ HAS_TRIVIAL_CTOR = 0x00000001,
+ HAS_TRIVIAL_DTOR = 0x00000002,
+ HAS_TRIVIAL_COPY = 0x00000004,
+ };
+
+ VectorImpl(size_t itemSize, uint32_t flags);
+ VectorImpl(const VectorImpl& rhs);
+ virtual ~VectorImpl();
+
+ /*! must be called from subclasses destructor */
+ void finish_vector();
+
+ VectorImpl& operator = (const VectorImpl& rhs);
+
+ /*! C-style array access */
+ inline const void* arrayImpl() const { return mStorage; }
+ void* editArrayImpl();
+
+ /*! vector stats */
+ inline size_t size() const { return mCount; }
+ inline bool isEmpty() const { return mCount == 0; }
+ size_t capacity() const;
+ ssize_t setCapacity(size_t size);
+ ssize_t resize(size_t size);
+
+ /*! append/insert another vector or array */
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertArrayAt(const void* array, size_t index, size_t length);
+ ssize_t appendArray(const void* array, size_t length);
+
+ /*! add/insert/replace items */
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t add();
+ ssize_t add(const void* item);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+
+ /*! remove items */
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+ void clear();
+
+ const void* itemLocation(size_t index) const;
+ void* editItemLocation(size_t index);
+
+ typedef int (*compar_t)(const void* lhs, const void* rhs);
+ typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state);
+ status_t sort(compar_t cmp);
+ status_t sort(compar_r_t cmp, void* state);
+
+protected:
+ size_t itemSize() const;
+ void release_storage();
+
+ virtual void do_construct(void* storage, size_t num) const = 0;
+ virtual void do_destroy(void* storage, size_t num) const = 0;
+ virtual void do_copy(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_splat(void* dest, const void* item, size_t num) const = 0;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0;
+
+private:
+ void* _grow(size_t where, size_t amount);
+ void _shrink(size_t where, size_t amount);
+
+ inline void _do_construct(void* storage, size_t num) const;
+ inline void _do_destroy(void* storage, size_t num) const;
+ inline void _do_copy(void* dest, const void* from, size_t num) const;
+ inline void _do_splat(void* dest, const void* item, size_t num) const;
+ inline void _do_move_forward(void* dest, const void* from, size_t num) const;
+ inline void _do_move_backward(void* dest, const void* from, size_t num) const;
+
+ // These 2 fields are exposed in the inlines below,
+ // so they're set in stone.
+ void * mStorage; // base address of the vector
+ size_t mCount; // number of items
+
+ const uint32_t mFlags;
+ const size_t mItemSize;
+};
+
+
+
+class SortedVectorImpl : public VectorImpl
+{
+public:
+ SortedVectorImpl(size_t itemSize, uint32_t flags);
+ explicit SortedVectorImpl(const VectorImpl& rhs);
+ virtual ~SortedVectorImpl();
+
+ SortedVectorImpl& operator = (const SortedVectorImpl& rhs);
+
+ //! finds the index of an item
+ ssize_t indexOf(const void* item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const void* item) const;
+
+ //! add an item in the right place (or replaces it if there is one)
+ ssize_t add(const void* item);
+
+ //! merges a vector into this one
+ ssize_t merge(const VectorImpl& vector);
+ ssize_t merge(const SortedVectorImpl& vector);
+
+ //! removes an item
+ ssize_t remove(const void* item);
+
+protected:
+ virtual int do_compare(const void* lhs, const void* rhs) const = 0;
+
+private:
+ ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
+
+ // these are made private, because they can't be used on a SortedVector
+ // (they don't have an implementation either)
+ ssize_t add();
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertArrayAt(const void* array, size_t index, size_t length);
+ ssize_t appendArray(const void* array, size_t length);
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+};
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_IMPL_H
diff --git a/include/utils/misc.h b/libutils/include/utils/misc.h
similarity index 100%
rename from include/utils/misc.h
rename to libutils/include/utils/misc.h
diff --git a/include/utils/threads.h b/libutils/include/utils/threads.h
similarity index 100%
rename from include/utils/threads.h
rename to libutils/include/utils/threads.h
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
new file mode 100644
index 0000000..ec6b67f
--- /dev/null
+++ b/libutils/tests/Android.bp
@@ -0,0 +1,50 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Build the unit tests.
+
+cc_test {
+ name: "libutils_tests",
+
+ srcs: [
+ "BlobCache_test.cpp",
+ "BitSet_test.cpp",
+ "Looper_test.cpp",
+ "LruCache_test.cpp",
+ "RefBase_test.cpp",
+ "String8_test.cpp",
+ "StrongPointer_test.cpp",
+ "SystemClock_test.cpp",
+ "Unicode_test.cpp",
+ "Vector_test.cpp",
+ ],
+
+ shared_libs: [
+ "libz",
+ "liblog",
+ "libcutils",
+ "libutils",
+ ],
+}
+
+cc_test_host {
+ name: "libutils_tests_host",
+ srcs: ["Vector_test.cpp"],
+ static_libs: [
+ "libutils",
+ "liblog",
+ ],
+}
diff --git a/libutils/tests/Android.mk b/libutils/tests/Android.mk
deleted file mode 100644
index 7cfad89..0000000
--- a/libutils/tests/Android.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Build the unit tests.
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libutils_tests
-
-LOCAL_SRC_FILES := \
- BasicHashtable_test.cpp \
- BlobCache_test.cpp \
- BitSet_test.cpp \
- Looper_test.cpp \
- LruCache_test.cpp \
- String8_test.cpp \
- Unicode_test.cpp \
- Vector_test.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- libz \
- liblog \
- libcutils \
- libutils \
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libutils/tests/BasicHashtable_test.cpp b/libutils/tests/BasicHashtable_test.cpp
deleted file mode 100644
index 4b3a717..0000000
--- a/libutils/tests/BasicHashtable_test.cpp
+++ /dev/null
@@ -1,582 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "BasicHashtable_test"
-
-#include <utils/BasicHashtable.h>
-#include <cutils/log.h>
-#include <gtest/gtest.h>
-#include <unistd.h>
-
-namespace {
-
-typedef int SimpleKey;
-typedef int SimpleValue;
-typedef android::key_value_pair_t<SimpleKey, SimpleValue> SimpleEntry;
-typedef android::BasicHashtable<SimpleKey, SimpleEntry> SimpleHashtable;
-
-struct ComplexKey {
- int k;
-
- explicit ComplexKey(int k) : k(k) {
- instanceCount += 1;
- }
-
- ComplexKey(const ComplexKey& other) : k(other.k) {
- instanceCount += 1;
- }
-
- ~ComplexKey() {
- instanceCount -= 1;
- }
-
- bool operator ==(const ComplexKey& other) const {
- return k == other.k;
- }
-
- bool operator !=(const ComplexKey& other) const {
- return k != other.k;
- }
-
- static ssize_t instanceCount;
-};
-
-ssize_t ComplexKey::instanceCount = 0;
-
-struct ComplexValue {
- int v;
-
- explicit ComplexValue(int v) : v(v) {
- instanceCount += 1;
- }
-
- ComplexValue(const ComplexValue& other) : v(other.v) {
- instanceCount += 1;
- }
-
- ~ComplexValue() {
- instanceCount -= 1;
- }
-
- static ssize_t instanceCount;
-};
-
-ssize_t ComplexValue::instanceCount = 0;
-
-} // namespace
-
-
-namespace android {
-
-typedef key_value_pair_t<ComplexKey, ComplexValue> ComplexEntry;
-typedef BasicHashtable<ComplexKey, ComplexEntry> ComplexHashtable;
-
-template<> inline hash_t hash_type(const ComplexKey& value) {
- return hash_type(value.k);
-}
-
-class BasicHashtableTest : public testing::Test {
-protected:
- virtual void SetUp() {
- ComplexKey::instanceCount = 0;
- ComplexValue::instanceCount = 0;
- }
-
- virtual void TearDown() {
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
- }
-
- void assertInstanceCount(ssize_t keys, ssize_t values) {
- if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
- FAIL() << "Expected " << keys << " keys and " << values << " values "
- "but there were actually " << ComplexKey::instanceCount << " keys and "
- << ComplexValue::instanceCount << " values";
- }
- }
-
-public:
- template <typename TKey, typename TEntry>
- static void cookieAt(const BasicHashtable<TKey, TEntry>& h, size_t index,
- bool* collision, bool* present, hash_t* hash) {
- uint32_t cookie = h.cookieAt(index);
- *collision = cookie & BasicHashtable<TKey, TEntry>::Bucket::COLLISION;
- *present = cookie & BasicHashtable<TKey, TEntry>::Bucket::PRESENT;
- *hash = cookie & BasicHashtable<TKey, TEntry>::Bucket::HASH_MASK;
- }
-
- template <typename TKey, typename TEntry>
- static const void* getBuckets(const BasicHashtable<TKey, TEntry>& h) {
- return h.mBuckets;
- }
-};
-
-template <typename TKey, typename TValue>
-static size_t add(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
- const TKey& key, const TValue& value) {
- return h.add(hash_type(key), key_value_pair_t<TKey, TValue>(key, value));
-}
-
-template <typename TKey, typename TValue>
-static ssize_t find(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
- ssize_t index, const TKey& key) {
- return h.find(index, hash_type(key), key);
-}
-
-template <typename TKey, typename TValue>
-static bool remove(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
- const TKey& key) {
- ssize_t index = find(h, -1, key);
- if (index >= 0) {
- h.removeAt(index);
- return true;
- }
- return false;
-}
-
-template <typename TEntry>
-static void getKeyValue(const TEntry& entry, int* key, int* value);
-
-template <> void getKeyValue(const SimpleEntry& entry, int* key, int* value) {
- *key = entry.key;
- *value = entry.value;
-}
-
-template <> void getKeyValue(const ComplexEntry& entry, int* key, int* value) {
- *key = entry.key.k;
- *value = entry.value.v;
-}
-
-template <typename TKey, typename TValue>
-static void dump(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h) {
- ALOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u",
- &h, h.size(), h.capacity(), h.bucketCount());
- for (size_t i = 0; i < h.bucketCount(); i++) {
- bool collision, present;
- hash_t hash;
- BasicHashtableTest::cookieAt(h, i, &collision, &present, &hash);
- if (present) {
- int key, value;
- getKeyValue(h.entryAt(i), &key, &value);
- ALOGD(" [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, "
- "hash_type(key)=0x%08x",
- i, collision, present, hash, key, value, hash_type(key));
- } else {
- ALOGD(" [%3u] = collision=%d, present=%d",
- i, collision, present);
- }
- }
-}
-
-TEST_F(BasicHashtableTest, DefaultConstructor_WithDefaultProperties) {
- SimpleHashtable h;
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(3U, h.capacity());
- EXPECT_EQ(5U, h.bucketCount());
- EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Constructor_WithNonUnityLoadFactor) {
- SimpleHashtable h(52, 0.8f);
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(77U, h.capacity());
- EXPECT_EQ(97U, h.bucketCount());
- EXPECT_EQ(0.8f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndExactCapacity) {
- SimpleHashtable h(46, 1.0f);
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f
- EXPECT_EQ(47U, h.bucketCount());
- EXPECT_EQ(1.0f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndInexactCapacity) {
- SimpleHashtable h(42, 1.0f);
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f
- EXPECT_EQ(47U, h.bucketCount());
- EXPECT_EQ(1.0f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, FindAddFindRemoveFind_OneEntry) {
- SimpleHashtable h;
- ssize_t index = find(h, -1, 8);
- ASSERT_EQ(-1, index);
-
- index = add(h, 8, 1);
- ASSERT_EQ(1U, h.size());
-
- ASSERT_EQ(index, find(h, -1, 8));
- ASSERT_EQ(8, h.entryAt(index).key);
- ASSERT_EQ(1, h.entryAt(index).value);
-
- index = find(h, index, 8);
- ASSERT_EQ(-1, index);
-
- ASSERT_TRUE(remove(h, 8));
- ASSERT_EQ(0U, h.size());
-
- index = find(h, -1, 8);
- ASSERT_EQ(-1, index);
-}
-
-TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithUniqueKey) {
- const size_t N = 11;
-
- SimpleHashtable h;
- for (size_t i = 0; i < N; i++) {
- ssize_t index = find(h, -1, int(i));
- ASSERT_EQ(-1, index);
-
- index = add(h, int(i), int(i * 10));
- ASSERT_EQ(i + 1, h.size());
-
- ASSERT_EQ(index, find(h, -1, int(i)));
- ASSERT_EQ(int(i), h.entryAt(index).key);
- ASSERT_EQ(int(i * 10), h.entryAt(index).value);
-
- index = find(h, index, int(i));
- ASSERT_EQ(-1, index);
- }
-
- for (size_t i = N; --i > 0; ) {
- ASSERT_TRUE(remove(h, int(i))) << "i = " << i;
- ASSERT_EQ(i, h.size());
-
- ssize_t index = find(h, -1, int(i));
- ASSERT_EQ(-1, index);
- }
-}
-
-TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithDuplicateKey) {
- const size_t N = 11;
- const int K = 1;
-
- SimpleHashtable h;
- for (size_t i = 0; i < N; i++) {
- ssize_t index = find(h, -1, K);
- if (i == 0) {
- ASSERT_EQ(-1, index);
- } else {
- ASSERT_NE(-1, index);
- }
-
- add(h, K, int(i));
- ASSERT_EQ(i + 1, h.size());
-
- index = -1;
- int values = 0;
- for (size_t j = 0; j <= i; j++) {
- index = find(h, index, K);
- ASSERT_GE(index, 0);
- ASSERT_EQ(K, h.entryAt(index).key);
- values |= 1 << h.entryAt(index).value;
- }
- ASSERT_EQ(values, (1 << (i + 1)) - 1);
-
- index = find(h, index, K);
- ASSERT_EQ(-1, index);
- }
-
- for (size_t i = N; --i > 0; ) {
- ASSERT_TRUE(remove(h, K)) << "i = " << i;
- ASSERT_EQ(i, h.size());
-
- ssize_t index = -1;
- for (size_t j = 0; j < i; j++) {
- index = find(h, index, K);
- ASSERT_GE(index, 0);
- ASSERT_EQ(K, h.entryAt(index).key);
- }
-
- index = find(h, index, K);
- ASSERT_EQ(-1, index);
- }
-}
-
-TEST_F(BasicHashtableTest, Clear_WhenAlreadyEmpty_DoesNothing) {
- SimpleHashtable h;
- h.clear();
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(3U, h.capacity());
- EXPECT_EQ(5U, h.bucketCount());
- EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_RemovesThem) {
- SimpleHashtable h;
- add(h, 0, 0);
- add(h, 1, 0);
- h.clear();
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(3U, h.capacity());
- EXPECT_EQ(5U, h.bucketCount());
- EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_DestroysThem) {
- ComplexHashtable h;
- add(h, ComplexKey(0), ComplexValue(0));
- add(h, ComplexKey(1), ComplexValue(0));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-
- h.clear();
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(3U, h.capacity());
- EXPECT_EQ(5U, h.bucketCount());
- EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Remove_AfterElementsAdded_DestroysThem) {
- ComplexHashtable h;
- add(h, ComplexKey(0), ComplexValue(0));
- add(h, ComplexKey(1), ComplexValue(0));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-
- ASSERT_TRUE(remove(h, ComplexKey(0)));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
-
- ASSERT_TRUE(remove(h, ComplexKey(1)));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(3U, h.capacity());
- EXPECT_EQ(5U, h.bucketCount());
- EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Destructor_AfterElementsAdded_DestroysThem) {
- {
- ComplexHashtable h;
- add(h, ComplexKey(0), ComplexValue(0));
- add(h, ComplexKey(1), ComplexValue(0));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
- } // h is destroyed here
-
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-}
-
-TEST_F(BasicHashtableTest, Next_WhenEmpty_ReturnsMinusOne) {
- SimpleHashtable h;
-
- ASSERT_EQ(-1, h.next(-1));
-}
-
-TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) {
- const int N = 88;
-
- SimpleHashtable h;
- for (int i = 0; i < N; i++) {
- add(h, i, i * 10);
- }
-
- bool set[N];
- memset(set, 0, sizeof(bool) * N);
- int count = 0;
- for (ssize_t index = -1; (index = h.next(index)) != -1; ) {
- ASSERT_GE(index, 0);
- ASSERT_LT(size_t(index), h.bucketCount());
-
- const SimpleEntry& entry = h.entryAt(index);
- ASSERT_GE(entry.key, 0);
- ASSERT_LT(entry.key, N);
- ASSERT_FALSE(set[entry.key]);
- ASSERT_EQ(entry.key * 10, entry.value);
-
- set[entry.key] = true;
- count += 1;
- }
- ASSERT_EQ(N, count);
-}
-
-TEST_F(BasicHashtableTest, Add_RehashesOnDemand) {
- SimpleHashtable h;
- size_t initialCapacity = h.capacity();
- size_t initialBucketCount = h.bucketCount();
-
- for (size_t i = 0; i < initialCapacity; i++) {
- add(h, int(i), 0);
- }
-
- EXPECT_EQ(initialCapacity, h.size());
- EXPECT_EQ(initialCapacity, h.capacity());
- EXPECT_EQ(initialBucketCount, h.bucketCount());
-
- add(h, -1, -1);
-
- EXPECT_EQ(initialCapacity + 1, h.size());
- EXPECT_GT(h.capacity(), initialCapacity);
- EXPECT_GT(h.bucketCount(), initialBucketCount);
- EXPECT_GT(h.bucketCount(), h.capacity());
-}
-
-TEST_F(BasicHashtableTest, Rehash_WhenCapacityAndBucketCountUnchanged_DoesNothing) {
- ComplexHashtable h;
- add(h, ComplexKey(0), ComplexValue(0));
- const void* oldBuckets = getBuckets(h);
- ASSERT_NE((void*)NULL, oldBuckets);
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
-
- h.rehash(h.capacity(), h.loadFactor());
-
- ASSERT_EQ(oldBuckets, getBuckets(h));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
-}
-
-TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasNoBuckets_ButDoesNotAllocateBuckets) {
- ComplexHashtable h;
- ASSERT_EQ((void*)NULL, getBuckets(h));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-
- h.rehash(9, 1.0f);
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(10U, h.capacity());
- EXPECT_EQ(11U, h.bucketCount());
- EXPECT_EQ(1.0f, h.loadFactor());
- EXPECT_EQ((void*)NULL, getBuckets(h));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-}
-
-TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasBuckets_ReleasesBucketsAndSetsCapacity) {
- ComplexHashtable h(10);
- add(h, ComplexKey(0), ComplexValue(0));
- ASSERT_TRUE(remove(h, ComplexKey(0)));
- ASSERT_NE((void*)NULL, getBuckets(h));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-
- h.rehash(0, 0.75f);
-
- EXPECT_EQ(0U, h.size());
- EXPECT_EQ(3U, h.capacity());
- EXPECT_EQ(5U, h.bucketCount());
- EXPECT_EQ(0.75f, h.loadFactor());
- EXPECT_EQ((void*)NULL, getBuckets(h));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-}
-
-TEST_F(BasicHashtableTest, Rehash_WhenLessThanCurrentCapacity_ShrinksBuckets) {
- ComplexHashtable h(10);
- add(h, ComplexKey(0), ComplexValue(0));
- add(h, ComplexKey(1), ComplexValue(1));
- const void* oldBuckets = getBuckets(h);
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-
- h.rehash(0, 0.75f);
-
- EXPECT_EQ(2U, h.size());
- EXPECT_EQ(3U, h.capacity());
- EXPECT_EQ(5U, h.bucketCount());
- EXPECT_EQ(0.75f, h.loadFactor());
- EXPECT_NE(oldBuckets, getBuckets(h));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-}
-
-TEST_F(BasicHashtableTest, CopyOnWrite) {
- ComplexHashtable h1;
- add(h1, ComplexKey(0), ComplexValue(0));
- add(h1, ComplexKey(1), ComplexValue(1));
- const void* originalBuckets = getBuckets(h1);
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
- ssize_t index0 = find(h1, -1, ComplexKey(0));
- EXPECT_GE(index0, 0);
-
- // copy constructor acquires shared reference
- ComplexHashtable h2(h1);
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
- ASSERT_EQ(originalBuckets, getBuckets(h2));
- EXPECT_EQ(h1.size(), h2.size());
- EXPECT_EQ(h1.capacity(), h2.capacity());
- EXPECT_EQ(h1.bucketCount(), h2.bucketCount());
- EXPECT_EQ(h1.loadFactor(), h2.loadFactor());
- EXPECT_EQ(index0, find(h2, -1, ComplexKey(0)));
-
- // operator= acquires shared reference
- ComplexHashtable h3;
- h3 = h2;
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
- ASSERT_EQ(originalBuckets, getBuckets(h3));
- EXPECT_EQ(h1.size(), h3.size());
- EXPECT_EQ(h1.capacity(), h3.capacity());
- EXPECT_EQ(h1.bucketCount(), h3.bucketCount());
- EXPECT_EQ(h1.loadFactor(), h3.loadFactor());
- EXPECT_EQ(index0, find(h3, -1, ComplexKey(0)));
-
- // editEntryAt copies shared contents
- h1.editEntryAt(index0).value.v = 42;
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
- ASSERT_NE(originalBuckets, getBuckets(h1));
- EXPECT_EQ(42, h1.entryAt(index0).value.v);
- EXPECT_EQ(0, h2.entryAt(index0).value.v);
- EXPECT_EQ(0, h3.entryAt(index0).value.v);
-
- // clear releases reference to shared contents
- h2.clear();
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
- EXPECT_EQ(0U, h2.size());
- ASSERT_NE(originalBuckets, getBuckets(h2));
-
- // operator= acquires shared reference, destroys unshared contents
- h1 = h3;
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
- ASSERT_EQ(originalBuckets, getBuckets(h1));
- EXPECT_EQ(h3.size(), h1.size());
- EXPECT_EQ(h3.capacity(), h1.capacity());
- EXPECT_EQ(h3.bucketCount(), h1.bucketCount());
- EXPECT_EQ(h3.loadFactor(), h1.loadFactor());
- EXPECT_EQ(index0, find(h1, -1, ComplexKey(0)));
-
- // add copies shared contents
- add(h1, ComplexKey(2), ComplexValue(2));
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(5, 5));
- ASSERT_NE(originalBuckets, getBuckets(h1));
- EXPECT_EQ(3U, h1.size());
- EXPECT_EQ(0U, h2.size());
- EXPECT_EQ(2U, h3.size());
-
- // remove copies shared contents
- h1 = h3;
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
- ASSERT_EQ(originalBuckets, getBuckets(h1));
- h1.removeAt(index0);
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(3, 3));
- ASSERT_NE(originalBuckets, getBuckets(h1));
- EXPECT_EQ(1U, h1.size());
- EXPECT_EQ(0U, h2.size());
- EXPECT_EQ(2U, h3.size());
-
- // rehash copies shared contents
- h1 = h3;
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
- ASSERT_EQ(originalBuckets, getBuckets(h1));
- h1.rehash(10, 1.0f);
- ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
- ASSERT_NE(originalBuckets, getBuckets(h1));
- EXPECT_EQ(2U, h1.size());
- EXPECT_EQ(0U, h2.size());
- EXPECT_EQ(2U, h3.size());
-}
-
-} // namespace android
diff --git a/libutils/tests/BitSet_test.cpp b/libutils/tests/BitSet_test.cpp
index 59d913e..fbcf025 100644
--- a/libutils/tests/BitSet_test.cpp
+++ b/libutils/tests/BitSet_test.cpp
@@ -16,11 +16,12 @@
#define LOG_TAG "BitSet_test"
-#include <utils/BitSet.h>
-#include <cutils/log.h>
-#include <gtest/gtest.h>
#include <unistd.h>
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <utils/BitSet.h>
+
namespace android {
class BitSet32Test : public testing::Test {
diff --git a/libutils/tests/BlobCache_test.cpp b/libutils/tests/BlobCache_test.cpp
index dac4e2c..1e2ff98 100644
--- a/libutils/tests/BlobCache_test.cpp
+++ b/libutils/tests/BlobCache_test.cpp
@@ -343,7 +343,9 @@
size_t size = mBC->getFlattenedSize() - 1;
uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
+ // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
+ // TODO: The above fails. I expect this is so because getFlattenedSize()
+ // overstimates the size by using PROPERTY_VALUE_MAX.
delete[] flat;
}
@@ -411,7 +413,9 @@
ASSERT_EQ(OK, mBC->flatten(flat, size));
// A buffer truncation shouldt cause an error
- ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
+ // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
+ // TODO: The above appears to fail because getFlattenedSize() is
+ // conservative.
delete[] flat;
// The error should cause the unflatten to result in an empty cache
diff --git a/libutils/tests/Looper_test.cpp b/libutils/tests/Looper_test.cpp
index 00077e6..8ebcfaf 100644
--- a/libutils/tests/Looper_test.cpp
+++ b/libutils/tests/Looper_test.cpp
@@ -76,7 +76,7 @@
int fd;
int events;
- StubCallbackHandler(int nextResult) : nextResult(nextResult),
+ explicit StubCallbackHandler(int nextResult) : nextResult(nextResult),
callbackCount(0), fd(-1), events(-1) {
}
@@ -138,7 +138,7 @@
TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) {
sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper);
- delayedWake->run();
+ delayedWake->run("LooperTest");
StopWatch stopWatch("pollOnce");
int result = mLooper->pollOnce(1000);
@@ -251,7 +251,7 @@
sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
- delayedWriteSignal->run();
+ delayedWriteSignal->run("LooperTest");
StopWatch stopWatch("pollOnce");
int result = mLooper->pollOnce(1000);
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index 6155def..4e885bb 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -15,10 +15,11 @@
*/
#include <stdlib.h>
+
+#include <android/log.h>
+#include <gtest/gtest.h>
#include <utils/JenkinsHash.h>
#include <utils/LruCache.h>
-#include <cutils/log.h>
-#include <gtest/gtest.h>
namespace {
@@ -73,6 +74,21 @@
ssize_t ComplexValue::instanceCount = 0;
+struct KeyWithPointer {
+ int *ptr;
+ bool operator ==(const KeyWithPointer& other) const {
+ return *ptr == *other.ptr;
+ }
+};
+
+struct KeyFailsOnCopy : public ComplexKey {
+ public:
+ KeyFailsOnCopy(const KeyFailsOnCopy& key) : ComplexKey(key) {
+ ADD_FAILURE();
+ }
+ KeyFailsOnCopy(int key) : ComplexKey(key) { }
+};
+
} // namespace
@@ -84,6 +100,14 @@
return hash_type(value.k);
}
+template<> inline android::hash_t hash_type(const KeyWithPointer& value) {
+ return hash_type(*value.ptr);
+}
+
+template<> inline android::hash_t hash_type(const KeyFailsOnCopy& value) {
+ return hash_type<ComplexKey>(value);
+}
+
class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
public:
EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
@@ -98,6 +122,14 @@
StringValue lastValue;
};
+class InvalidateKeyCallback : public OnEntryRemoved<KeyWithPointer, StringValue> {
+public:
+ void operator()(KeyWithPointer& k, StringValue&) {
+ delete k.ptr;
+ k.ptr = nullptr;
+ }
+};
+
class LruCacheTest : public testing::Test {
protected:
virtual void SetUp() {
@@ -221,7 +253,7 @@
cache.put(ComplexKey(0), ComplexValue(0));
cache.put(ComplexKey(1), ComplexValue(1));
EXPECT_EQ(2U, cache.size());
- assertInstanceCount(2, 3); // the null value counts as an instance
+ assertInstanceCount(2, 3); // the member mNullValue counts as an instance
}
TEST_F(LruCacheTest, Clear) {
@@ -293,4 +325,135 @@
EXPECT_EQ(3, callback.callbackCount);
}
+TEST_F(LruCacheTest, CallbackRemovesKeyWorksOK) {
+ LruCache<KeyWithPointer, StringValue> cache(1);
+ InvalidateKeyCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+ KeyWithPointer key1;
+ key1.ptr = new int(1);
+ KeyWithPointer key2;
+ key2.ptr = new int(2);
+
+ cache.put(key1, "one");
+ // As the size of the cache is 1, the put will call the callback.
+ // Make sure everything goes smoothly even if the callback invalidates
+ // the key (b/24785286)
+ cache.put(key2, "two");
+ EXPECT_EQ(1U, cache.size());
+ EXPECT_STREQ("two", cache.get(key2));
+ cache.clear();
+}
+
+TEST_F(LruCacheTest, IteratorCheck) {
+ LruCache<int, int> cache(100);
+
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+ EXPECT_EQ(3U, cache.size());
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ int v = it.value();
+ // Check we haven't seen the value before.
+ EXPECT_TRUE(returnedValues.find(v) == returnedValues.end());
+ returnedValues.insert(v);
+ }
+ EXPECT_EQ(std::unordered_set<int>({4, 5, 6}), returnedValues);
+}
+
+TEST_F(LruCacheTest, EmptyCacheIterator) {
+ // Check that nothing crashes...
+ LruCache<int, int> cache(100);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>(), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheIterator) {
+ // Check that nothing crashes...
+ LruCache<int, int> cache(100);
+ cache.put(1, 2);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 2 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheRemove) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 2);
+
+ cache.remove(1);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ }), returnedValues);
+}
+
+TEST_F(LruCacheTest, Remove) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+
+ cache.remove(2);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 4, 6 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveYoungest) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+
+ cache.remove(3);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 4, 5 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveNonMember) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+
+ cache.remove(7);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 4, 5, 6 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, DontCopyKeyInGet) {
+ LruCache<KeyFailsOnCopy, KeyFailsOnCopy> cache(1);
+ // Check that get doesn't copy the key
+ cache.get(KeyFailsOnCopy(0));
+}
+
}
diff --git a/libutils/tests/README.txt b/libutils/tests/README.txt
new file mode 100644
index 0000000..ad54e57
--- /dev/null
+++ b/libutils/tests/README.txt
@@ -0,0 +1,8 @@
+Run device tests:
+
+mma -j<whatever>
+(after adb root; adb disable-verity; adb reboot)
+adb root
+adb remount
+adb sync
+adb shell /data/nativetest/libutils_tests/libutils_tests
diff --git a/libutils/tests/RefBase_test.cpp b/libutils/tests/RefBase_test.cpp
new file mode 100644
index 0000000..2e0cf6e
--- /dev/null
+++ b/libutils/tests/RefBase_test.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+
+#include <thread>
+#include <atomic>
+#include <sched.h>
+#include <errno.h>
+
+// Enhanced version of StrongPointer_test, but using RefBase underneath.
+
+using namespace android;
+
+static constexpr int NITERS = 1000000;
+
+static constexpr int INITIAL_STRONG_VALUE = 1 << 28; // Mirroring RefBase definition.
+
+class Foo : public RefBase {
+public:
+ Foo(bool* deleted_check) : mDeleted(deleted_check) {
+ *mDeleted = false;
+ }
+
+ ~Foo() {
+ *mDeleted = true;
+ }
+private:
+ bool* mDeleted;
+};
+
+TEST(RefBase, StrongMoves) {
+ bool isDeleted;
+ Foo* foo = new Foo(&isDeleted);
+ ASSERT_EQ(INITIAL_STRONG_VALUE, foo->getStrongCount());
+ ASSERT_FALSE(isDeleted) << "Already deleted...?";
+ sp<Foo> sp1(foo);
+ wp<Foo> wp1(sp1);
+ ASSERT_EQ(1, foo->getStrongCount());
+ // Weak count includes both strong and weak references.
+ ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
+ {
+ sp<Foo> sp2 = std::move(sp1);
+ ASSERT_EQ(1, foo->getStrongCount())
+ << "std::move failed, incremented refcnt";
+ ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
+ // The strong count isn't increasing, let's double check the old object
+ // is properly reset and doesn't early delete
+ sp1 = std::move(sp2);
+ }
+ ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+ {
+ // Now let's double check it deletes on time
+ sp<Foo> sp2 = std::move(sp1);
+ }
+ ASSERT_TRUE(isDeleted) << "foo was leaked!";
+ ASSERT_TRUE(wp1.promote().get() == nullptr);
+}
+
+TEST(RefBase, WeakCopies) {
+ bool isDeleted;
+ Foo* foo = new Foo(&isDeleted);
+ EXPECT_EQ(0, foo->getWeakRefs()->getWeakCount());
+ ASSERT_FALSE(isDeleted) << "Foo (weak) already deleted...?";
+ wp<Foo> wp1(foo);
+ EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
+ {
+ wp<Foo> wp2 = wp1;
+ ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
+ }
+ EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
+ ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+ wp1 = nullptr;
+ ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur";
+}
+
+
+// Set up a situation in which we race with visit2AndRremove() to delete
+// 2 strong references. Bar destructor checks that there are no early
+// deletions and prior updates are visible to destructor.
+class Bar : public RefBase {
+public:
+ Bar(std::atomic<int>* delete_count) : mVisited1(false), mVisited2(false),
+ mDeleteCount(delete_count) {
+ }
+
+ ~Bar() {
+ EXPECT_TRUE(mVisited1);
+ EXPECT_TRUE(mVisited2);
+ (*mDeleteCount)++;
+ }
+ bool mVisited1;
+ bool mVisited2;
+private:
+ std::atomic<int>* mDeleteCount;
+};
+
+static sp<Bar> buffer;
+static std::atomic<bool> bufferFull(false);
+
+// Wait until bufferFull has value val.
+static inline void waitFor(bool val) {
+ while (bufferFull != val) {}
+}
+
+cpu_set_t otherCpus;
+
+// Divide the cpus we're allowed to run on into myCpus and otherCpus.
+// Set origCpus to the processors we were originally allowed to run on.
+// Return false if origCpus doesn't include at least processors 0 and 1.
+static bool setExclusiveCpus(cpu_set_t* origCpus /* out */,
+ cpu_set_t* myCpus /* out */, cpu_set_t* otherCpus) {
+ if (sched_getaffinity(0, sizeof(cpu_set_t), origCpus) != 0) {
+ return false;
+ }
+ if (!CPU_ISSET(0, origCpus) || !CPU_ISSET(1, origCpus)) {
+ return false;
+ }
+ CPU_ZERO(myCpus);
+ CPU_ZERO(otherCpus);
+ CPU_OR(myCpus, myCpus, origCpus);
+ CPU_OR(otherCpus, otherCpus, origCpus);
+ for (unsigned i = 0; i < CPU_SETSIZE; ++i) {
+ // I get the even cores, the other thread gets the odd ones.
+ if (i & 1) {
+ CPU_CLR(i, myCpus);
+ } else {
+ CPU_CLR(i, otherCpus);
+ }
+ }
+ return true;
+}
+
+static void visit2AndRemove() {
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {
+ FAIL() << "setaffinity returned:" << errno;
+ }
+ for (int i = 0; i < NITERS; ++i) {
+ waitFor(true);
+ buffer->mVisited2 = true;
+ buffer = nullptr;
+ bufferFull = false;
+ }
+}
+
+TEST(RefBase, RacingDestructors) {
+ cpu_set_t origCpus;
+ cpu_set_t myCpus;
+ // Restrict us and the helper thread to disjoint cpu sets.
+ // This prevents us from getting scheduled against each other,
+ // which would be atrociously slow.
+ if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {
+ std::thread t(visit2AndRemove);
+ std::atomic<int> deleteCount(0);
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {
+ FAIL() << "setaffinity returned:" << errno;
+ }
+ for (int i = 0; i < NITERS; ++i) {
+ waitFor(false);
+ Bar* bar = new Bar(&deleteCount);
+ sp<Bar> sp3(bar);
+ buffer = sp3;
+ bufferFull = true;
+ ASSERT_TRUE(bar->getStrongCount() >= 1);
+ // Weak count includes strong count.
+ ASSERT_TRUE(bar->getWeakRefs()->getWeakCount() >= 1);
+ sp3->mVisited1 = true;
+ sp3 = nullptr;
+ }
+ t.join();
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
+ FAIL();
+ }
+ ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!";
+ } // Otherwise this is slow and probably pointless on a uniprocessor.
+}
+
+static wp<Bar> wpBuffer;
+static std::atomic<bool> wpBufferFull(false);
+
+// Wait until wpBufferFull has value val.
+static inline void wpWaitFor(bool val) {
+ while (wpBufferFull != val) {}
+}
+
+static void visit3AndRemove() {
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {
+ FAIL() << "setaffinity returned:" << errno;
+ }
+ for (int i = 0; i < NITERS; ++i) {
+ wpWaitFor(true);
+ {
+ sp<Bar> sp1 = wpBuffer.promote();
+ // We implicitly check that sp1 != NULL
+ sp1->mVisited2 = true;
+ }
+ wpBuffer = nullptr;
+ wpBufferFull = false;
+ }
+}
+
+TEST(RefBase, RacingPromotions) {
+ cpu_set_t origCpus;
+ cpu_set_t myCpus;
+ // Restrict us and the helper thread to disjoint cpu sets.
+ // This prevents us from getting scheduled against each other,
+ // which would be atrociously slow.
+ if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {
+ std::thread t(visit3AndRemove);
+ std::atomic<int> deleteCount(0);
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {
+ FAIL() << "setaffinity returned:" << errno;
+ }
+ for (int i = 0; i < NITERS; ++i) {
+ Bar* bar = new Bar(&deleteCount);
+ wp<Bar> wp1(bar);
+ bar->mVisited1 = true;
+ if (i % (NITERS / 10) == 0) {
+ // Do this rarely, since it generates a log message.
+ wp1 = nullptr; // No longer destroys the object.
+ wp1 = bar;
+ }
+ wpBuffer = wp1;
+ ASSERT_EQ(bar->getWeakRefs()->getWeakCount(), 2);
+ wpBufferFull = true;
+ // Promotion races with that in visit3AndRemove.
+ // This may or may not succeed, but it shouldn't interfere with
+ // the concurrent one.
+ sp<Bar> sp1 = wp1.promote();
+ wpWaitFor(false); // Waits for other thread to drop strong pointer.
+ sp1 = nullptr;
+ // No strong pointers here.
+ sp1 = wp1.promote();
+ ASSERT_EQ(sp1.get(), nullptr) << "Dead wp promotion succeeded!";
+ }
+ t.join();
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
+ FAIL();
+ }
+ ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!";
+ } // Otherwise this is slow and probably pointless on a uniprocessor.
+}
diff --git a/libutils/tests/String8_test.cpp b/libutils/tests/String8_test.cpp
index c42c68d..3947a5f 100644
--- a/libutils/tests/String8_test.cpp
+++ b/libutils/tests/String8_test.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "String8_test"
#include <utils/Log.h>
#include <utils/String8.h>
+#include <utils/String16.h>
#include <gtest/gtest.h>
@@ -72,4 +73,27 @@
EXPECT_STREQ(src3, " Verify me.");
}
+TEST_F(String8Test, SetToSizeMaxReturnsNoMemory) {
+ const char *in = "some string";
+ EXPECT_EQ(NO_MEMORY, String8("").setTo(in, SIZE_MAX));
+}
+
+// http://b/29250543
+TEST_F(String8Test, CorrectInvalidSurrogate) {
+ // d841d8 is an invalid start for a surrogate pair. Make sure this is handled by ignoring the
+ // first character in the pair and handling the rest correctly.
+ String16 string16(u"\xd841\xd841\xdc41\x0000");
+ String8 string8(string16);
+
+ EXPECT_EQ(4U, string8.length());
+}
+
+TEST_F(String8Test, CheckUtf32Conversion) {
+ // Since bound checks were added, check the conversion can be done without fatal errors.
+ // The utf8 lengths of these are chars are 1 + 2 + 3 + 4 = 10.
+ const char32_t string32[] = U"\x0000007f\x000007ff\x0000911\x0010fffe";
+ String8 string8(string32);
+ EXPECT_EQ(10U, string8.length());
+}
+
}
diff --git a/libutils/tests/StrongPointer_test.cpp b/libutils/tests/StrongPointer_test.cpp
new file mode 100644
index 0000000..323a6f2
--- /dev/null
+++ b/libutils/tests/StrongPointer_test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+class Foo : public LightRefBase<Foo> {
+public:
+ explicit Foo(bool* deleted_check) : mDeleted(deleted_check) {
+ *mDeleted = false;
+ }
+
+ ~Foo() {
+ *mDeleted = true;
+ }
+private:
+ bool* mDeleted;
+};
+
+TEST(StrongPointer, move) {
+ bool isDeleted;
+ Foo* foo = new Foo(&isDeleted);
+ ASSERT_EQ(0, foo->getStrongCount());
+ ASSERT_FALSE(isDeleted) << "Already deleted...?";
+ sp<Foo> sp1(foo);
+ ASSERT_EQ(1, foo->getStrongCount());
+ {
+ sp<Foo> sp2 = std::move(sp1);
+ ASSERT_EQ(1, foo->getStrongCount()) << "std::move failed, incremented refcnt";
+ ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
+ // The strong count isn't increasing, let's double check the old object
+ // is properly reset and doesn't early delete
+ sp1 = std::move(sp2);
+ }
+ ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+ {
+ // Now let's double check it deletes on time
+ sp<Foo> sp2 = std::move(sp1);
+ }
+ ASSERT_TRUE(isDeleted) << "foo was leaked!";
+}
diff --git a/libutils/tests/SystemClock_test.cpp b/libutils/tests/SystemClock_test.cpp
new file mode 100644
index 0000000..5ad060b
--- /dev/null
+++ b/libutils/tests/SystemClock_test.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <utils/SystemClock.h>
+
+#include <gtest/gtest.h>
+
+static const auto MS_IN_NS = 1000000;
+
+static const int64_t SLEEP_MS = 500;
+static const int64_t SLEEP_NS = SLEEP_MS * MS_IN_NS;
+// Conservatively assume that we might be descheduled for up to 50 ms
+static const int64_t SLACK_MS = 50;
+static const int64_t SLACK_NS = SLACK_MS * MS_IN_NS;
+
+TEST(SystemClock, SystemClock) {
+ auto startUptimeMs = android::uptimeMillis();
+ auto startRealtimeMs = android::elapsedRealtime();
+ auto startRealtimeNs = android::elapsedRealtimeNano();
+
+ ASSERT_GT(startUptimeMs, 0)
+ << "uptimeMillis() reported an impossible uptime";
+ ASSERT_GE(startRealtimeMs, startUptimeMs)
+ << "elapsedRealtime() thinks we've suspended for negative time";
+ ASSERT_GE(startRealtimeNs, startUptimeMs * MS_IN_NS)
+ << "elapsedRealtimeNano() thinks we've suspended for negative time";
+
+ ASSERT_GE(startRealtimeNs, startRealtimeMs * MS_IN_NS)
+ << "elapsedRealtime() and elapsedRealtimeNano() are inconsistent";
+ ASSERT_LT(startRealtimeNs, (startRealtimeMs + SLACK_MS) * MS_IN_NS)
+ << "elapsedRealtime() and elapsedRealtimeNano() are inconsistent";
+
+ timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = SLEEP_MS * MS_IN_NS;
+ auto nanosleepErr = TEMP_FAILURE_RETRY(nanosleep(&ts, nullptr));
+ ASSERT_EQ(nanosleepErr, 0) << "nanosleep() failed: " << strerror(errno);
+
+ auto endUptimeMs = android::uptimeMillis();
+ auto endRealtimeMs = android::elapsedRealtime();
+ auto endRealtimeNs = android::elapsedRealtimeNano();
+
+ EXPECT_GE(endUptimeMs - startUptimeMs, SLEEP_MS)
+ << "uptimeMillis() advanced too little after nanosleep()";
+ EXPECT_LT(endUptimeMs - startUptimeMs, SLEEP_MS + SLACK_MS)
+ << "uptimeMillis() advanced too much after nanosleep()";
+ EXPECT_GE(endRealtimeMs - startRealtimeMs, SLEEP_MS)
+ << "elapsedRealtime() advanced too little after nanosleep()";
+ EXPECT_LT(endRealtimeMs - startRealtimeMs, SLEEP_MS + SLACK_MS)
+ << "elapsedRealtime() advanced too much after nanosleep()";
+ EXPECT_GE(endRealtimeNs - startRealtimeNs, SLEEP_NS)
+ << "elapsedRealtimeNano() advanced too little after nanosleep()";
+ EXPECT_LT(endRealtimeNs - startRealtimeNs, SLEEP_NS + SLACK_NS)
+ << "elapsedRealtimeNano() advanced too much after nanosleep()";
+
+ EXPECT_GE(endRealtimeNs, endRealtimeMs * MS_IN_NS)
+ << "elapsedRealtime() and elapsedRealtimeNano() are inconsistent after nanosleep()";
+ EXPECT_LT(endRealtimeNs, (endRealtimeMs + SLACK_MS) * MS_IN_NS)
+ << "elapsedRealtime() and elapsedRealtimeNano() are inconsistent after nanosleep()";
+}
diff --git a/libutils/tests/TestHelpers.h b/libutils/tests/TestHelpers.h
index d8e985e..6801cd7 100644
--- a/libutils/tests/TestHelpers.h
+++ b/libutils/tests/TestHelpers.h
@@ -60,7 +60,7 @@
int mDelayMillis;
public:
- DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
+ explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
protected:
virtual ~DelayedTask() { }
diff --git a/libutils/tests/Unicode_test.cpp b/libutils/tests/Unicode_test.cpp
index 18c130c..d23e43a 100644
--- a/libutils/tests/Unicode_test.cpp
+++ b/libutils/tests/Unicode_test.cpp
@@ -29,6 +29,8 @@
virtual void TearDown() {
}
+
+ char16_t const * const kSearchString = u"I am a leaf on the wind.";
};
TEST_F(UnicodeTest, UTF8toUTF16ZeroLength) {
@@ -96,7 +98,7 @@
char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL
- utf8_to_utf16(str, sizeof(str), output);
+ utf8_to_utf16(str, sizeof(str), output, sizeof(output) / sizeof(output[0]));
EXPECT_EQ(0x0030, output[0])
<< "should be U+0030";
@@ -112,4 +114,48 @@
<< "should be NULL terminated";
}
+TEST_F(UnicodeTest, strstr16EmptyTarget) {
+ EXPECT_EQ(strstr16(kSearchString, u""), kSearchString)
+ << "should return the original pointer";
+}
+
+TEST_F(UnicodeTest, strstr16SameString) {
+ const char16_t* result = strstr16(kSearchString, kSearchString);
+ EXPECT_EQ(kSearchString, result)
+ << "should return the original pointer";
+}
+
+TEST_F(UnicodeTest, strstr16TargetStartOfString) {
+ const char16_t* result = strstr16(kSearchString, u"I am");
+ EXPECT_EQ(kSearchString, result)
+ << "should return the original pointer";
+}
+
+
+TEST_F(UnicodeTest, strstr16TargetEndOfString) {
+ const char16_t* result = strstr16(kSearchString, u"wind.");
+ EXPECT_EQ(kSearchString+19, result);
+}
+
+TEST_F(UnicodeTest, strstr16TargetWithinString) {
+ const char16_t* result = strstr16(kSearchString, u"leaf");
+ EXPECT_EQ(kSearchString+7, result);
+}
+
+TEST_F(UnicodeTest, strstr16TargetNotPresent) {
+ const char16_t* result = strstr16(kSearchString, u"soar");
+ EXPECT_EQ(nullptr, result);
+}
+
+// http://b/29267949
+// Test that overreading in utf8_to_utf16_length is detected
+TEST_F(UnicodeTest, InvalidUtf8OverreadDetected) {
+ // An utf8 char starting with \xc4 is two bytes long.
+ // Add extra zeros so no extra memory is read in case the code doesn't
+ // work as expected.
+ static char utf8[] = "\xc4\x00\x00\x00";
+ ASSERT_DEATH(utf8_to_utf16_length((uint8_t *) utf8, strlen(utf8),
+ true /* overreadIsFatal */), "" /* regex for ASSERT_DEATH */);
+}
+
}
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index 0ba7161..671200f 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -16,11 +16,14 @@
#define LOG_TAG "Vector_test"
-#include <utils/Vector.h>
-#include <cutils/log.h>
-#include <gtest/gtest.h>
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
#include <unistd.h>
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <utils/Vector.h>
+
namespace android {
class VectorTest : public testing::Test {
@@ -71,5 +74,80 @@
EXPECT_EQ(other[3], 5);
}
+// TODO: gtest isn't capable of parsing Abort messages formatted by
+// Android (fails differently on host and target), so we always need to
+// use an empty error message for death tests.
+TEST_F(VectorTest, SetCapacity_Overflow) {
+ Vector<int> vector;
+ EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), "");
+}
+
+TEST_F(VectorTest, SetCapacity_ShrinkBelowSize) {
+ Vector<int> vector;
+ vector.add(1);
+ vector.add(2);
+ vector.add(3);
+ vector.add(4);
+
+ vector.setCapacity(8);
+ ASSERT_EQ(8U, vector.capacity());
+ vector.setCapacity(2);
+ ASSERT_EQ(8U, vector.capacity());
+}
+
+// NOTE: All of the tests below are useless because of the "TODO" above.
+// We have no way of knowing *why* the process crashed. Given that we're
+// inserting a NULL array, we'll fail with a SIGSEGV eventually. We need
+// the ability to make assertions on the abort message to make sure we're
+// failing for the right reasons.
+TEST_F(VectorTest, _grow_OverflowSize) {
+ Vector<int> vector;
+ vector.add(1);
+
+ // Checks that the size calculation (not the capacity calculation) doesn't
+ // overflow : the size here will be (1 + SIZE_MAX).
+ //
+ // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size_overflow");
+ EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "");
+}
+
+TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
+ Vector<int> vector;
+
+ // This should fail because the calculated capacity will overflow even though
+ // the size of the vector doesn't.
+ //
+ // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity_overflow");
+ EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "");
+}
+
+TEST_F(VectorTest, _grow_OverflowBufferAlloc) {
+ Vector<int> vector;
+ // This should fail because the capacity * sizeof(int) overflows, even
+ // though the capacity itself doesn't.
+ //
+ // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
+ EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "");
+}
+
+TEST_F(VectorTest, editArray_Shared) {
+ Vector<int> vector1;
+ vector1.add(1);
+ vector1.add(2);
+ vector1.add(3);
+ vector1.add(4);
+
+ Vector<int> vector2 = vector1;
+ ASSERT_EQ(vector1.array(), vector2.array());
+ // We must make a copy here, since we're not the exclusive owners
+ // of this array.
+ ASSERT_NE(vector1.editArray(), vector2.editArray());
+
+ // Vector doesn't implement operator ==.
+ ASSERT_EQ(vector1.size(), vector2.size());
+ for (size_t i = 0; i < vector1.size(); ++i) {
+ EXPECT_EQ(vector1[i], vector2[i]);
+ }
+}
} // namespace android
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
new file mode 100644
index 0000000..44daf36
--- /dev/null
+++ b/libziparchive/Android.bp
@@ -0,0 +1,124 @@
+//
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+ name: "libziparchive_flags",
+ cflags: [
+ // ZLIB_CONST turns on const for input buffers, which is pretty standard.
+ "-DZLIB_CONST",
+ "-Werror",
+ "-Wall",
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ cppflags: [
+ "-Wold-style-cast",
+ // Incorrectly warns when C++11 empty brace {} initializer is used.
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
+ "-Wno-missing-field-initializers",
+ ],
+}
+
+cc_defaults {
+ name: "libziparchive_defaults",
+ srcs: [
+ "zip_archive.cc",
+ "zip_archive_stream_entry.cc",
+ "zip_writer.cc",
+ ],
+
+ target: {
+ windows: {
+ cflags: ["-mno-ms-bitfields"],
+
+ enabled: true,
+ },
+ },
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+}
+
+
+cc_library {
+ name: "libziparchive",
+ host_supported: true,
+ defaults: ["libziparchive_defaults", "libziparchive_flags"],
+ shared_libs: ["liblog", "libbase"],
+ target: {
+ android: {
+ shared_libs: ["libz", "libutils"],
+ },
+ host: {
+ static_libs: ["libutils"],
+ },
+ linux_bionic: {
+ static_libs: ["libz"],
+ enabled: true,
+ },
+ linux: {
+ shared_libs: ["libz-host"],
+ },
+ darwin: {
+ shared_libs: ["libz-host"],
+ },
+ windows: {
+ shared_libs: ["libz-host"],
+ },
+ },
+}
+
+// Also provide libziparchive-host until everything is switched over to using libziparchive
+cc_library {
+ name: "libziparchive-host",
+ host_supported: true,
+ device_supported: false,
+ defaults: ["libziparchive_defaults", "libziparchive_flags"],
+ shared_libs: ["libz-host"],
+ static_libs: ["libutils"],
+}
+
+// Tests.
+cc_test {
+ name: "ziparchive-tests",
+ host_supported: true,
+ defaults: ["libziparchive_flags"],
+
+ srcs: [
+ "entry_name_utils_test.cc",
+ "zip_archive_test.cc",
+ "zip_writer_test.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libziparchive",
+ "libz",
+ "libutils",
+ ],
+
+ target: {
+ host: {
+ cppflags: ["-Wno-unnamed-type-template-args"],
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
deleted file mode 100644
index 559c48b..0000000
--- a/libziparchive/Android.mk
+++ /dev/null
@@ -1,73 +0,0 @@
-#
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-source_files := zip_archive.cc
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := ${source_files}
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_SHARED_LIBRARIES := libutils libbase
-LOCAL_MODULE:= libziparchive
-LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CPPFLAGS := -Wold-style-cast
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := ${source_files}
-LOCAL_STATIC_LIBRARIES := libz libutils libbase
-LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
-ifneq ($(strip $(USE_MINGW)),)
- LOCAL_CFLAGS += -mno-ms-bitfields
-endif
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := ${source_files}
-LOCAL_STATIC_LIBRARIES := libutils
-LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
-LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-# Tests.
-include $(CLEAR_VARS)
-LOCAL_MODULE := ziparchive-tests
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
-LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
-LOCAL_SHARED_LIBRARIES := liblog libbase
-LOCAL_STATIC_LIBRARIES := libziparchive libz libutils
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := ziparchive-tests-host
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += \
- -Werror \
- -Wno-unnamed-type-template-args
-LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
-LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
-LOCAL_STATIC_LIBRARIES := \
- libz \
- libutils
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libziparchive/testdata/bad_crc.zip b/libziparchive/testdata/bad_crc.zip
new file mode 100644
index 0000000..e12ba07
--- /dev/null
+++ b/libziparchive/testdata/bad_crc.zip
Binary files differ
diff --git a/libziparchive/testdata/dummy-update.zip b/libziparchive/testdata/dummy-update.zip
new file mode 100644
index 0000000..6976bf1
--- /dev/null
+++ b/libziparchive/testdata/dummy-update.zip
Binary files differ
diff --git a/libziparchive/testdata/large.zip b/libziparchive/testdata/large.zip
new file mode 100644
index 0000000..49659c8
--- /dev/null
+++ b/libziparchive/testdata/large.zip
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 3716343..0ac6f2c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -18,6 +18,8 @@
* Read-only access to Zip archives, with minimal heap allocation.
*/
+#define LOG_TAG "ziparchive"
+
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
@@ -30,16 +32,19 @@
#include <memory>
#include <vector>
-#include "base/file.h"
-#include "base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd
-#include "base/memory.h"
-#include "log/log.h"
-#include "utils/Compat.h"
-#include "utils/FileMap.h"
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h> // TEMP_FAILURE_RETRY may or may not be in unistd
+#include <android-base/memory.h>
+#include <log/log.h>
+#include <utils/Compat.h>
+#include <utils/FileMap.h>
+#include "ziparchive/zip_archive.h"
#include "zlib.h"
#include "entry_name_utils-inl.h"
-#include "ziparchive/zip_archive.h"
+#include "zip_archive_common.h"
+#include "zip_archive_private.h"
using android::base::get_unaligned;
@@ -49,161 +54,6 @@
#define O_BINARY 0
#endif
-// The "end of central directory" (EOCD) record. Each archive
-// contains exactly once such record which appears at the end of
-// the archive. It contains archive wide information like the
-// number of entries in the archive and the offset to the central
-// directory of the offset.
-struct EocdRecord {
- static const uint32_t kSignature = 0x06054b50;
-
- // End of central directory signature, should always be
- // |kSignature|.
- uint32_t eocd_signature;
- // The number of the current "disk", i.e, the "disk" that this
- // central directory is on.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that disk_num == 1.
- uint16_t disk_num;
- // The disk where the central directory starts.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that cd_start_disk == 1.
- uint16_t cd_start_disk;
- // The number of central directory records on this disk.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that num_records_on_disk == num_records.
- uint16_t num_records_on_disk;
- // The total number of central directory records.
- uint16_t num_records;
- // The size of the central directory (in bytes).
- uint32_t cd_size;
- // The offset of the start of the central directory, relative
- // to the start of the file.
- uint32_t cd_start_offset;
- // Length of the central directory comment.
- uint16_t comment_length;
- private:
- EocdRecord() = default;
- DISALLOW_COPY_AND_ASSIGN(EocdRecord);
-} __attribute__((packed));
-
-// A structure representing the fixed length fields for a single
-// record in the central directory of the archive. In addition to
-// the fixed length fields listed here, each central directory
-// record contains a variable length "file_name" and "extra_field"
-// whose lengths are given by |file_name_length| and |extra_field_length|
-// respectively.
-struct CentralDirectoryRecord {
- static const uint32_t kSignature = 0x02014b50;
-
- // The start of record signature. Must be |kSignature|.
- uint32_t record_signature;
- // Tool version. Ignored by this implementation.
- uint16_t version_made_by;
- // Tool version. Ignored by this implementation.
- uint16_t version_needed;
- // The "general purpose bit flags" for this entry. The only
- // flag value that we currently check for is the "data descriptor"
- // flag.
- uint16_t gpb_flags;
- // The compression method for this entry, one of |kCompressStored|
- // and |kCompressDeflated|.
- uint16_t compression_method;
- // The file modification time and date for this entry.
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- // The CRC-32 checksum for this entry.
- uint32_t crc32;
- // The compressed size (in bytes) of this entry.
- uint32_t compressed_size;
- // The uncompressed size (in bytes) of this entry.
- uint32_t uncompressed_size;
- // The length of the entry file name in bytes. The file name
- // will appear immediately after this record.
- uint16_t file_name_length;
- // The length of the extra field info (in bytes). This data
- // will appear immediately after the entry file name.
- uint16_t extra_field_length;
- // The length of the entry comment (in bytes). This data will
- // appear immediately after the extra field.
- uint16_t comment_length;
- // The start disk for this entry. Ignored by this implementation).
- uint16_t file_start_disk;
- // File attributes. Ignored by this implementation.
- uint16_t internal_file_attributes;
- // File attributes. Ignored by this implementation.
- uint32_t external_file_attributes;
- // The offset to the local file header for this entry, from the
- // beginning of this archive.
- uint32_t local_file_header_offset;
- private:
- CentralDirectoryRecord() = default;
- DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
-} __attribute__((packed));
-
-// The local file header for a given entry. This duplicates information
-// present in the central directory of the archive. It is an error for
-// the information here to be different from the central directory
-// information for a given entry.
-struct LocalFileHeader {
- static const uint32_t kSignature = 0x04034b50;
-
- // The local file header signature, must be |kSignature|.
- uint32_t lfh_signature;
- // Tool version. Ignored by this implementation.
- uint16_t version_needed;
- // The "general purpose bit flags" for this entry. The only
- // flag value that we currently check for is the "data descriptor"
- // flag.
- uint16_t gpb_flags;
- // The compression method for this entry, one of |kCompressStored|
- // and |kCompressDeflated|.
- uint16_t compression_method;
- // The file modification time and date for this entry.
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- // The CRC-32 checksum for this entry.
- uint32_t crc32;
- // The compressed size (in bytes) of this entry.
- uint32_t compressed_size;
- // The uncompressed size (in bytes) of this entry.
- uint32_t uncompressed_size;
- // The length of the entry file name in bytes. The file name
- // will appear immediately after this record.
- uint16_t file_name_length;
- // The length of the extra field info (in bytes). This data
- // will appear immediately after the entry file name.
- uint16_t extra_field_length;
- private:
- LocalFileHeader() = default;
- DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
-} __attribute__((packed));
-
-struct DataDescriptor {
- // The *optional* data descriptor start signature.
- static const uint32_t kOptSignature = 0x08074b50;
-
- // CRC-32 checksum of the entry.
- uint32_t crc32;
- // Compressed size of the entry.
- uint32_t compressed_size;
- // Uncompressed size of the entry.
- uint32_t uncompressed_size;
- private:
- DataDescriptor() = default;
- DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
-} __attribute__((packed));
-
-
-static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD
-
-// The maximum size of a central directory or a file
-// comment in bytes.
-static const uint32_t kMaxCommentLen = 65535;
-
// The maximum number of bytes to scan backwards for the EOCD start.
static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
@@ -288,43 +138,6 @@
* every page that the Central Directory touches. Easier to tuck a copy
* of the string length into the hash table entry.
*/
-struct ZipArchive {
- /* open Zip archive */
- const int fd;
- const bool close_file;
-
- /* mapped central directory area */
- off64_t directory_offset;
- android::FileMap directory_map;
-
- /* number of entries in the Zip archive */
- uint16_t num_entries;
-
- /*
- * We know how many entries are in the Zip archive, so we can have a
- * fixed-size hash table. We define a load factor of 0.75 and overallocat
- * so the maximum number entries can never be higher than
- * ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
- */
- uint32_t hash_table_size;
- ZipString* hash_table;
-
- ZipArchive(const int fd, bool assume_ownership) :
- fd(fd),
- close_file(assume_ownership),
- directory_offset(0),
- num_entries(0),
- hash_table_size(0),
- hash_table(NULL) {}
-
- ~ZipArchive() {
- if (close_file && fd >= 0) {
- close(fd);
- }
-
- free(hash_table);
- }
-};
/*
* Round up to the next highest power of 2.
@@ -404,21 +217,14 @@
return 0;
}
-static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
- ZipArchive* archive, off64_t file_length,
- off64_t read_amount, uint8_t* scan_buffer) {
+static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
+ off64_t file_length, off64_t read_amount,
+ uint8_t* scan_buffer) {
const off64_t search_start = file_length - read_amount;
- if (lseek64(fd, search_start, SEEK_SET) != search_start) {
- ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start),
- strerror(errno));
- return kIoError;
- }
- ssize_t actual = TEMP_FAILURE_RETRY(
- read(fd, scan_buffer, static_cast<size_t>(read_amount)));
- if (actual != static_cast<ssize_t>(read_amount)) {
- ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
- strerror(errno));
+ if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
+ ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
+ static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
return kIoError;
}
@@ -461,9 +267,14 @@
* Grab the CD offset and size, and the number of entries in the
* archive and verify that they look reasonable.
*/
- if (eocd->cd_start_offset + eocd->cd_size > eocd_offset) {
+ if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
+#if defined(__ANDROID__)
+ if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
+ android_errorWriteLog(0x534e4554, "31251826");
+ }
+#endif
return kInvalidOffset;
}
if (eocd->num_records == 0) {
@@ -478,9 +289,11 @@
* It all looks good. Create a mapping for the CD, and set the fields
* in archive.
*/
- if (!archive->directory_map.create(debug_file_name, fd,
- static_cast<off64_t>(eocd->cd_start_offset),
- static_cast<size_t>(eocd->cd_size), true /* read only */) ) {
+
+ if (!archive->InitializeCentralDirectory(debug_file_name,
+ static_cast<off64_t>(eocd->cd_start_offset),
+ static_cast<size_t>(eocd->cd_size))) {
+ ALOGE("Zip: failed to intialize central directory.\n");
return kMmapFailed;
}
@@ -495,18 +308,16 @@
*
* On success, returns 0 after populating fields from the EOCD area:
* directory_offset
- * directory_map
+ * directory_ptr
* num_entries
*/
-static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
- ZipArchive* archive) {
+static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
// Test file length. We use lseek64 to make sure the file
// is small enough to be a zip file (Its size must be less than
// 0xffffffff bytes).
- off64_t file_length = lseek64(fd, 0, SEEK_END);
+ off64_t file_length = archive->mapped_zip.GetFileLength();
if (file_length == -1) {
- ALOGV("Zip: lseek on fd %d failed", fd);
return kInvalidFile;
}
@@ -537,11 +348,9 @@
read_amount = file_length;
}
- uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount));
- int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
- file_length, read_amount, scan_buffer);
-
- free(scan_buffer);
+ std::vector<uint8_t> scan_buffer(read_amount);
+ int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
+ scan_buffer.data());
return result;
}
@@ -552,9 +361,8 @@
* Returns 0 on success.
*/
static int32_t ParseZipArchive(ZipArchive* archive) {
- const uint8_t* const cd_ptr =
- reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr());
- const size_t cd_length = archive->directory_map.getDataLength();
+ const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
+ const size_t cd_length = archive->central_directory.GetMapLength();
const uint16_t num_entries = archive->num_entries;
/*
@@ -628,7 +436,7 @@
static int32_t OpenArchiveInternal(ZipArchive* archive,
const char* debug_file_name) {
int32_t result = -1;
- if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
+ if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
return result;
}
@@ -659,6 +467,13 @@
return OpenArchiveInternal(archive, fileName);
}
+int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
+ ZipArchiveHandle *handle) {
+ ZipArchive* archive = new ZipArchive(address, length);
+ *handle = archive;
+ return OpenArchiveInternal(archive, debug_file_name);
+}
+
/*
* Close a ZipArchive, closing the file and freeing the contents.
*/
@@ -668,11 +483,10 @@
delete archive;
}
-static int32_t UpdateEntryFromDataDescriptor(int fd,
+static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
ZipEntry *entry) {
uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
- ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf)));
- if (actual != sizeof(ddBuf)) {
+ if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
return kIoError;
}
@@ -687,29 +501,6 @@
return 0;
}
-// Attempts to read |len| bytes into |buf| at offset |off|.
-//
-// This method uses pread64 on platforms that support it and
-// lseek64 + read on platforms that don't. This implies that
-// callers should not rely on the |fd| offset being incremented
-// as a side effect of this call.
-static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len,
- off64_t off) {
-#if !defined(_WIN32)
- return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
-#else
- // The only supported platform that doesn't support pread at the moment
- // is Windows. Only recent versions of windows support unix like forks,
- // and even there the semantics are quite different.
- if (lseek64(fd, off, SEEK_SET) != off) {
- ALOGW("Zip: failed seek to offset %" PRId64, off);
- return kIoError;
- }
-
- return TEMP_FAILURE_RETRY(read(fd, buf, len));
-#endif
-}
-
static int32_t FindEntry(const ZipArchive* archive, const int ent,
ZipEntry* data) {
const uint16_t nameLen = archive->hash_table[ent].name_length;
@@ -723,9 +514,8 @@
// This is the base of our mmapped region, we have to sanity check that
// the name that's in the hash table is a pointer to a location within
// this mapped region.
- const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
- archive->directory_map.getDataPtr());
- if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
+ const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
+ if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
ALOGW("Zip: Invalid entry pointer");
return kInvalidOffset;
}
@@ -742,7 +532,7 @@
// and other interesting attributes from the central directory. These
// will later be compared against values from the local file header.
data->method = cdr->compression_method;
- data->mod_time = cdr->last_mod_time;
+ data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
data->crc32 = cdr->crc32;
data->compressed_length = cdr->compressed_size;
data->uncompressed_length = cdr->uncompressed_size;
@@ -757,9 +547,7 @@
}
uint8_t lfh_buf[sizeof(LocalFileHeader)];
- ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf),
- local_header_offset);
- if (actual != sizeof(lfh_buf)) {
+ if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64,
static_cast<int64_t>(local_header_offset));
return kIoError;
@@ -799,22 +587,16 @@
return kInvalidOffset;
}
- uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
- ssize_t actual = ReadAtOffset(archive->fd, name_buf, nameLen,
- name_offset);
-
- if (actual != nameLen) {
+ std::vector<uint8_t> name_buf(nameLen);
+ if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
- free(name_buf);
return kIoError;
}
- if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
- free(name_buf);
+ if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
return kInconsistentInformation;
}
- free(name_buf);
} else {
ALOGW("Zip: lfh name did not match central directory.");
return kInconsistentInformation;
@@ -1004,7 +786,8 @@
// Creates a FileWriter for |fd| and prepare to write |entry| to it,
// guaranteeing that the file descriptor is valid and that there's enough
// space on the volume to write out the entry completely and that the file
- // is truncated to the correct length.
+ // is truncated to the correct length (no truncation if |fd| references a
+ // block device).
//
// Returns a valid FileWriter on success, |nullptr| if an error occurred.
static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
@@ -1029,20 +812,30 @@
// disk does not have enough space.
result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
if (result == -1 && errno == ENOSPC) {
- ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
- static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+ ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 " : %s",
+ static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
+ strerror(errno));
return std::unique_ptr<FileWriter>(nullptr);
}
}
#endif // __linux__
- result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
- if (result == -1) {
- ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
- static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ ALOGW("Zip: unable to fstat file: %s", strerror(errno));
return std::unique_ptr<FileWriter>(nullptr);
}
+ // Block device doesn't support ftruncate(2).
+ if (!S_ISBLK(sb.st_mode)) {
+ result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+ if (result == -1) {
+ ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
+ static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+ return std::unique_ptr<FileWriter>(nullptr);
+ }
+ }
+
return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
}
@@ -1083,7 +876,7 @@
}
#pragma GCC diagnostic pop
-static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
+static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
Writer* writer, uint64_t* crc_out) {
const size_t kBufSize = 32768;
std::vector<uint8_t> read_buf(kBufSize);
@@ -1132,10 +925,9 @@
do {
/* read as much as we can */
if (zstream.avail_in == 0) {
- const ZD_TYPE getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
- const ZD_TYPE actual = TEMP_FAILURE_RETRY(read(fd, &read_buf[0], getSize));
- if (actual != getSize) {
- ALOGW("Zip: inflate read failed (" ZD " vs " ZD ")", actual, getSize);
+ const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
+ if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
+ ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
return kIoError;
}
@@ -1182,7 +974,7 @@
return 0;
}
-static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
+static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
uint64_t *crc_out) {
static const uint32_t kBufSize = 32768;
std::vector<uint8_t> buf(kBufSize);
@@ -1195,11 +987,9 @@
// Safe conversion because kBufSize is narrow enough for a 32 bit signed
// value.
- const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
- const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
-
- if (actual != block_size) {
- ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
+ const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+ if (!mapped_zip.ReadData(buf.data(), block_size)) {
+ ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
return kIoError;
}
@@ -1221,7 +1011,7 @@
const uint16_t method = entry->method;
off64_t data_offset = entry->offset;
- if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
+ if (!archive->mapped_zip.SeekToOffset(data_offset)) {
ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
return kIoError;
}
@@ -1230,13 +1020,13 @@
int32_t return_value = -1;
uint64_t crc = 0;
if (method == kCompressStored) {
- return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
+ return_value = CopyEntryToWriter(archive->mapped_zip, entry, writer, &crc);
} else if (method == kCompressDeflated) {
- return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
+ return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer, &crc);
}
if (!return_value && entry->has_data_descriptor) {
- return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
+ return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
if (return_value) {
return return_value;
}
@@ -1277,5 +1067,152 @@
}
int GetFileDescriptor(const ZipArchiveHandle handle) {
- return reinterpret_cast<ZipArchive*>(handle)->fd;
+ return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
+}
+
+ZipString::ZipString(const char* entry_name)
+ : name(reinterpret_cast<const uint8_t*>(entry_name)) {
+ size_t len = strlen(entry_name);
+ CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
+ name_length = static_cast<uint16_t>(len);
+}
+
+#if !defined(_WIN32)
+class ProcessWriter : public Writer {
+ public:
+ ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
+ proc_function_(func),
+ cookie_(cookie) {
+ }
+
+ virtual bool Append(uint8_t* buf, size_t buf_size) override {
+ return proc_function_(buf, buf_size, cookie_);
+ }
+
+ private:
+ ProcessZipEntryFunction proc_function_;
+ void* cookie_;
+};
+
+int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+ ProcessZipEntryFunction func, void* cookie) {
+ ProcessWriter writer(func, cookie);
+ return ExtractToWriter(handle, entry, &writer);
+}
+
+#endif //!defined(_WIN32)
+
+int MappedZipFile::GetFileDescriptor() const {
+ if (!has_fd_) {
+ ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
+ return -1;
+ }
+ return fd_;
+}
+
+void* MappedZipFile::GetBasePtr() const {
+ if (has_fd_) {
+ ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
+ return nullptr;
+ }
+ return base_ptr_;
+}
+
+off64_t MappedZipFile::GetFileLength() const {
+ if (has_fd_) {
+ off64_t result = lseek64(fd_, 0, SEEK_END);
+ if (result == -1) {
+ ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
+ }
+ return result;
+ } else {
+ if (base_ptr_ == nullptr) {
+ ALOGE("Zip: invalid file map\n");
+ return -1;
+ }
+ return static_cast<off64_t>(data_length_);
+ }
+}
+
+bool MappedZipFile::SeekToOffset(off64_t offset) {
+ if (has_fd_) {
+ if (lseek64(fd_, offset, SEEK_SET) != offset) {
+ ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
+ return false;
+ }
+ return true;
+ } else {
+ if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
+ ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset,
+ data_length_);
+ return false;
+ }
+
+ read_pos_ = offset;
+ return true;
+ }
+}
+
+bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
+ if (has_fd_) {
+ if(!android::base::ReadFully(fd_, buffer, read_amount)) {
+ ALOGE("Zip: read from %d failed\n", fd_);
+ return false;
+ }
+ } else {
+ memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
+ read_pos_ += read_amount;
+ }
+ return true;
+}
+
+// Attempts to read |len| bytes into |buf| at offset |off|.
+bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
+#if !defined(_WIN32)
+ if (has_fd_) {
+ if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
+ ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
+ return false;
+ }
+ return true;
+ }
+#endif
+ if (!SeekToOffset(off)) {
+ return false;
+ }
+ return ReadData(buf, len);
+
+}
+
+void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
+ base_ptr_ = static_cast<uint8_t*>(map_base_ptr) + cd_start_offset;
+ length_ = cd_size;
+}
+
+bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
+ size_t cd_size) {
+ if (mapped_zip.HasFd()) {
+ if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
+ cd_start_offset, cd_size, true /* read only */)) {
+ return false;
+ }
+
+ CHECK_EQ(directory_map->getDataLength(), cd_size);
+ central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
+ } else {
+ if (mapped_zip.GetBasePtr() == nullptr) {
+ ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
+ return false;
+ }
+ if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
+ mapped_zip.GetFileLength()) {
+ ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
+ "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
+ static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
+ return false;
+ }
+
+ central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
+ }
+ return true;
}
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
new file mode 100644
index 0000000..ca42509
--- /dev/null
+++ b/libziparchive/zip_archive_common.h
@@ -0,0 +1,179 @@
+/*
+ * 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 LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+
+#include "android-base/macros.h"
+
+#include <inttypes.h>
+
+// The "end of central directory" (EOCD) record. Each archive
+// contains exactly once such record which appears at the end of
+// the archive. It contains archive wide information like the
+// number of entries in the archive and the offset to the central
+// directory of the offset.
+struct EocdRecord {
+ static const uint32_t kSignature = 0x06054b50;
+
+ // End of central directory signature, should always be
+ // |kSignature|.
+ uint32_t eocd_signature;
+ // The number of the current "disk", i.e, the "disk" that this
+ // central directory is on.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that disk_num == 1.
+ uint16_t disk_num;
+ // The disk where the central directory starts.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that cd_start_disk == 1.
+ uint16_t cd_start_disk;
+ // The number of central directory records on this disk.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that num_records_on_disk == num_records.
+ uint16_t num_records_on_disk;
+ // The total number of central directory records.
+ uint16_t num_records;
+ // The size of the central directory (in bytes).
+ uint32_t cd_size;
+ // The offset of the start of the central directory, relative
+ // to the start of the file.
+ uint32_t cd_start_offset;
+ // Length of the central directory comment.
+ uint16_t comment_length;
+ private:
+ EocdRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(EocdRecord);
+} __attribute__((packed));
+
+// A structure representing the fixed length fields for a single
+// record in the central directory of the archive. In addition to
+// the fixed length fields listed here, each central directory
+// record contains a variable length "file_name" and "extra_field"
+// whose lengths are given by |file_name_length| and |extra_field_length|
+// respectively.
+struct CentralDirectoryRecord {
+ static const uint32_t kSignature = 0x02014b50;
+
+ // The start of record signature. Must be |kSignature|.
+ uint32_t record_signature;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_made_by;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+ // The length of the entry comment (in bytes). This data will
+ // appear immediately after the extra field.
+ uint16_t comment_length;
+ // The start disk for this entry. Ignored by this implementation).
+ uint16_t file_start_disk;
+ // File attributes. Ignored by this implementation.
+ uint16_t internal_file_attributes;
+ // File attributes. Ignored by this implementation.
+ uint32_t external_file_attributes;
+ // The offset to the local file header for this entry, from the
+ // beginning of this archive.
+ uint32_t local_file_header_offset;
+ private:
+ CentralDirectoryRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
+} __attribute__((packed));
+
+// The local file header for a given entry. This duplicates information
+// present in the central directory of the archive. It is an error for
+// the information here to be different from the central directory
+// information for a given entry.
+struct LocalFileHeader {
+ static const uint32_t kSignature = 0x04034b50;
+
+ // The local file header signature, must be |kSignature|.
+ uint32_t lfh_signature;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+ private:
+ LocalFileHeader() = default;
+ DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
+} __attribute__((packed));
+
+struct DataDescriptor {
+ // The *optional* data descriptor start signature.
+ static const uint32_t kOptSignature = 0x08074b50;
+
+ // CRC-32 checksum of the entry.
+ uint32_t crc32;
+ // Compressed size of the entry.
+ uint32_t compressed_size;
+ // Uncompressed size of the entry.
+ uint32_t uncompressed_size;
+ private:
+ DataDescriptor() = default;
+ DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
+} __attribute__((packed));
+
+// mask value that signifies that the entry has a DD
+static const uint32_t kGPBDDFlagMask = 0x0008;
+
+// The maximum size of a central directory or a file
+// comment in bytes.
+static const uint32_t kMaxCommentLen = 65535;
+
+#endif /* LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_ */
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
new file mode 100644
index 0000000..971db4f
--- /dev/null
+++ b/libziparchive/zip_archive_private.h
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
+#define LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <utils/FileMap.h>
+#include <ziparchive/zip_archive.h>
+
+class MappedZipFile {
+ public:
+ explicit MappedZipFile(const int fd) :
+ has_fd_(true),
+ fd_(fd),
+ base_ptr_(nullptr),
+ data_length_(0),
+ read_pos_(0) {}
+
+ explicit MappedZipFile(void* address, size_t length) :
+ has_fd_(false),
+ fd_(-1),
+ base_ptr_(address),
+ data_length_(static_cast<off64_t>(length)),
+ read_pos_(0) {}
+
+ bool HasFd() const {return has_fd_;}
+
+ int GetFileDescriptor() const;
+
+ void* GetBasePtr() const;
+
+ off64_t GetFileLength() const;
+
+ bool SeekToOffset(off64_t offset);
+
+ bool ReadData(uint8_t* buffer, size_t read_amount);
+
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off);
+
+ private:
+ // If has_fd_ is true, fd is valid and we'll read contents of a zip archive
+ // from the file. Otherwise, we're opening the archive from a memory mapped
+ // file. In that case, base_ptr_ points to the start of the memory region and
+ // data_length_ defines the file length.
+ const bool has_fd_;
+
+ const int fd_;
+
+ void* const base_ptr_;
+ const off64_t data_length_;
+ // read_pos_ is the offset to the base_ptr_ where we read data from.
+ size_t read_pos_;
+};
+
+class CentralDirectory {
+ public:
+ CentralDirectory(void) :
+ base_ptr_(nullptr),
+ length_(0) {}
+
+ const uint8_t* GetBasePtr() const {return base_ptr_;}
+
+ size_t GetMapLength() const {return length_;}
+
+ void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
+
+ private:
+ const uint8_t* base_ptr_;
+ size_t length_;
+};
+
+struct ZipArchive {
+ // open Zip archive
+ mutable MappedZipFile mapped_zip;
+ const bool close_file;
+
+ // mapped central directory area
+ off64_t directory_offset;
+ CentralDirectory central_directory;
+ std::unique_ptr<android::FileMap> directory_map;
+
+ // number of entries in the Zip archive
+ uint16_t num_entries;
+
+ // We know how many entries are in the Zip archive, so we can have a
+ // fixed-size hash table. We define a load factor of 0.75 and over
+ // allocate so the maximum number entries can never be higher than
+ // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
+ uint32_t hash_table_size;
+ ZipString* hash_table;
+
+ ZipArchive(const int fd, bool assume_ownership) :
+ mapped_zip(fd),
+ close_file(assume_ownership),
+ directory_offset(0),
+ central_directory(),
+ directory_map(new android::FileMap()),
+ num_entries(0),
+ hash_table_size(0),
+ hash_table(nullptr) {}
+
+ ZipArchive(void* address, size_t length) :
+ mapped_zip(address, length),
+ close_file(false),
+ directory_offset(0),
+ central_directory(),
+ directory_map(new android::FileMap()),
+ num_entries(0),
+ hash_table_size(0),
+ hash_table(nullptr) {}
+
+ ~ZipArchive() {
+ if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
+ close(mapped_zip.GetFileDescriptor());
+ }
+
+ free(hash_table);
+ }
+
+ bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
+ size_t cd_size);
+
+};
+
+#endif // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
new file mode 100644
index 0000000..3f336a6
--- /dev/null
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ZIPARCHIVE"
+
+// Read-only stream access to Zip Archive entries.
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <log/log.h>
+
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+#include <zlib.h>
+
+#include "zip_archive_private.h"
+
+static constexpr size_t kBufSize = 65535;
+
+bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
+ ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
+ off64_t data_offset = entry.offset;
+ if (!archive->mapped_zip.SeekToOffset(data_offset)) {
+ ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno));
+ return false;
+ }
+ crc32_ = entry.crc32;
+ return true;
+}
+
+class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
+ public:
+ explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
+ : ZipArchiveStreamEntry(handle) {}
+ virtual ~ZipArchiveStreamEntryUncompressed() {}
+
+ const std::vector<uint8_t>* Read() override;
+
+ bool Verify() override;
+
+ protected:
+ bool Init(const ZipEntry& entry) override;
+
+ uint32_t length_;
+
+ private:
+ std::vector<uint8_t> data_;
+ uint32_t computed_crc32_;
+};
+
+bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
+ if (!ZipArchiveStreamEntry::Init(entry)) {
+ return false;
+ }
+
+ length_ = entry.uncompressed_length;
+
+ data_.resize(kBufSize);
+ computed_crc32_ = 0;
+
+ return true;
+}
+
+const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
+ if (length_ == 0) {
+ return nullptr;
+ }
+
+ size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
+ ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
+ errno = 0;
+ if (!archive->mapped_zip.ReadData(data_.data(), bytes)) {
+ if (errno != 0) {
+ ALOGE("Error reading from archive fd: %s", strerror(errno));
+ } else {
+ ALOGE("Short read of zip file, possibly corrupted zip?");
+ }
+ length_ = 0;
+ return nullptr;
+ }
+
+ if (bytes < data_.size()) {
+ data_.resize(bytes);
+ }
+ computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
+ length_ -= bytes;
+ return &data_;
+}
+
+bool ZipArchiveStreamEntryUncompressed::Verify() {
+ return length_ == 0 && crc32_ == computed_crc32_;
+}
+
+class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
+ public:
+ explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
+ : ZipArchiveStreamEntry(handle) {}
+ virtual ~ZipArchiveStreamEntryCompressed();
+
+ const std::vector<uint8_t>* Read() override;
+
+ bool Verify() override;
+
+ protected:
+ bool Init(const ZipEntry& entry) override;
+
+ private:
+ bool z_stream_init_ = false;
+ z_stream z_stream_;
+ std::vector<uint8_t> in_;
+ std::vector<uint8_t> out_;
+ uint32_t uncompressed_length_;
+ uint32_t compressed_length_;
+ uint32_t computed_crc32_;
+};
+
+// This method is using libz macros with old-style-casts
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
+ return inflateInit2(stream, window_bits);
+}
+#pragma GCC diagnostic pop
+
+bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
+ if (!ZipArchiveStreamEntry::Init(entry)) {
+ return false;
+ }
+
+ // Initialize the zlib stream struct.
+ memset(&z_stream_, 0, sizeof(z_stream_));
+ z_stream_.zalloc = Z_NULL;
+ z_stream_.zfree = Z_NULL;
+ z_stream_.opaque = Z_NULL;
+ z_stream_.next_in = nullptr;
+ z_stream_.avail_in = 0;
+ z_stream_.avail_out = 0;
+ z_stream_.data_type = Z_UNKNOWN;
+
+ // Use the undocumented "negative window bits" feature to tell zlib
+ // that there's no zlib header waiting for it.
+ int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)",
+ ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
+ }
+
+ return false;
+ }
+
+ z_stream_init_ = true;
+
+ uncompressed_length_ = entry.uncompressed_length;
+ compressed_length_ = entry.compressed_length;
+
+ out_.resize(kBufSize);
+ in_.resize(kBufSize);
+
+ computed_crc32_ = 0;
+
+ return true;
+}
+
+ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
+ if (z_stream_init_) {
+ inflateEnd(&z_stream_);
+ z_stream_init_ = false;
+ }
+}
+
+bool ZipArchiveStreamEntryCompressed::Verify() {
+ return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
+ crc32_ == computed_crc32_;
+}
+
+const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
+ if (z_stream_.avail_out == 0) {
+ z_stream_.next_out = out_.data();
+ z_stream_.avail_out = out_.size();;
+ }
+
+ while (true) {
+ if (z_stream_.avail_in == 0) {
+ if (compressed_length_ == 0) {
+ return nullptr;
+ }
+ size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
+ ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
+ errno = 0;
+ if (!archive->mapped_zip.ReadData(in_.data(), bytes)) {
+ if (errno != 0) {
+ ALOGE("Error reading from archive fd: %s", strerror(errno));
+ } else {
+ ALOGE("Short read of zip file, possibly corrupted zip?");
+ }
+ return nullptr;
+ }
+
+ compressed_length_ -= bytes;
+ z_stream_.next_in = in_.data();
+ z_stream_.avail_in = bytes;
+ }
+
+ int zerr = inflate(&z_stream_, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
+ zerr, z_stream_.next_in, z_stream_.avail_in,
+ z_stream_.next_out, z_stream_.avail_out);
+ return nullptr;
+ }
+
+ if (z_stream_.avail_out == 0) {
+ uncompressed_length_ -= out_.size();
+ computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+ return &out_;
+ }
+ if (zerr == Z_STREAM_END) {
+ if (z_stream_.avail_out != 0) {
+ // Resize the vector down to the actual size of the data.
+ out_.resize(out_.size() - z_stream_.avail_out);
+ computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+ uncompressed_length_ -= out_.size();
+ return &out_;
+ }
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
+ public:
+ explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
+ : ZipArchiveStreamEntryUncompressed(handle) {}
+ virtual ~ZipArchiveStreamEntryRawCompressed() {}
+
+ bool Verify() override;
+
+ protected:
+ bool Init(const ZipEntry& entry) override;
+};
+
+bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
+ if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
+ return false;
+ }
+ length_ = entry.compressed_length;
+
+ return true;
+}
+
+bool ZipArchiveStreamEntryRawCompressed::Verify() {
+ return length_ == 0;
+}
+
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(
+ ZipArchiveHandle handle, const ZipEntry& entry) {
+ ZipArchiveStreamEntry* stream = nullptr;
+ if (entry.method != kCompressStored) {
+ stream = new ZipArchiveStreamEntryCompressed(handle);
+ } else {
+ stream = new ZipArchiveStreamEntryUncompressed(handle);
+ }
+ if (stream && !stream->Init(entry)) {
+ delete stream;
+ stream = nullptr;
+ }
+
+ return stream;
+}
+
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(
+ ZipArchiveHandle handle, const ZipEntry& entry) {
+ ZipArchiveStreamEntry* stream = nullptr;
+ if (entry.method == kCompressStored) {
+ // Not compressed, don't need to do anything special.
+ stream = new ZipArchiveStreamEntryUncompressed(handle);
+ } else {
+ stream = new ZipArchiveStreamEntryRawCompressed(handle);
+ }
+ if (stream && !stream->Init(entry)) {
+ delete stream;
+ stream = nullptr;
+ }
+ return stream;
+}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 9a3cdb4..9dd6cc0 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -14,54 +14,54 @@
* limitations under the License.
*/
-#include "ziparchive/zip_archive.h"
-
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
+#include <string.h>
#include <unistd.h>
+
+#include <memory>
#include <vector>
-#include <base/file.h>
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
+#include <utils/FileMap.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
static std::string test_data_dir;
static const std::string kMissingZip = "missing.zip";
static const std::string kValidZip = "valid.zip";
+static const std::string kLargeZip = "large.zip";
+static const std::string kBadCrcZip = "bad_crc.zip";
+static const std::string kUpdateZip = "dummy-update.zip";
-static const uint8_t kATxtContents[] = {
+static const std::vector<uint8_t> kATxtContents {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'\n'
};
-static const uint8_t kBTxtContents[] = {
+static const std::vector<uint8_t> kATxtContentsCompressed {
+ 'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H',
+ 132, 210, '\\', '\0'
+};
+
+static const std::vector<uint8_t> kBTxtContents {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'\n'
};
-static const uint16_t kATxtNameLength = 5;
-static const uint16_t kBTxtNameLength = 5;
-static const uint16_t kNonexistentTxtNameLength = 15;
-static const uint16_t kEmptyTxtNameLength = 9;
-
-static const uint8_t kATxtName[kATxtNameLength] = {
- 'a', '.', 't', 'x', 't'
-};
-
-static const uint8_t kBTxtName[kBTxtNameLength] = {
- 'b', '.', 't', 'x', 't'
-};
-
-static const uint8_t kNonexistentTxtName[kNonexistentTxtNameLength] = {
- 'n', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', '.', 't', 'x' ,'t'
-};
-
-static const uint8_t kEmptyTxtName[kEmptyTxtNameLength] = {
- 'e', 'm', 'p', 't', 'y', '.', 't', 'x', 't'
-};
+static const std::string kATxtName("a.txt");
+static const std::string kBTxtName("b.txt");
+static const std::string kNonexistentTxtName("nonexistent.txt");
+static const std::string kEmptyTxtName("empty.txt");
+static const std::string kLargeCompressTxtName("compress.txt");
+static const std::string kLargeUncompressTxtName("uncompress.txt");
static int32_t OpenArchiveWrapper(const std::string& name,
ZipArchiveHandle* handle) {
@@ -75,6 +75,11 @@
ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
}
+static void SetZipString(ZipString* zip_str, const std::string& str) {
+ zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
+ zip_str->name_length = str.size();
+}
+
TEST(ziparchive, Open) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -91,7 +96,7 @@
}
TEST(ziparchive, OpenAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
ASSERT_NE(-1, fd);
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
@@ -101,7 +106,7 @@
}
TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
ASSERT_NE(-1, fd);
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
@@ -115,7 +120,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
ZipEntry data;
ZipString name;
@@ -152,7 +157,7 @@
void* iteration_cookie;
ZipString prefix("b/");
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL));
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, nullptr));
ZipEntry data;
ZipString name;
@@ -181,7 +186,7 @@
void* iteration_cookie;
ZipString suffix(".txt");
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix));
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, &suffix));
ZipEntry data;
ZipString name;
@@ -262,8 +267,7 @@
ZipEntry data;
ZipString name;
- name.name = kATxtName;
- name.name_length = kATxtNameLength;
+ SetZipString(&name, kATxtName);
ASSERT_EQ(0, FindEntry(handle, name, &data));
// Known facts about a.txt, from zipinfo -v.
@@ -272,11 +276,11 @@
ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
ASSERT_EQ(0x950821c5, data.crc32);
+ ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
// An entry that doesn't exist. Should be a negative return code.
ZipString absent_name;
- absent_name.name = kNonexistentTxtName;
- absent_name.name_length = kNonexistentTxtNameLength;
+ SetZipString(&absent_name, kNonexistentTxtName);
ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
CloseArchive(handle);
@@ -287,7 +291,7 @@
ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
ZipString name;
ZipEntry data;
@@ -305,26 +309,24 @@
// An entry that's deflated.
ZipEntry data;
ZipString a_name;
- a_name.name = kATxtName;
- a_name.name_length = kATxtNameLength;
+ SetZipString(&a_name, kATxtName);
ASSERT_EQ(0, FindEntry(handle, a_name, &data));
const uint32_t a_size = data.uncompressed_length;
- ASSERT_EQ(a_size, sizeof(kATxtContents));
+ ASSERT_EQ(a_size, kATxtContents.size());
uint8_t* buffer = new uint8_t[a_size];
ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
- ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size));
+ ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
delete[] buffer;
// An entry that's stored.
ZipString b_name;
- b_name.name = kBTxtName;
- b_name.name_length = kBTxtNameLength;
+ SetZipString(&b_name, kBTxtName);
ASSERT_EQ(0, FindEntry(handle, b_name, &data));
const uint32_t b_size = data.uncompressed_length;
- ASSERT_EQ(b_size, sizeof(kBTxtContents));
+ ASSERT_EQ(b_size, kBTxtContents.size());
buffer = new uint8_t[b_size];
ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
- ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size));
+ ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
delete[] buffer;
CloseArchive(handle);
@@ -373,70 +375,46 @@
0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
};
-static const uint8_t kAbTxtName[] = { 'a', 'b', '.', 't', 'x', 't' };
-static const uint16_t kAbTxtNameLength = sizeof(kAbTxtName);
+static const std::string kAbTxtName("ab.txt");
static const size_t kAbUncompressedSize = 270216;
-static int make_temporary_file(const char* file_name_pattern) {
- char full_path[1024];
- // Account for differences between the host and the target.
- //
- // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
- snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
- int fd = mkstemp(full_path);
- if (fd == -1) {
- snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
- fd = mkstemp(full_path);
- }
-
- return fd;
-}
-
TEST(ziparchive, EmptyEntries) {
- char temp_file_pattern[] = "empty_entries_test_XXXXXX";
- int fd = make_temporary_file(temp_file_pattern);
- ASSERT_NE(-1, fd);
- const ssize_t file_size = sizeof(kEmptyEntriesZip);
- ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
ZipEntry entry;
ZipString empty_name;
- empty_name.name = kEmptyTxtName;
- empty_name.name_length = kEmptyTxtNameLength;
+ SetZipString(&empty_name, kEmptyTxtName);
ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
- char output_file_pattern[] = "empty_entries_output_XXXXXX";
- int output_fd = make_temporary_file(output_file_pattern);
- ASSERT_NE(-1, output_fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+
+ TemporaryFile tmp_output_file;
+ ASSERT_NE(-1, tmp_output_file.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
struct stat stat_buf;
- ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+ ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
ASSERT_EQ(0, stat_buf.st_size);
-
- close(fd);
- close(output_fd);
}
TEST(ziparchive, EntryLargerThan32K) {
- char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
- int fd = make_temporary_file(temp_file_pattern);
- ASSERT_NE(-1, fd);
- ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
sizeof(kAbZip) - 1));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
ZipEntry entry;
ZipString ab_name;
- ab_name.name = kAbTxtName;
- ab_name.name_length = kAbTxtNameLength;
+ SetZipString(&ab_name, kAbTxtName);
ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
@@ -445,21 +423,21 @@
ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
// Extract the entry to a file.
- char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
- int output_fd = make_temporary_file(output_file_pattern);
- ASSERT_NE(-1, output_fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+ TemporaryFile tmp_output_file;
+ ASSERT_NE(-1, tmp_output_file.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
// Make sure the extracted file size is as expected.
struct stat stat_buf;
- ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+ ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
// Read the file back to a buffer and make sure the contents are
// the same as the memory buffer we extracted directly to.
std::vector<uint8_t> file_contents(kAbUncompressedSize);
- ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
- ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
+ ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0],
+ file_contents.size()));
ASSERT_EQ(file_contents, buffer);
for (int i = 0; i < 90072; ++i) {
@@ -468,74 +446,198 @@
ASSERT_EQ('b', line[1]);
ASSERT_EQ('\n', line[2]);
}
-
- close(fd);
- close(output_fd);
}
TEST(ziparchive, TrailerAfterEOCD) {
- char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
- int fd = make_temporary_file(temp_file_pattern);
- ASSERT_NE(-1, fd);
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
// Create a file with 8 bytes of random garbage.
static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
- const ssize_t file_size = sizeof(kEmptyEntriesZip);
- const ssize_t trailer_size = sizeof(trailer);
- ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
- ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
ZipArchiveHandle handle;
- ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+ ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
}
TEST(ziparchive, ExtractToFile) {
- char kTempFilePattern[] = "zip_archive_input_XXXXXX";
- int fd = make_temporary_file(kTempFilePattern);
- ASSERT_NE(-1, fd);
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
- const ssize_t data_size = sizeof(data);
+ const size_t data_size = sizeof(data);
- ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
ZipEntry entry;
ZipString name;
- name.name = kATxtName;
- name.name_length = kATxtNameLength;
+ SetZipString(&name, kATxtName);
ASSERT_EQ(0, FindEntry(handle, name, &entry));
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
// Assert that the first 8 bytes of the file haven't been clobbered.
uint8_t read_buffer[data_size];
- ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
- ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
+ ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
// Assert that the remainder of the file contains the incompressed data.
std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
- ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
- TEMP_FAILURE_RETRY(
- read(fd, &uncompressed_data[0], entry.uncompressed_length)));
- ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents,
- sizeof(kATxtContents)));
+ ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
+ entry.uncompressed_length));
+ ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
+ kATxtContents.size()));
// Assert that the total length of the file is sane
- ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)),
- lseek64(fd, 0, SEEK_END));
+ ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
+ lseek64(tmp_file.fd, 0, SEEK_END));
+}
- close(fd);
+#if !defined(_WIN32)
+TEST(ziparchive, OpenFromMemory) {
+ const std::string zip_path = test_data_dir + "/" + kUpdateZip;
+ android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
+ ASSERT_NE(-1, fd);
+ struct stat sb;
+ ASSERT_EQ(0, fstat(fd, &sb));
+
+ // Memory map the file first and open the archive from the memory region.
+ android::FileMap file_map;
+ file_map.create(zip_path.c_str(), fd, 0/*offset*/, sb.st_size, true);
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(),
+ zip_path.c_str(), &handle));
+
+ // Assert one entry can be found and extracted correctly.
+ std::string BINARY_PATH("META-INF/com/google/android/update-binary");
+ ZipString binary_path(BINARY_PATH.c_str());
+ ZipEntry binary_entry;
+ ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
+ TemporaryFile tmp_binary;
+ ASSERT_NE(-1, tmp_binary.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
+}
+#endif
+
+static void ZipArchiveStreamTest(
+ ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
+ bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
+ ZipString name;
+ SetZipString(&name, entry_name);
+ ASSERT_EQ(0, FindEntry(handle, name, entry));
+ std::unique_ptr<ZipArchiveStreamEntry> stream;
+ if (raw) {
+ stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
+ if (entry->method == kCompressStored) {
+ read_data->resize(entry->uncompressed_length);
+ } else {
+ read_data->resize(entry->compressed_length);
+ }
+ } else {
+ stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
+ read_data->resize(entry->uncompressed_length);
+ }
+ uint8_t* read_data_ptr = read_data->data();
+ ASSERT_TRUE(stream.get() != nullptr);
+ const std::vector<uint8_t>* data;
+ uint64_t total_size = 0;
+ while ((data = stream->Read()) != nullptr) {
+ total_size += data->size();
+ memcpy(read_data_ptr, data->data(), data->size());
+ read_data_ptr += data->size();
+ }
+ ASSERT_EQ(verified, stream->Verify());
+ ASSERT_EQ(total_size, read_data->size());
+}
+
+static void ZipArchiveStreamTestUsingContents(
+ const std::string& zip_file, const std::string& entry_name,
+ const std::vector<uint8_t>& contents, bool raw) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
+
+ ZipEntry entry;
+ std::vector<uint8_t> read_data;
+ ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
+
+ ASSERT_EQ(contents.size(), read_data.size());
+ ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
+
+ CloseArchive(handle);
+}
+
+static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, const std::string& entry_name) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
+
+ ZipEntry entry;
+ std::vector<uint8_t> read_data;
+ ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
+
+ std::vector<uint8_t> cmp_data(entry.uncompressed_length);
+ ASSERT_EQ(entry.uncompressed_length, read_data.size());
+ ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size()));
+ ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, StreamCompressed) {
+ ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContents, false);
+}
+
+TEST(ziparchive, StreamUncompressed) {
+ ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, false);
+}
+
+TEST(ziparchive, StreamRawCompressed) {
+ ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContentsCompressed, true);
+}
+
+TEST(ziparchive, StreamRawUncompressed) {
+ ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, true);
+}
+
+TEST(ziparchive, StreamLargeCompressed) {
+ ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeCompressTxtName);
+}
+
+TEST(ziparchive, StreamLargeUncompressed) {
+ ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeUncompressTxtName);
+}
+
+TEST(ziparchive, StreamCompressedBadCrc) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
+
+ ZipEntry entry;
+ std::vector<uint8_t> read_data;
+ ZipArchiveStreamTest(handle, kATxtName, false, false, &entry, &read_data);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, StreamUncompressedBadCrc) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
+
+ ZipEntry entry;
+ std::vector<uint8_t> read_data;
+ ZipArchiveStreamTest(handle, kBTxtName, false, false, &entry, &read_data);
+
+ CloseArchive(handle);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
static struct option options[] = {
- { "test_data_dir", required_argument, NULL, 't' },
- { NULL, 0, NULL, 0 }
+ { "test_data_dir", required_argument, nullptr, 't' },
+ { nullptr, 0, nullptr, 0 }
};
while (true) {
@@ -556,9 +658,15 @@
}
if (test_data_dir[0] != '/') {
- printf("Test data must be an absolute path, was %s\n\n",
- test_data_dir.c_str());
- return -2;
+ std::vector<char> cwd_buffer(1024);
+ const char* cwd = getcwd(cwd_buffer.data(), cwd_buffer.size() - 1);
+ if (cwd == nullptr) {
+ printf("Cannot get current working directory, use an absolute path instead, was %s\n\n",
+ test_data_dir.c_str());
+ return -2;
+ }
+ test_data_dir = '/' + test_data_dir;
+ test_data_dir = cwd + test_data_dir;
}
return RUN_ALL_TESTS();
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
new file mode 100644
index 0000000..b72ed7f
--- /dev/null
+++ b/libziparchive/zip_writer.cc
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
+#include "ziparchive/zip_writer.h"
+
+#include <utils/Log.h>
+
+#include <sys/param.h>
+
+#include <cassert>
+#include <cstdio>
+#include <memory>
+#include <vector>
+#include <zlib.h>
+#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+
+#if !defined(powerof2)
+#define powerof2(x) ((((x)-1)&(x))==0)
+#endif
+
+/* Zip compression methods we support */
+enum {
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
+};
+
+// Size of the output buffer used for compression.
+static const size_t kBufSize = 32768u;
+
+// No error, operation completed successfully.
+static const int32_t kNoError = 0;
+
+// The ZipWriter is in a bad state.
+static const int32_t kInvalidState = -1;
+
+// There was an IO error while writing to disk.
+static const int32_t kIoError = -2;
+
+// The zip entry name was invalid.
+static const int32_t kInvalidEntryName = -3;
+
+// An error occurred in zlib.
+static const int32_t kZlibError = -4;
+
+// The start aligned function was called with the aligned flag.
+static const int32_t kInvalidAlign32Flag = -5;
+
+// The alignment parameter is not a power of 2.
+static const int32_t kInvalidAlignment = -6;
+
+static const char* sErrorCodes[] = {
+ "Invalid state",
+ "IO error",
+ "Invalid entry name",
+ "Zlib error",
+};
+
+const char* ZipWriter::ErrorCodeString(int32_t error_code) {
+ if (error_code < 0 && (-error_code) < static_cast<int32_t>(arraysize(sErrorCodes))) {
+ return sErrorCodes[-error_code];
+ }
+ return nullptr;
+}
+
+static void DeleteZStream(z_stream* stream) {
+ deflateEnd(stream);
+ delete stream;
+}
+
+ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
+ z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
+}
+
+ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
+ current_offset_(writer.current_offset_),
+ state_(writer.state_),
+ files_(std::move(writer.files_)),
+ z_stream_(std::move(writer.z_stream_)),
+ buffer_(std::move(writer.buffer_)){
+ writer.file_ = nullptr;
+ writer.state_ = State::kError;
+}
+
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
+ file_ = writer.file_;
+ current_offset_ = writer.current_offset_;
+ state_ = writer.state_;
+ files_ = std::move(writer.files_);
+ z_stream_ = std::move(writer.z_stream_);
+ buffer_ = std::move(writer.buffer_);
+ writer.file_ = nullptr;
+ writer.state_ = State::kError;
+ return *this;
+}
+
+int32_t ZipWriter::HandleError(int32_t error_code) {
+ state_ = State::kError;
+ z_stream_.reset();
+ return error_code;
+}
+
+int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
+ uint32_t alignment = 0;
+ if (flags & kAlign32) {
+ flags &= ~kAlign32;
+ alignment = 4;
+ }
+ return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
+}
+
+int32_t ZipWriter::StartAlignedEntry(const char* path, size_t flags, uint32_t alignment) {
+ return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
+}
+
+int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+ uint32_t alignment = 0;
+ if (flags & kAlign32) {
+ flags &= ~kAlign32;
+ alignment = 4;
+ }
+ return StartAlignedEntryWithTime(path, flags, time, alignment);
+}
+
+static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
+ /* round up to an even number of seconds */
+ when = static_cast<time_t>((static_cast<unsigned long>(when) + 1) & (~1));
+
+ struct tm* ptm;
+#if !defined(_WIN32)
+ struct tm tm_result;
+ ptm = localtime_r(&when, &tm_result);
+#else
+ ptm = localtime(&when);
+#endif
+
+ int year = ptm->tm_year;
+ if (year < 80) {
+ year = 80;
+ }
+
+ *out_date = (year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday;
+ *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+}
+
+int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
+ time_t time, uint32_t alignment) {
+ if (state_ != State::kWritingZip) {
+ return kInvalidState;
+ }
+
+ if (flags & kAlign32) {
+ return kInvalidAlign32Flag;
+ }
+
+ if (powerof2(alignment) == 0) {
+ return kInvalidAlignment;
+ }
+
+ FileInfo fileInfo = {};
+ fileInfo.path = std::string(path);
+ fileInfo.local_file_header_offset = current_offset_;
+
+ if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(fileInfo.path.data()),
+ fileInfo.path.size())) {
+ return kInvalidEntryName;
+ }
+
+ LocalFileHeader header = {};
+ header.lfh_signature = LocalFileHeader::kSignature;
+
+ // Set this flag to denote that a DataDescriptor struct will appear after the data,
+ // containing the crc and size fields.
+ header.gpb_flags |= kGPBDDFlagMask;
+
+ if (flags & ZipWriter::kCompress) {
+ fileInfo.compression_method = kCompressDeflated;
+
+ int32_t result = PrepareDeflate();
+ if (result != kNoError) {
+ return result;
+ }
+ } else {
+ fileInfo.compression_method = kCompressStored;
+ }
+ header.compression_method = fileInfo.compression_method;
+
+ ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
+ header.last_mod_time = fileInfo.last_mod_time;
+ header.last_mod_date = fileInfo.last_mod_date;
+
+ header.file_name_length = fileInfo.path.size();
+
+ off64_t offset = current_offset_ + sizeof(header) + fileInfo.path.size();
+ std::vector<char> zero_padding;
+ if (alignment != 0 && (offset & (alignment - 1))) {
+ // Pad the extra field so the data will be aligned.
+ uint16_t padding = alignment - (offset % alignment);
+ header.extra_field_length = padding;
+ offset += padding;
+ zero_padding.resize(padding);
+ memset(zero_padding.data(), 0, zero_padding.size());
+ }
+
+ if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fwrite(path, sizeof(*path), fileInfo.path.size(), file_) != fileInfo.path.size()) {
+ return HandleError(kIoError);
+ }
+
+ if (header.extra_field_length != 0 &&
+ fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
+ != header.extra_field_length) {
+ return HandleError(kIoError);
+ }
+
+ files_.emplace_back(std::move(fileInfo));
+
+ current_offset_ = offset;
+ state_ = State::kWritingEntry;
+ return kNoError;
+}
+
+int32_t ZipWriter::PrepareDeflate() {
+ assert(state_ == State::kWritingZip);
+
+ // Initialize the z_stream for compression.
+ z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+ int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
+ DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+#pragma GCC diagnostic pop
+
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+ return HandleError(kZlibError);
+ } else {
+ ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+ return HandleError(kZlibError);
+ }
+ }
+
+ z_stream_->next_out = buffer_.data();
+ z_stream_->avail_out = buffer_.size();
+ return kNoError;
+}
+
+int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
+ if (state_ != State::kWritingEntry) {
+ return HandleError(kInvalidState);
+ }
+
+ FileInfo& currentFile = files_.back();
+ int32_t result = kNoError;
+ if (currentFile.compression_method & kCompressDeflated) {
+ result = CompressBytes(¤tFile, data, len);
+ } else {
+ result = StoreBytes(¤tFile, data, len);
+ }
+
+ if (result != kNoError) {
+ return result;
+ }
+
+ currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
+ currentFile.uncompressed_size += len;
+ return kNoError;
+}
+
+int32_t ZipWriter::StoreBytes(FileInfo* file, const void* data, size_t len) {
+ assert(state_ == State::kWritingEntry);
+
+ if (fwrite(data, 1, len, file_) != len) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += len;
+ current_offset_ += len;
+ return kNoError;
+}
+
+int32_t ZipWriter::CompressBytes(FileInfo* file, const void* data, size_t len) {
+ assert(state_ == State::kWritingEntry);
+ assert(z_stream_);
+ assert(z_stream_->next_out != nullptr);
+ assert(z_stream_->avail_out != 0);
+
+ // Prepare the input.
+ z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
+ z_stream_->avail_in = len;
+
+ while (z_stream_->avail_in > 0) {
+ // We have more data to compress.
+ int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
+ if (zerr != Z_OK) {
+ return HandleError(kZlibError);
+ }
+
+ if (z_stream_->avail_out == 0) {
+ // The output is full, let's write it to disk.
+ size_t write_bytes = z_stream_->next_out - buffer_.data();
+ if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += write_bytes;
+ current_offset_ += write_bytes;
+
+ // Reset the output buffer for the next input.
+ z_stream_->next_out = buffer_.data();
+ z_stream_->avail_out = buffer_.size();
+ }
+ }
+ return kNoError;
+}
+
+int32_t ZipWriter::FlushCompressedBytes(FileInfo* file) {
+ assert(state_ == State::kWritingEntry);
+ assert(z_stream_);
+ assert(z_stream_->next_out != nullptr);
+ assert(z_stream_->avail_out != 0);
+
+ // Keep deflating while there isn't enough space in the buffer to
+ // to complete the compress.
+ int zerr;
+ while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
+ assert(z_stream_->avail_out == 0);
+ size_t write_bytes = z_stream_->next_out - buffer_.data();
+ if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += write_bytes;
+ current_offset_ += write_bytes;
+
+ z_stream_->next_out = buffer_.data();
+ z_stream_->avail_out = buffer_.size();
+ }
+ if (zerr != Z_STREAM_END) {
+ return HandleError(kZlibError);
+ }
+
+ size_t write_bytes = z_stream_->next_out - buffer_.data();
+ if (write_bytes != 0) {
+ if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += write_bytes;
+ current_offset_ += write_bytes;
+ }
+ z_stream_.reset();
+ return kNoError;
+}
+
+int32_t ZipWriter::FinishEntry() {
+ if (state_ != State::kWritingEntry) {
+ return kInvalidState;
+ }
+
+ FileInfo& currentFile = files_.back();
+ if (currentFile.compression_method & kCompressDeflated) {
+ int32_t result = FlushCompressedBytes(¤tFile);
+ if (result != kNoError) {
+ return result;
+ }
+ }
+
+ const uint32_t sig = DataDescriptor::kOptSignature;
+ if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+ state_ = State::kError;
+ return kIoError;
+ }
+
+ DataDescriptor dd = {};
+ dd.crc32 = currentFile.crc32;
+ dd.compressed_size = currentFile.compressed_size;
+ dd.uncompressed_size = currentFile.uncompressed_size;
+ if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+ state_ = State::kWritingZip;
+ return kNoError;
+}
+
+int32_t ZipWriter::Finish() {
+ if (state_ != State::kWritingZip) {
+ return kInvalidState;
+ }
+
+ off64_t startOfCdr = current_offset_;
+ for (FileInfo& file : files_) {
+ CentralDirectoryRecord cdr = {};
+ cdr.record_signature = CentralDirectoryRecord::kSignature;
+ cdr.gpb_flags |= kGPBDDFlagMask;
+ cdr.compression_method = file.compression_method;
+ cdr.last_mod_time = file.last_mod_time;
+ cdr.last_mod_date = file.last_mod_date;
+ cdr.crc32 = file.crc32;
+ cdr.compressed_size = file.compressed_size;
+ cdr.uncompressed_size = file.uncompressed_size;
+ cdr.file_name_length = file.path.size();
+ cdr.local_file_header_offset = file.local_file_header_offset;
+ if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fwrite(file.path.data(), 1, file.path.size(), file_) != file.path.size()) {
+ return HandleError(kIoError);
+ }
+
+ current_offset_ += sizeof(cdr) + file.path.size();
+ }
+
+ EocdRecord er = {};
+ er.eocd_signature = EocdRecord::kSignature;
+ er.disk_num = 0;
+ er.cd_start_disk = 0;
+ er.num_records_on_disk = files_.size();
+ er.num_records = files_.size();
+ er.cd_size = current_offset_ - startOfCdr;
+ er.cd_start_offset = startOfCdr;
+
+ if (fwrite(&er, sizeof(er), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fflush(file_) != 0) {
+ return HandleError(kIoError);
+ }
+
+ current_offset_ += sizeof(er);
+ state_ = State::kDone;
+ return kNoError;
+}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
new file mode 100644
index 0000000..16a574d
--- /dev/null
+++ b/libziparchive/zip_writer_test.cc
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ziparchive/zip_archive.h"
+#include "ziparchive/zip_writer.h"
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <time.h>
+#include <memory>
+#include <vector>
+
+struct zipwriter : public ::testing::Test {
+ TemporaryFile* temp_file_;
+ int fd_;
+ FILE* file_;
+
+ void SetUp() override {
+ temp_file_ = new TemporaryFile();
+ fd_ = temp_file_->fd;
+ file_ = fdopen(fd_, "w");
+ ASSERT_NE(file_, nullptr);
+ }
+
+ void TearDown() override {
+ fclose(file_);
+ delete temp_file_;
+ }
+};
+
+TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
+ ZipWriter writer(file_);
+
+ const char* expected = "hello";
+
+ ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ EXPECT_EQ(strlen(expected), data.compressed_length);
+ EXPECT_EQ(strlen(expected), data.uncompressed_length);
+ EXPECT_EQ(kCompressStored, data.method);
+
+ char buffer[6];
+ EXPECT_EQ(0,
+ ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)));
+ buffer[5] = 0;
+
+ EXPECT_STREQ(expected, buffer);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ char buffer[4];
+ ZipEntry data;
+
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(2u, data.compressed_length);
+ EXPECT_EQ(2u, data.uncompressed_length);
+ ASSERT_EQ(0,
+ ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+ buffer[2] = 0;
+ EXPECT_STREQ("he", buffer);
+
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(3u, data.compressed_length);
+ EXPECT_EQ(3u, data.uncompressed_length);
+ ASSERT_EQ(0,
+ ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+ buffer[3] = 0;
+ EXPECT_STREQ("llo", buffer);
+
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(0u, data.compressed_length);
+ EXPECT_EQ(0u, data.uncompressed_length);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ EXPECT_EQ(0, data.offset & 0x03);
+
+ CloseArchive(handle);
+}
+
+void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
+ memset(tm, 0, sizeof(struct tm));
+ tm->tm_hour = (zip_time >> 11) & 0x1f;
+ tm->tm_min = (zip_time >> 5) & 0x3f;
+ tm->tm_sec = (zip_time & 0x1f) << 1;
+
+ tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
+ tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
+ tm->tm_mday = (zip_time >> 16) & 0x1f;
+}
+
+static struct tm MakeTm() {
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = 2001 - 1900;
+ tm.tm_mon = 1;
+ tm.tm_mday = 12;
+ tm.tm_hour = 18;
+ tm.tm_min = 30;
+ tm.tm_sec = 20;
+ return tm;
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
+ ZipWriter writer(file_);
+
+ struct tm tm = MakeTm();
+ time_t time = mktime(&tm);
+ ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ EXPECT_EQ(0, data.offset & 0x03);
+
+ struct tm mod;
+ ConvertZipTimeToTm(data.mod_time, &mod);
+ EXPECT_EQ(tm.tm_sec, mod.tm_sec);
+ EXPECT_EQ(tm.tm_min, mod.tm_min);
+ EXPECT_EQ(tm.tm_hour, mod.tm_hour);
+ EXPECT_EQ(tm.tm_mday, mod.tm_mday);
+ EXPECT_EQ(tm.tm_mon, mod.tm_mon);
+ EXPECT_EQ(tm.tm_year, mod.tm_year);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ EXPECT_EQ(0, data.offset & 0xfff);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
+ ZipWriter writer(file_);
+
+ struct tm tm = MakeTm();
+ time_t time = mktime(&tm);
+ ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ EXPECT_EQ(0, data.offset & 0xfff);
+
+ struct tm mod;
+ ConvertZipTimeToTm(data.mod_time, &mod);
+ EXPECT_EQ(tm.tm_sec, mod.tm_sec);
+ EXPECT_EQ(tm.tm_min, mod.tm_min);
+ EXPECT_EQ(tm.tm_hour, mod.tm_hour);
+ EXPECT_EQ(tm.tm_mday, mod.tm_mday);
+ EXPECT_EQ(tm.tm_mon, mod.tm_mon);
+ EXPECT_EQ(tm.tm_year, mod.tm_year);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+ ASSERT_EQ(0, writer.WriteBytes("helo", 4));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ EXPECT_EQ(kCompressDeflated, data.method);
+ EXPECT_EQ(4u, data.uncompressed_length);
+
+ char buffer[5];
+ ASSERT_EQ(0,
+ ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+ buffer[4] = 0;
+
+ EXPECT_STREQ("helo", buffer);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipFlushFull) {
+ // This exact data will cause the Finish() to require multiple calls
+ // to deflate() because the ZipWriter buffer isn't big enough to hold
+ // the entire compressed data buffer.
+ constexpr size_t kBufSize = 10000000;
+ std::vector<uint8_t> buffer(kBufSize);
+ size_t prev = 1;
+ for (size_t i = 0; i < kBufSize; i++) {
+ buffer[i] = i + prev;
+ prev = i;
+ }
+
+ ZipWriter writer(file_);
+ ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+ ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ EXPECT_EQ(kCompressDeflated, data.method);
+ EXPECT_EQ(kBufSize, data.uncompressed_length);
+
+ std::vector<uint8_t> decompress(kBufSize);
+ memset(decompress.data(), 0, kBufSize);
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size()));
+ EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
+ << "Input buffer and output buffer are different.";
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, CheckStartEntryErrors) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
+ ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
+}
diff --git a/lmkd/Android.mk b/lmkd/Android.mk
index 39081d6..8980d1c 100644
--- a/lmkd/Android.mk
+++ b/lmkd/Android.mk
@@ -2,9 +2,11 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := lmkd.c
-LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup
+LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup libcutils
LOCAL_CFLAGS := -Werror
LOCAL_MODULE := lmkd
+LOCAL_INIT_RC := lmkd.rc
+
include $(BUILD_EXECUTABLE)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 7bbc811..8a6168c 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -18,6 +18,7 @@
#include <arpa/inet.h>
#include <errno.h>
+#include <sched.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
@@ -39,7 +40,7 @@
#endif
#define MEMCG_SYSFS_PATH "/dev/memcg/"
-#define MEMPRESSURE_WATCH_LEVEL "medium"
+#define MEMPRESSURE_WATCH_LEVEL "low"
#define ZONEINFO_PATH "/proc/zoneinfo"
#define LINE_MAX 128
@@ -77,12 +78,7 @@
static int epollfd;
static int maxevents;
-#define OOM_DISABLE (-17)
-/* inclusive */
-#define OOM_ADJUST_MIN (-16)
-#define OOM_ADJUST_MAX 15
-
-/* kernel OOM score values */
+/* OOM score values used by both kernel and framework */
#define OOM_SCORE_ADJ_MIN (-1000)
#define OOM_SCORE_ADJ_MAX 1000
@@ -114,8 +110,8 @@
static struct proc *pidhash[PIDHASH_SZ];
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
-#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN)
-static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1];
+#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
+static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
/*
* Wait 1-2 seconds for the death report of a killed process prior to
@@ -148,14 +144,6 @@
return ret;
}
-static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
-{
- if (oom_adj == OOM_ADJUST_MAX)
- return OOM_SCORE_ADJ_MAX;
- else
- return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
-}
-
static struct proc *pid_lookup(int pid) {
struct proc *procp;
@@ -230,7 +218,7 @@
}
static void writefilestring(char *path, char *s) {
- int fd = open(path, O_WRONLY);
+ int fd = open(path, O_WRONLY | O_CLOEXEC);
int len = strlen(s);
int ret;
@@ -254,13 +242,13 @@
char path[80];
char val[20];
- if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) {
+ if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
return;
}
snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
- snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj));
+ snprintf(val, sizeof(val), "%d", oomadj);
writefilestring(path, val);
if (use_inkernel_interface)
@@ -410,8 +398,6 @@
}
static void ctrl_connect_handler(uint32_t events __unused) {
- struct sockaddr addr;
- socklen_t alen;
struct epoll_event epev;
if (ctrl_dfd >= 0) {
@@ -419,8 +405,7 @@
ctrl_dfd_reopened = 1;
}
- alen = sizeof(addr);
- ctrl_dfd = accept(ctrl_lfd, &addr, &alen);
+ ctrl_dfd = accept(ctrl_lfd, NULL, NULL);
if (ctrl_dfd < 0) {
ALOGE("lmkd control socket accept failed; errno=%d", errno);
@@ -486,7 +471,7 @@
memset(mip, 0, sizeof(struct sysmeminfo));
- fd = open(ZONEINFO_PATH, O_RDONLY);
+ fd = open(ZONEINFO_PATH, O_RDONLY | O_CLOEXEC);
if (fd == -1) {
ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
return -1;
@@ -517,7 +502,7 @@
ssize_t ret;
snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
- fd = open(path, O_RDONLY);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd == -1)
return -1;
@@ -540,7 +525,7 @@
ssize_t ret;
snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
- fd = open(path, O_RDONLY);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd == -1)
return NULL;
ret = read_all(fd, line, sizeof(line) - 1);
@@ -607,7 +592,7 @@
static int find_and_kill_process(int other_free, int other_file, bool first)
{
int i;
- int min_score_adj = OOM_ADJUST_MAX + 1;
+ int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
int minfree = 0;
int killed_size = 0;
@@ -619,10 +604,10 @@
}
}
- if (min_score_adj == OOM_ADJUST_MAX + 1)
+ if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)
return 0;
- for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
+ for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
struct proc *procp;
retry:
@@ -685,19 +670,19 @@
struct epoll_event epev;
int ret;
- mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY);
+ mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
if (mpfd < 0) {
ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
goto err_open_mpfd;
}
- evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY);
+ evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY | O_CLOEXEC);
if (evctlfd < 0) {
ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
goto err_open_evctlfd;
}
- evfd = eventfd(0, EFD_NONBLOCK);
+ evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
if (evfd < 0) {
ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
goto err_eventfd;
@@ -783,7 +768,7 @@
ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
}
- for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) {
+ for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
procadjslot_list[i].next = &procadjslot_list[i];
procadjslot_list[i].prev = &procadjslot_list[i];
}
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
new file mode 100644
index 0000000..3bb84ab
--- /dev/null
+++ b/lmkd/lmkd.rc
@@ -0,0 +1,6 @@
+service lmkd /system/bin/lmkd
+ class core
+ group root readproc
+ critical
+ socket lmkd seqpacket 0660 system system
+ writepid /dev/cpuset/system-background/tasks
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 7115f9b..1dacbe1 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -5,7 +5,7 @@
LOCAL_SRC_FILES:= logcat.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils libpcrecpp
LOCAL_MODULE := logcat
@@ -13,4 +13,16 @@
include $(BUILD_EXECUTABLE)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := logpersist.start
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_INIT_RC := logcatd.rc
+LOCAL_MODULE_PATH := $(bin_dir)
+LOCAL_SRC_FILES := logpersist
+ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
+LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
+include $(BUILD_PREBUILT)
+
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index e598bb8..4a171fd 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -6,6 +6,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <getopt.h>
#include <math.h>
#include <sched.h>
#include <signal.h>
@@ -24,17 +25,17 @@
#include <memory>
#include <string>
-#include <base/file.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
-#include <log/log.h>
-#include <log/log_read.h>
-#include <log/logd.h>
-#include <log/logger.h>
#include <log/logprint.h>
-#include <utils/threads.h>
+#include <private/android_logger.h>
+#include <system/thread_defs.h>
+
+#include <pcrecpp.h>
#define DEFAULT_MAX_ROTATED_LOGS 4
@@ -66,17 +67,30 @@
/* Global Variables */
-static const char * g_outputFileName = NULL;
+static const char * g_outputFileName;
// 0 means "no log rotation"
-static size_t g_logRotateSizeKBytes = 0;
+static size_t g_logRotateSizeKBytes;
// 0 means "unbounded"
static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
static int g_outFD = -1;
-static size_t g_outByteCount = 0;
-static int g_printBinary = 0;
-static int g_devCount = 0; // >1 means multiple
+static size_t g_outByteCount;
+static int g_printBinary;
+static int g_devCount; // >1 means multiple
+static pcrecpp::RE* g_regex;
+// 0 means "infinite"
+static size_t g_maxCount;
+static size_t g_printCount;
+static bool g_printItAnyways;
+static bool g_debug;
-__noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
+enum helpType {
+ HELP_FALSE,
+ HELP_TRUE,
+ HELP_FORMAT
+};
+
+// if showHelp is set, newline required in fmt statement to transition to usage
+__noreturn static void logcat_panic(enum helpType showHelp, const char *fmt, ...) __printflike(2,3);
static int openLogFile (const char *pathname)
{
@@ -100,35 +114,33 @@
(g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
- char *file0, *file1;
+ std::string file1 = android::base::StringPrintf(
+ "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
- asprintf(&file1, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
-
+ std::string file0;
if (i - 1 == 0) {
- asprintf(&file0, "%s", g_outputFileName);
+ file0 = android::base::StringPrintf("%s", g_outputFileName);
} else {
- asprintf(&file0, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
+ file0 = android::base::StringPrintf(
+ "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
}
- if (!file0 || !file1) {
+ if ((file0.length() == 0) || (file1.length() == 0)) {
perror("while rotating log files");
break;
}
- err = rename(file0, file1);
+ err = rename(file0.c_str(), file1.c_str());
if (err < 0 && errno != ENOENT) {
perror("while rotating log files");
}
-
- free(file1);
- free(file0);
}
g_outFD = openLogFile(g_outputFileName);
if (g_outFD < 0) {
- logcat_panic(false, "couldn't open output file");
+ logcat_panic(HELP_FALSE, "couldn't open output file");
}
g_outByteCount = 0;
@@ -142,6 +154,17 @@
TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
}
+static bool regexOk(const AndroidLogEntry& entry)
+{
+ if (!g_regex) {
+ return true;
+ }
+
+ std::string messageString(entry.message, entry.messageLen);
+
+ return g_regex->PartialMatch(messageString);
+}
+
static void processBuffer(log_device_t* dev, struct log_msg *buf)
{
int bytesWritten = 0;
@@ -154,7 +177,7 @@
static EventTagMap *eventTagMap = NULL;
if (!eventTagMap && !hasOpenedEventTagMap) {
- eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
+ eventTagMap = android_openEventTagMap(NULL);
hasOpenedEventTagMap = true;
}
err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
@@ -166,15 +189,22 @@
} else {
err = android_log_processLogBuffer(&buf->entry_v1, &entry);
}
- if (err < 0) {
+ if ((err < 0) && !g_debug) {
goto error;
}
- if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
- bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
+ if (android_log_shouldPrintLine(g_logformat,
+ std::string(entry.tag, entry.tagLen).c_str(),
+ entry.priority)) {
+ bool match = regexOk(entry);
- if (bytesWritten < 0) {
- logcat_panic(false, "output error");
+ g_printCount += match;
+ if (match || g_printItAnyways) {
+ bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
+
+ if (bytesWritten < 0) {
+ logcat_panic(HELP_FALSE, "output error");
+ }
}
}
@@ -187,7 +217,6 @@
}
error:
- //fprintf (stderr, "Error processing record\n");
return;
}
@@ -199,20 +228,22 @@
dev->printed ? "switch to" : "beginning of",
dev->device);
if (write(g_outFD, buf, strlen(buf)) < 0) {
- logcat_panic(false, "output error");
+ logcat_panic(HELP_FALSE, "output error");
}
}
dev->printed = true;
}
}
-static void setupOutput()
-{
-
+static void setupOutputAndSchedulingPolicy(bool blocking) {
if (g_outputFileName == NULL) {
g_outFD = STDOUT_FILENO;
+ return;
+ }
- } else {
+ if (blocking) {
+ // Lower priority and set to batch scheduling if we are saving
+ // the logs into files and taking continuous content.
if (set_sched_policy(0, SP_BACKGROUND) < 0) {
fprintf(stderr, "failed to set background scheduling policy\n");
}
@@ -226,26 +257,26 @@
if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
fprintf(stderr, "failed set to priority\n");
}
-
- g_outFD = openLogFile (g_outputFileName);
-
- if (g_outFD < 0) {
- logcat_panic(false, "couldn't open output file");
- }
-
- struct stat statbuf;
- if (fstat(g_outFD, &statbuf) == -1) {
- close(g_outFD);
- logcat_panic(false, "couldn't get output file stat\n");
- }
-
- if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
- close(g_outFD);
- logcat_panic(false, "invalid output file stat\n");
- }
-
- g_outByteCount = statbuf.st_size;
}
+
+ g_outFD = openLogFile (g_outputFileName);
+
+ if (g_outFD < 0) {
+ logcat_panic(HELP_FALSE, "couldn't open output file");
+ }
+
+ struct stat statbuf;
+ if (fstat(g_outFD, &statbuf) == -1) {
+ close(g_outFD);
+ logcat_panic(HELP_FALSE, "couldn't get output file stat\n");
+ }
+
+ if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
+ close(g_outFD);
+ logcat_panic(HELP_FALSE, "invalid output file stat\n");
+ }
+
+ g_outByteCount = statbuf.st_size;
}
static void show_help(const char *cmd)
@@ -253,39 +284,68 @@
fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
fprintf(stderr, "options include:\n"
- " -s Set default filter to silent.\n"
- " Like specifying filterspec '*:S'\n"
- " -f <filename> Log to file. Default is stdout\n"
- " -r <kbytes> Rotate log every kbytes. Requires -f\n"
- " -n <count> Sets max number of rotated logs to <count>, default 4\n"
- " -v <format> Sets the log print format, where <format> is:\n\n"
- " brief color long printable process raw tag thread\n"
- " threadtime time usec\n\n"
- " -D print dividers between each log buffer\n"
- " -c clear (flush) the entire log and exit\n"
- " -d dump the log and then exit (don't block)\n"
- " -t <count> print only the most recent <count> lines (implies -d)\n"
- " -t '<time>' print most recent lines since specified time (implies -d)\n"
- " -T <count> print only the most recent <count> lines (does not imply -d)\n"
- " -T '<time>' print most recent lines since specified time (not imply -d)\n"
- " count is pure numerical, time is 'MM-DD hh:mm:ss.mmm'\n"
- " -g get the size of the log's ring buffer and exit\n"
- " -L dump logs from prior to last reboot\n"
- " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n"
- " 'events', 'crash' or 'all'. Multiple -b parameters are\n"
- " allowed and results are interleaved. The default is\n"
- " -b main -b system -b crash.\n"
- " -B output the log in binary.\n"
- " -S output statistics.\n"
- " -G <size> set size of log ring buffer, may suffix with K or M.\n"
- " -p print prune white and ~black list. Service is specified as\n"
+ " -s Set default filter to silent. Equivalent to filterspec '*:S'\n"
+ " -f <file>, --file=<file> Log to file. Default is stdout\n"
+ " -r <kbytes>, --rotate-kbytes=<kbytes>\n"
+ " Rotate log every kbytes. Requires -f option\n"
+ " -n <count>, --rotate-count=<count>\n"
+ " Sets max number of rotated logs to <count>, default 4\n"
+ " --id=<id> If the signature id for logging to file changes, then clear\n"
+ " the fileset and continue\n"
+ " -v <format>, --format=<format>\n"
+ " Sets log print format verb and adverbs, where <format> is:\n"
+ " brief help long process raw tag thread threadtime time\n"
+ " and individually flagged modifying adverbs can be added:\n"
+ " color descriptive epoch monotonic printable uid\n"
+ " usec UTC year zone\n"
+ // private and undocumented nsec, no signal, too much noise
+ // useful for -T or -t <timestamp> accurate testing though.
+ " -D, --dividers Print dividers between each log buffer\n"
+ " -c, --clear Clear (flush) the entire log and exit\n"
+ " if Log to File specified, clear fileset instead\n"
+ " -d Dump the log and then exit (don't block)\n"
+ " -e <expr>, --regex=<expr>\n"
+ " Only print lines where the log message matches <expr>\n"
+ " where <expr> is a regular expression\n"
+ // Leave --head undocumented as alias for -m
+ " -m <count>, --max-count=<count>\n"
+ " Quit after printing <count> lines. This is meant to be\n"
+ " paired with --regex, but will work on its own.\n"
+ " --print Paired with --regex and --max-count to let content bypass\n"
+ " regex filter but still stop at number of matches.\n"
+ // Leave --tail undocumented as alias for -t
+ " -t <count> Print only the most recent <count> lines (implies -d)\n"
+ " -t '<time>' Print most recent lines since specified time (implies -d)\n"
+ " -T <count> Print only the most recent <count> lines (does not imply -d)\n"
+ " -T '<time>' Print most recent lines since specified time (not imply -d)\n"
+ " count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
+ " 'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
+ " -g, --buffer-size Get the size of the ring buffer.\n"
+ " -G <size>, --buffer-size=<size>\n"
+ " Set size of log ring buffer, may suffix with K or M.\n"
+ " -L, --last Dump logs from prior to last reboot\n"
+ // Leave security (Device Owner only installations) and
+ // kernel (userdebug and eng) buffers undocumented.
+ " -b <buffer>, --buffer=<buffer> Request alternate ring buffer, 'main',\n"
+ " 'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
+ " Multiple -b parameters or comma separated list of buffers are\n"
+ " allowed. Buffers interleaved. Default -b main,system,crash.\n"
+ " -B, --binary Output the log in binary.\n"
+ " -S, --statistics Output statistics.\n"
+ " -p, --prune Print prune white and ~black list. Service is specified as\n"
" UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
" with ~, otherwise weighed for longevity if unadorned. All\n"
" other pruning activity is oldest first. Special case ~!\n"
" represents an automatic quicker pruning for the noisiest\n"
" UID as determined by the current statistics.\n"
- " -P '<list> ...' set prune white and ~black list, using same format as\n"
- " printed above. Must be quoted.\n");
+ " -P '<list> ...', --prune='<list> ...'\n"
+ " Set prune white and ~black list, using same format as\n"
+ " listed above. Must be quoted.\n"
+ " --pid=<pid> Only prints logs from the given pid.\n"
+ // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value for match to 2 hours
+ " --wrap Sleep for 2 hours or when buffer about to wrap whichever\n"
+ " comes first. Improves efficiency of polling by providing\n"
+ " an about-to-wrap wakeup.\n");
fprintf(stderr,"\nfilterspecs are a series of \n"
" <tag>[:priority]\n\n"
@@ -305,6 +365,40 @@
"or defaults to \"threadtime\"\n\n");
}
+static void show_format_help()
+{
+ fprintf(stderr,
+ "-v <format>, --format=<format> options:\n"
+ " Sets log print format verb and adverbs, where <format> is:\n"
+ " brief long process raw tag thread threadtime time\n"
+ " and individually flagged modifying adverbs can be added:\n"
+ " color descriptive epoch monotonic printable uid usec UTC year zone\n"
+ "\nSingle format verbs:\n"
+ " brief — Display priority/tag and PID of the process issuing the message.\n"
+ " long — Display all metadata fields, separate messages with blank lines.\n"
+ " process — Display PID only.\n"
+ " raw — Display the raw log message, with no other metadata fields.\n"
+ " tag — Display the priority/tag only.\n"
+ " threadtime — Display the date, invocation time, priority, tag, and the PID\n"
+ " and TID of the thread issuing the message. (the default format).\n"
+ " time — Display the date, invocation time, priority/tag, and PID of the\n"
+ " process issuing the message.\n"
+ "\nAdverb modifiers can be used in combination:\n"
+ " color — Display in highlighted color to match priority. i.e. \x1B[38;5;231mVERBOSE\n"
+ " \x1B[38;5;75mDEBUG \x1B[38;5;40mINFO \x1B[38;5;166mWARNING \x1B[38;5;196mERROR FATAL\x1B[0m\n"
+ " descriptive — events logs only, descriptions from event-log-tags database.\n"
+ " epoch — Display time as seconds since Jan 1 1970.\n"
+ " monotonic — Display time as cpu seconds since last boot.\n"
+ " printable — Ensure that any binary logging content is escaped.\n"
+ " uid — If permitted, display the UID or Android ID of logged process.\n"
+ " usec — Display time down the microsecond precision.\n"
+ " UTC — Display time as UTC.\n"
+ " year — Add the year to the displayed time.\n"
+ " zone — Add the local timezone to the displayed time.\n"
+ " \"<zone>\" — Print using this public named timezone (experimental).\n\n"
+ );
+}
+
static int setLogFormat(const char * formatString)
{
static AndroidLogPrintFormat format;
@@ -344,18 +438,22 @@
}
/*String to unsigned int, returns -1 if it fails*/
-static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0,
+static bool getSizeTArg(const char *ptr, size_t *val, size_t min = 0,
size_t max = SIZE_MAX)
{
- char *endp;
- errno = 0;
- size_t ret = (size_t) strtoll(ptr, &endp, 0);
-
- if (endp[0] != '\0' || errno != 0 ) {
+ if (!ptr) {
return false;
}
- if (ret > max || ret < min) {
+ char *endp;
+ errno = 0;
+ size_t ret = (size_t)strtoll(ptr, &endp, 0);
+
+ if (endp[0] || errno) {
+ return false;
+ }
+
+ if ((ret > max) || (ret < min)) {
return false;
}
@@ -363,31 +461,48 @@
return true;
}
-static void logcat_panic(bool showHelp, const char *fmt, ...)
+static void logcat_panic(enum helpType showHelp, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
- if (showHelp) {
+ switch (showHelp) {
+ case HELP_TRUE:
show_help(getprogname());
+ break;
+ case HELP_FORMAT:
+ show_format_help();
+ break;
+ case HELP_FALSE:
+ default:
+ break;
}
exit(EXIT_FAILURE);
}
-static const char g_defaultTimeFormat[] = "%m-%d %H:%M:%S.%q";
+static char *parseTime(log_time &t, const char *cp) {
-// Find last logged line in gestalt of all matching existing output files
+ char *ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
+ if (ep) {
+ return ep;
+ }
+ ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
+ if (ep) {
+ return ep;
+ }
+ return t.strptime(cp, "%s.%q");
+}
+
+// Find last logged line in <outputFileName>, or <outputFileName>.1
static log_time lastLogTime(char *outputFileName) {
log_time retval(log_time::EPOCH);
if (!outputFileName) {
return retval;
}
- log_time now(CLOCK_REALTIME);
-
std::string directory;
char *file = strrchr(outputFileName, '/');
if (!file) {
@@ -399,16 +514,25 @@
*file = '/';
++file;
}
+
+ std::unique_ptr<DIR, int(*)(DIR*)>
+ dir(opendir(directory.c_str()), closedir);
+ if (!dir.get()) {
+ return retval;
+ }
+
+ log_time now(android_log_clockid());
+
size_t len = strlen(file);
log_time modulo(0, NS_PER_SEC);
- std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(directory.c_str()), closedir);
struct dirent *dp;
+
while ((dp = readdir(dir.get())) != NULL) {
- if ((dp->d_type != DT_REG)
- || strncmp(dp->d_name, file, len)
- || (dp->d_name[len]
- && ((dp->d_name[len] != '.')
- || !isdigit(dp->d_name[len+1])))) {
+ if ((dp->d_type != DT_REG) ||
+ (strncmp(dp->d_name, file, len) != 0) ||
+ (dp->d_name[len] &&
+ ((dp->d_name[len] != '.') ||
+ (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
continue;
}
@@ -423,7 +547,7 @@
bool found = false;
for (const auto& line : android::base::Split(file, "\n")) {
log_time t(log_time::EPOCH);
- char *ep = t.strptime(line.c_str(), g_defaultTimeFormat);
+ char *ep = parseTime(t, line.c_str());
if (!ep || (*ep != ' ')) {
continue;
}
@@ -459,26 +583,41 @@
} /* namespace android */
+void reportErrorName(const char **current,
+ const char* name,
+ bool blockSecurity) {
+ if (*current) {
+ return;
+ }
+ if (blockSecurity && (android_name_to_log_id(name) == LOG_ID_SECURITY)) {
+ return;
+ }
+ *current = name;
+}
int main(int argc, char **argv)
{
using namespace android;
int err;
int hasSetLogFormat = 0;
- int clearLog = 0;
- int getLogSize = 0;
+ bool clearLog = false;
+ bool allSelected = false;
+ bool getLogSize = false;
+ bool getPruneList = false;
+ bool printStatistics = false;
+ bool printDividers = false;
unsigned long setLogSize = 0;
- int getPruneList = 0;
char *setPruneList = NULL;
- int printStatistics = 0;
+ char *setId = NULL;
int mode = ANDROID_LOG_RDONLY;
const char *forceFilters = NULL;
log_device_t* devices = NULL;
log_device_t* dev;
- bool printDividers = false;
struct logger_list *logger_list;
size_t tail_lines = 0;
log_time tail_time(log_time::EPOCH);
+ size_t pid = 0;
+ bool got_t = false;
signal(SIGPIPE, exit);
@@ -492,25 +631,105 @@
for (;;) {
int ret;
- ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:");
+ int option_index = 0;
+ // list of long-argument only strings for later comparison
+ static const char pid_str[] = "pid";
+ static const char debug_str[] = "debug";
+ static const char id_str[] = "id";
+ static const char wrap_str[] = "wrap";
+ static const char print_str[] = "print";
+ static const struct option long_options[] = {
+ { "binary", no_argument, NULL, 'B' },
+ { "buffer", required_argument, NULL, 'b' },
+ { "buffer-size", optional_argument, NULL, 'g' },
+ { "clear", no_argument, NULL, 'c' },
+ { debug_str, no_argument, NULL, 0 },
+ { "dividers", no_argument, NULL, 'D' },
+ { "file", required_argument, NULL, 'f' },
+ { "format", required_argument, NULL, 'v' },
+ // hidden and undocumented reserved alias for --regex
+ { "grep", required_argument, NULL, 'e' },
+ // hidden and undocumented reserved alias for --max-count
+ { "head", required_argument, NULL, 'm' },
+ { id_str, required_argument, NULL, 0 },
+ { "last", no_argument, NULL, 'L' },
+ { "max-count", required_argument, NULL, 'm' },
+ { pid_str, required_argument, NULL, 0 },
+ { print_str, no_argument, NULL, 0 },
+ { "prune", optional_argument, NULL, 'p' },
+ { "regex", required_argument, NULL, 'e' },
+ { "rotate-count", required_argument, NULL, 'n' },
+ { "rotate-kbytes", required_argument, NULL, 'r' },
+ { "statistics", no_argument, NULL, 'S' },
+ // hidden and undocumented reserved alias for -t
+ { "tail", required_argument, NULL, 't' },
+ // support, but ignore and do not document, the optional argument
+ { wrap_str, optional_argument, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+ };
+
+ ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
+ long_options, &option_index);
if (ret < 0) {
break;
}
- switch(ret) {
+ switch (ret) {
+ case 0:
+ // only long options
+ if (long_options[option_index].name == pid_str) {
+ // ToDo: determine runtime PID_MAX?
+ if (!getSizeTArg(optarg, &pid, 1)) {
+ logcat_panic(HELP_TRUE, "%s %s out of range\n",
+ long_options[option_index].name, optarg);
+ }
+ break;
+ }
+ if (long_options[option_index].name == wrap_str) {
+ mode |= ANDROID_LOG_WRAP |
+ ANDROID_LOG_RDONLY |
+ ANDROID_LOG_NONBLOCK;
+ // ToDo: implement API that supports setting a wrap timeout
+ size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
+ if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
+ logcat_panic(HELP_TRUE, "%s %s out of range\n",
+ long_options[option_index].name, optarg);
+ }
+ if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
+ fprintf(stderr,
+ "WARNING: %s %u seconds, ignoring %zu\n",
+ long_options[option_index].name,
+ ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
+ }
+ break;
+ }
+ if (long_options[option_index].name == print_str) {
+ g_printItAnyways = true;
+ break;
+ }
+ if (long_options[option_index].name == debug_str) {
+ g_debug = true;
+ break;
+ }
+ if (long_options[option_index].name == id_str) {
+ setId = optarg && optarg[0] ? optarg : NULL;
+ break;
+ }
+ break;
+
case 's':
// default to all silent
android_log_addFilterRule(g_logformat, "*:s");
break;
case 'c':
- clearLog = 1;
+ clearLog = true;
mode |= ANDROID_LOG_WRONLY;
break;
case 'L':
- mode |= ANDROID_LOG_PSTORE;
+ mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK;
break;
case 'd':
@@ -518,15 +737,16 @@
break;
case 't':
+ got_t = true;
mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
/* FALLTHRU */
case 'T':
if (strspn(optarg, "0123456789") != strlen(optarg)) {
- char *cp = tail_time.strptime(optarg, g_defaultTimeFormat);
+ char *cp = parseTime(tail_time, optarg);
if (!cp) {
- logcat_panic(false,
- "-%c \"%s\" not in \"%s\" time format\n",
- ret, optarg, g_defaultTimeFormat);
+ logcat_panic(HELP_FALSE,
+ "-%c \"%s\" not in time format\n",
+ ret, optarg);
}
if (*cp) {
char c = *cp;
@@ -550,10 +770,26 @@
printDividers = true;
break;
- case 'g':
- getLogSize = 1;
+ case 'e':
+ g_regex = new pcrecpp::RE(optarg);
break;
+ case 'm': {
+ char *end = NULL;
+ if (!getSizeTArg(optarg, &g_maxCount)) {
+ logcat_panic(HELP_FALSE, "-%c \"%s\" isn't an "
+ "integer greater than zero\n", ret, optarg);
+ }
+ }
+ break;
+
+ case 'g':
+ if (!optarg) {
+ getLogSize = true;
+ break;
+ }
+ // FALLTHRU
+
case 'G': {
char *cp;
if (strtoll(optarg, &cp, 0) > 0) {
@@ -590,57 +826,77 @@
break;
case 'p':
- getPruneList = 1;
- break;
+ if (!optarg) {
+ getPruneList = true;
+ break;
+ }
+ // FALLTHRU
case 'P':
setPruneList = optarg;
break;
case 'b': {
- if (strcmp(optarg, "all") == 0) {
- while (devices) {
- dev = devices;
- devices = dev->next;
- delete dev;
- }
+ unsigned idMask = 0;
+ while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
+ if (strcmp(optarg, "default") == 0) {
+ idMask |= (1 << LOG_ID_MAIN) |
+ (1 << LOG_ID_SYSTEM) |
+ (1 << LOG_ID_CRASH);
+ } else if (strcmp(optarg, "all") == 0) {
+ allSelected = true;
+ idMask = (unsigned)-1;
+ } else {
+ log_id_t log_id = android_name_to_log_id(optarg);
+ const char *name = android_log_id_to_name(log_id);
- devices = dev = NULL;
- g_devCount = 0;
- for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
- const char *name = android_log_id_to_name((log_id_t)i);
- log_id_t log_id = android_name_to_log_id(name);
-
- if (log_id != (log_id_t)i) {
- continue;
+ if (strcmp(name, optarg) != 0) {
+ logcat_panic(HELP_TRUE,
+ "unknown buffer %s\n", optarg);
}
-
- bool binary = strcmp(name, "events") == 0;
- log_device_t* d = new log_device_t(name, binary);
-
- if (dev) {
- dev->next = d;
- dev = d;
- } else {
- devices = dev = d;
- }
- g_devCount++;
+ if (log_id == LOG_ID_SECURITY) allSelected = false;
+ idMask |= (1 << log_id);
}
- break;
+ optarg = NULL;
}
- bool binary = strcmp(optarg, "events") == 0;
+ for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ const char *name = android_log_id_to_name((log_id_t)i);
+ log_id_t log_id = android_name_to_log_id(name);
- if (devices) {
- dev = devices;
- while (dev->next) {
- dev = dev->next;
+ if (log_id != (log_id_t)i) {
+ continue;
}
- dev->next = new log_device_t(optarg, binary);
- } else {
- devices = new log_device_t(optarg, binary);
+ if ((idMask & (1 << i)) == 0) {
+ continue;
+ }
+
+ bool found = false;
+ for (dev = devices; dev; dev = dev->next) {
+ if (!strcmp(name, dev->device)) {
+ found = true;
+ break;
+ }
+ if (!dev->next) {
+ break;
+ }
+ }
+ if (found) {
+ continue;
+ }
+
+ bool binary = !strcmp(name, "events") ||
+ !strcmp(name, "security");
+ log_device_t* d = new log_device_t(name, binary);
+
+ if (dev) {
+ dev->next = d;
+ dev = d;
+ } else {
+ devices = dev = d;
+ }
+ g_devCount++;
}
- g_devCount++;
}
break;
@@ -649,7 +905,7 @@
break;
case 'f':
- if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) {
+ if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
tail_time = lastLogTime(optarg);
}
// redirect output to a file
@@ -658,20 +914,27 @@
case 'r':
if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
- logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
+ logcat_panic(HELP_TRUE,
+ "Invalid parameter \"%s\" to -r\n", optarg);
}
break;
case 'n':
if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
- logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
+ logcat_panic(HELP_TRUE,
+ "Invalid parameter \"%s\" to -n\n", optarg);
}
break;
case 'v':
- err = setLogFormat (optarg);
+ if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
+ show_format_help();
+ exit(0);
+ }
+ err = setLogFormat(optarg);
if (err < 0) {
- logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
+ logcat_panic(HELP_FORMAT,
+ "Invalid parameter \"%s\" to -v\n", optarg);
}
hasSetLogFormat |= err;
break;
@@ -745,19 +1008,35 @@
break;
case 'S':
- printStatistics = 1;
+ printStatistics = true;
break;
case ':':
- logcat_panic(true, "Option -%c needs an argument\n", optopt);
+ logcat_panic(HELP_TRUE,
+ "Option -%c needs an argument\n", optopt);
break;
default:
- logcat_panic(true, "Unrecognized Option %c\n", optopt);
+ logcat_panic(HELP_TRUE,
+ "Unrecognized Option %c\n", optopt);
break;
}
}
+ if (g_maxCount && got_t) {
+ logcat_panic(HELP_TRUE,
+ "Cannot use -m (--max-count) and -t together\n");
+ }
+ if (g_printItAnyways && (!g_regex || !g_maxCount)) {
+ // One day it would be nice if --print -v color and --regex <expr>
+ // could play with each other and show regex highlighted content.
+ fprintf(stderr, "WARNING: "
+ "--print ignored, to be used in combination with\n"
+ " "
+ "--regex <expr> and --max-count <N>\n");
+ g_printItAnyways = false;
+ }
+
if (!devices) {
dev = devices = new log_device_t("main", false);
g_devCount = 1;
@@ -772,10 +1051,23 @@
}
if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
- logcat_panic(true, "-r requires -f as well\n");
+ logcat_panic(HELP_TRUE, "-r requires -f as well\n");
}
- setupOutput();
+ if (setId != NULL) {
+ if (g_outputFileName == NULL) {
+ logcat_panic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
+ }
+
+ std::string file_name = android::base::StringPrintf("%s.id", g_outputFileName);
+ std::string file;
+ bool file_ok = android::base::ReadFileToString(file_name, &file);
+ android::base::WriteStringToFile(setId, file_name,
+ S_IRUSR | S_IWUSR, getuid(), getgid());
+ if (!file_ok || (file.compare(setId) == 0)) {
+ setId = NULL;
+ }
+ }
if (hasSetLogFormat == 0) {
const char* logFormat = getenv("ANDROID_PRINTF_LOG");
@@ -794,7 +1086,8 @@
if (forceFilters) {
err = android_log_addFilterString(g_logformat, forceFilters);
if (err < 0) {
- logcat_panic(false, "Invalid filter expression in logcat args\n");
+ logcat_panic(HELP_FALSE,
+ "Invalid filter expression in logcat args\n");
}
} else if (argc == optind) {
// Add from environment variable
@@ -804,7 +1097,7 @@
err = android_log_addFilterString(g_logformat, env_tags_orig);
if (err < 0) {
- logcat_panic(true,
+ logcat_panic(HELP_TRUE,
"Invalid filter expression in ANDROID_LOG_TAGS\n");
}
}
@@ -814,60 +1107,108 @@
err = android_log_addFilterString(g_logformat, argv[i]);
if (err < 0) {
- logcat_panic(true, "Invalid filter expression '%s'\n", argv[i]);
+ logcat_panic(HELP_TRUE,
+ "Invalid filter expression '%s'\n", argv[i]);
}
}
}
dev = devices;
if (tail_time != log_time::EPOCH) {
- logger_list = android_logger_list_alloc_time(mode, tail_time, 0);
+ logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
} else {
- logger_list = android_logger_list_alloc(mode, tail_lines, 0);
+ logger_list = android_logger_list_alloc(mode, tail_lines, pid);
}
+ const char *openDeviceFail = NULL;
+ const char *clearFail = NULL;
+ const char *setSizeFail = NULL;
+ const char *getSizeFail = NULL;
+ // We have three orthogonal actions below to clear, set log size and
+ // get log size. All sharing the same iteration loop.
while (dev) {
dev->logger_list = logger_list;
dev->logger = android_logger_open(logger_list,
android_name_to_log_id(dev->device));
if (!dev->logger) {
- logcat_panic(false, "Unable to open log device '%s'\n",
- dev->device);
+ reportErrorName(&openDeviceFail, dev->device, allSelected);
+ dev = dev->next;
+ continue;
}
- if (clearLog) {
- int ret;
- ret = android_logger_clear(dev->logger);
- if (ret) {
- logcat_panic(false, "failed to clear the log");
+ if (clearLog || setId) {
+ if (g_outputFileName) {
+ int maxRotationCountDigits =
+ (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+
+ for (int i = g_maxRotatedLogs ; i >= 0 ; --i) {
+ std::string file;
+
+ if (i == 0) {
+ file = android::base::StringPrintf("%s", g_outputFileName);
+ } else {
+ file = android::base::StringPrintf("%s.%.*d",
+ g_outputFileName, maxRotationCountDigits, i);
+ }
+
+ if (file.length() == 0) {
+ perror("while clearing log files");
+ reportErrorName(&clearFail, dev->device, allSelected);
+ break;
+ }
+
+ err = unlink(file.c_str());
+
+ if (err < 0 && errno != ENOENT && clearFail == NULL) {
+ perror("while clearing log files");
+ reportErrorName(&clearFail, dev->device, allSelected);
+ }
+ }
+ } else if (android_logger_clear(dev->logger)) {
+ reportErrorName(&clearFail, dev->device, allSelected);
}
}
- if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
- logcat_panic(false, "failed to set the log size");
+ if (setLogSize) {
+ if (android_logger_set_log_size(dev->logger, setLogSize)) {
+ reportErrorName(&setSizeFail, dev->device, allSelected);
+ }
}
if (getLogSize) {
- long size, readable;
+ long size = android_logger_get_log_size(dev->logger);
+ long readable = android_logger_get_log_readable_size(dev->logger);
- size = android_logger_get_log_size(dev->logger);
- if (size < 0) {
- logcat_panic(false, "failed to get the log size");
+ if ((size < 0) || (readable < 0)) {
+ reportErrorName(&getSizeFail, dev->device, allSelected);
+ } else {
+ printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
+ "max entry is %db, max payload is %db\n", dev->device,
+ value_of_size(size), multiplier_of_size(size),
+ value_of_size(readable), multiplier_of_size(readable),
+ (int) LOGGER_ENTRY_MAX_LEN,
+ (int) LOGGER_ENTRY_MAX_PAYLOAD);
}
-
- readable = android_logger_get_log_readable_size(dev->logger);
- if (readable < 0) {
- logcat_panic(false, "failed to get the readable log size");
- }
-
- printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
- "max entry is %db, max payload is %db\n", dev->device,
- value_of_size(size), multiplier_of_size(size),
- value_of_size(readable), multiplier_of_size(readable),
- (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
}
dev = dev->next;
}
+ // report any errors in the above loop and exit
+ if (openDeviceFail) {
+ logcat_panic(HELP_FALSE,
+ "Unable to open log device '%s'\n", openDeviceFail);
+ }
+ if (clearFail) {
+ logcat_panic(HELP_FALSE,
+ "failed to clear the '%s' log\n", clearFail);
+ }
+ if (setSizeFail) {
+ logcat_panic(HELP_FALSE,
+ "failed to set the '%s' log size\n", setSizeFail);
+ }
+ if (getSizeFail) {
+ logcat_panic(HELP_FALSE,
+ "failed to get the readable '%s' log size", getSizeFail);
+ }
if (setPruneList) {
size_t len = strlen(setPruneList);
@@ -877,11 +1218,11 @@
if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
buf[len] = '\0';
if (android_logger_set_prune_list(logger_list, buf, bLen)) {
- logcat_panic(false, "failed to set the prune list");
+ logcat_panic(HELP_FALSE, "failed to set the prune list");
}
free(buf);
} else {
- logcat_panic(false, "failed to set the prune list (alloc)");
+ logcat_panic(HELP_FALSE, "failed to set the prune list (alloc)");
}
}
@@ -889,7 +1230,7 @@
size_t len = 8192;
char *buf;
- for(int retry = 32;
+ for (int retry = 32;
(retry >= 0) && ((buf = new char [len]));
delete [] buf, buf = NULL, --retry) {
if (getPruneList) {
@@ -912,7 +1253,7 @@
}
if (!buf) {
- logcat_panic(false, "failed to read data");
+ logcat_panic(HELP_FALSE, "failed to read data");
}
// remove trailing FF
@@ -939,7 +1280,6 @@
return EXIT_SUCCESS;
}
-
if (getLogSize) {
return EXIT_SUCCESS;
}
@@ -950,19 +1290,22 @@
return EXIT_SUCCESS;
}
+ setupOutputAndSchedulingPolicy((mode & ANDROID_LOG_NONBLOCK) == 0);
+
//LOG_EVENT_INT(10, 12345);
//LOG_EVENT_LONG(11, 0x1122334455667788LL);
//LOG_EVENT_STRING(0, "whassup, doc?");
dev = NULL;
log_device_t unexpected("unexpected", false);
- while (1) {
+
+ while (!g_maxCount || (g_printCount < g_maxCount)) {
struct log_msg log_msg;
log_device_t* d;
int ret = android_logger_list_read(logger_list, &log_msg);
if (ret == 0) {
- logcat_panic(false, "read: unexpected EOF!\n");
+ logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
}
if (ret < 0) {
@@ -971,15 +1314,15 @@
}
if (ret == -EIO) {
- logcat_panic(false, "read: unexpected EOF!\n");
+ logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
}
if (ret == -EINVAL) {
- logcat_panic(false, "read: unexpected length.\n");
+ logcat_panic(HELP_FALSE, "read: unexpected length.\n");
}
- logcat_panic(false, "logcat read failure");
+ logcat_panic(HELP_FALSE, "logcat read failure");
}
- for(d = devices; d; d = d->next) {
+ for (d = devices; d; d = d->next) {
if (android_name_to_log_id(d->device) == log_msg.id()) {
break;
}
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
new file mode 100644
index 0000000..b082a64
--- /dev/null
+++ b/logcat/logcatd.rc
@@ -0,0 +1,66 @@
+#
+# init scriptures for logcatd persistent logging.
+#
+# Make sure any property changes are only performed with /data mounted, after
+# post-fs-data state because otherwise behavior is undefined. The exceptions
+# are device adjustments for logcatd service properties (persist.* overrides
+# notwithstanding) for logd.logpersistd.size and logd.logpersistd.buffer.
+
+# persist to non-persistent trampolines to permit device properties can be
+# overridden when /data mounts, or during runtime.
+on property:persist.logd.logpersistd.size=256
+ setprop persist.logd.logpersistd.size ""
+ setprop logd.logpersistd.size ""
+
+on property:persist.logd.logpersistd.size=*
+ # expect /init to report failure if property empty (default)
+ setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
+
+on property:persist.logd.logpersistd.buffer=all
+ setprop persist.logd.logpersistd.buffer ""
+ setprop logd.logpersistd.buffer ""
+
+on property:persist.logd.logpersistd.buffer=*
+ # expect /init to report failure if property empty (default)
+ setprop logd.logpersistd.buffer ${persist.logd.logpersistd.buffer}
+
+on property:persist.logd.logpersistd=logcatd
+ setprop logd.logpersistd logcatd
+
+# enable, prep and start logcatd service
+on load_persist_props_action
+ setprop logd.logpersistd.enable true
+
+on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
+ # all exec/services are called with umask(077), so no gain beyond 0700
+ mkdir /data/misc/logd 0700 logd log
+ # logd for write to /data/misc/logd, log group for read from pstore (-L)
+ # b/28788401 b/30041146 b/30612424
+ # exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+ start logcatd
+
+# stop logcatd service and clear data
+on property:logd.logpersistd.enable=true && property:logd.logpersistd=clear
+ setprop persist.logd.logpersistd ""
+ stop logcatd
+ # logd for clear of only our files in /data/misc/logd
+ exec - logd log -- /system/bin/logcat -c -f /data/misc/logd/logcat -n ${logd.logpersistd.size:-256}
+ setprop logd.logpersistd ""
+
+# stop logcatd service
+on property:logd.logpersistd=stop
+ setprop persist.logd.logpersistd ""
+ stop logcatd
+ setprop logd.logpersistd ""
+
+on property:logd.logpersistd.enable=false
+ stop logcatd
+
+# logcatd service
+service logcatd /system/bin/logcat -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+ class late_start
+ disabled
+ # logd for write to /data/misc/logd, log group for read from log daemon
+ user logd
+ group log
+ writepid /dev/cpuset/system-background/tasks
diff --git a/logcat/logpersist b/logcat/logpersist
new file mode 100755
index 0000000..c09b6b2
--- /dev/null
+++ b/logcat/logpersist
@@ -0,0 +1,178 @@
+#! /system/bin/sh
+# logpersist cat, start and stop handlers
+progname="${0##*/}"
+case `getprop ro.debuggable` in
+1) ;;
+*) echo "${progname} - Permission denied"
+ exit 1
+ ;;
+esac
+
+property=persist.logd.logpersistd
+
+case `getprop ${property#persist.}.enable` in
+true) ;;
+*) echo "${progname} - Disabled"
+ exit 1
+ ;;
+esac
+
+log_uid=logd
+log_tag_property=persist.log.tag
+data=/data/misc/logd/logcat
+service=logcatd
+size_default=256
+buffer_default=all
+args="${@}"
+
+size=${size_default}
+buffer=${buffer_default}
+clear=false
+while [ ${#} -gt 0 ]; do
+ case ${1} in
+ -c|--clear) clear=true ;;
+ --size=*) size="${1#--size=}" ;;
+ --rotate-count=*) size="${1#--rotate-count=}" ;;
+ -n|--size|--rotate-count) size="${2}" ; shift ;;
+ --buffer=*) buffer="${1#--buffer=}" ;;
+ -b|--buffer) buffer="${2}" ; shift ;;
+ -h|--help|*)
+ LEAD_SPACE_="`echo ${progname%.*} | tr '[ -~]' ' '`"
+ echo "${progname%.*}.cat - dump current ${service} logs"
+ echo "${progname%.*}.start [--size=<size_in_kb>] [--buffer=<buffers>] [--clear]"
+ echo "${LEAD_SPACE_} - start ${service} service"
+ echo "${progname%.*}.stop [--clear] - stop ${service} service"
+ case ${1} in
+ -h|--help) exit 0 ;;
+ *) echo ERROR: bad argument ${@} >&2 ; exit 1 ;;
+ esac
+ ;;
+ esac
+ shift
+done
+
+if [ -z "${size}" -o "${size_default}" = "${size}" ]; then
+ unset size
+fi
+if [ -n "${size}" ] &&
+ ! ( [ 0 -lt "${size}" ] && [ 2048 -ge "${size}" ] ) >/dev/null 2>&1; then
+ echo ERROR: Invalid --size ${size} >&2
+ exit 1
+fi
+if [ -z "${buffer}" -o "${buffer_default}" = "${buffer}" ]; then
+ unset buffer
+fi
+if [ -n "${buffer}" ] && ! logcat -b ${buffer} -g >/dev/null 2>&1; then
+ echo ERROR: Invalid --buffer ${buffer} >&2
+ exit 1
+fi
+
+log_tag="`getprop ${log_tag_property}`"
+logd_logpersistd="`getprop ${property}`"
+
+case ${progname} in
+*.cat)
+ if [ -n "${size}${buffer}" -o "true" = "${clear}" ]; then
+ echo WARNING: Can not use --clear, --size or --buffer with ${progname%.*}.cat >&2
+ fi
+ su ${log_uid} ls "${data%/*}" |
+ tr -d '\r' |
+ sort -ru |
+ sed "s#^#${data%/*}/#" |
+ grep "${data}[.]*[0-9]*\$" |
+ su ${log_uid} xargs cat
+ ;;
+*.start)
+ current_buffer="`getprop ${property#persist.}.buffer`"
+ current_size="`getprop ${property#persist.}.size`"
+ if [ "${service}" = "`getprop ${property#persist.}`" ]; then
+ if [ "true" = "${clear}" ]; then
+ setprop ${property#persist.} "clear"
+ elif [ "${buffer}|${size}" != "${current_buffer}|${current_size}" ]; then
+ echo "ERROR: Changing existing collection parameters from" >&2
+ if [ "${buffer}" != "${current_buffer}" ]; then
+ a=${current_buffer}
+ b=${buffer}
+ if [ -z "${a}" ]; then a="${default_buffer}"; fi
+ if [ -z "${b}" ]; then b="${default_buffer}"; fi
+ echo " --buffer ${a} to ${b}" >&2
+ fi
+ if [ "${size}" != "${current_size}" ]; then
+ a=${current_size}
+ b=${size}
+ if [ -z "${a}" ]; then a="${default_size}"; fi
+ if [ -z "${b}" ]; then b="${default_size}"; fi
+ echo " --size ${a} to ${b}" >&2
+ fi
+ echo " Are you sure you want to do this?" >&2
+ echo " Suggest add --clear to erase data and restart with new settings." >&2
+ echo " To blindly override and retain data, ${progname%.*}.stop first." >&2
+ exit 1
+ fi
+ elif [ "true" = "${clear}" ]; then
+ setprop ${property#persist.} "clear"
+ fi
+ if [ -n "${buffer}${current_buffer}" ]; then
+ setprop ${property}.buffer "${buffer}"
+ if [ -z "${buffer}" ]; then
+ # deal with trampoline for empty properties
+ setprop ${property#persist.}.buffer ""
+ fi
+ fi
+ if [ -n "${size}${current_size}" ]; then
+ setprop ${property}.size "${size}"
+ if [ -z "${size}" ]; then
+ # deal with trampoline for empty properties
+ setprop ${property#persist.}.size ""
+ fi
+ fi
+ while [ "clear" = "`getprop ${property#persist.}`" ]; do
+ continue
+ done
+ # Tell Settings that we are back on again if we turned logging off
+ tag="${log_tag#Settings}"
+ if [ X"${log_tag}" != X"${tag}" ]; then
+ echo "WARNING: enabling logd service" >&2
+ setprop ${log_tag_property} "${tag#,}"
+ fi
+ # ${service}.rc does the heavy lifting with the following trigger
+ setprop ${property} ${service}
+ # 20ms done, to permit process feedback check
+ sleep 1
+ getprop ${property#persist.}
+ # also generate an error return code if not found running
+ pgrep -u ${log_uid} ${service%d}
+ ;;
+*.stop)
+ if [ -n "${size}${buffer}" ]; then
+ echo "WARNING: Can not use --size or --buffer with ${progname%.*}.stop" >&2
+ fi
+ if [ "true" = "${clear}" ]; then
+ setprop ${property} "clear"
+ else
+ setprop ${property} "stop"
+ fi
+ if [ -n "`getprop ${property#persist.}.buffer`" ]; then
+ setprop ${property}.buffer ""
+ # deal with trampoline for empty properties
+ setprop ${property#persist.}.buffer ""
+ fi
+ if [ -n "`getprop ${property#persist.}.size`" ]; then
+ setprop ${property}.size ""
+ # deal with trampoline for empty properties
+ setprop ${property#persist.}.size ""
+ fi
+ while [ "clear" = "`getprop ${property#persist.}`" ]; do
+ continue
+ done
+ ;;
+*)
+ echo "ERROR: Unexpected command ${0##*/} ${args}" >&2
+ exit 1
+esac
+
+if [ X"${log_tag}" != X"`getprop ${log_tag_property}`" ] ||
+ [ X"${logd_logpersistd}" != X"`getprop ${property}`" ]; then
+ echo "WARNING: killing Settings application to pull in new values" >&2
+ am force-stop com.android.settings
+fi
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index a28664e..cb8b061 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -25,7 +25,6 @@
-Wall -Wextra \
-Werror \
-fno-builtin \
- -std=gnu++11
# -----------------------------------------------------------------------------
# Benchmarks (actually a gTest where the result code does not matter)
@@ -56,6 +55,6 @@
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/logcat_benchmark.cpp b/logcat/tests/logcat_benchmark.cpp
index be815be..dd85164 100644
--- a/logcat/tests/logcat_benchmark.cpp
+++ b/logcat/tests/logcat_benchmark.cpp
@@ -49,7 +49,7 @@
}
}
- timestamp(const char *buffer)
+ explicit timestamp(const char *buffer)
{
init(buffer);
}
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index de2db67..081bf92 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -15,15 +15,25 @@
*/
#include <ctype.h>
+#include <dirent.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
#include <gtest/gtest.h>
#include <log/log.h>
-#include <log/logger.h>
-#include <log/log_read.h>
+#include <log/log_event_list.h>
+
+#define BIG_BUFFER (5 * 1024)
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
// non-syscall libs. Since we are only using this in the emergency of
@@ -45,11 +55,16 @@
TEST(logcat, buckets) {
FILE *fp;
+#undef LOG_TAG
+#define LOG_TAG "inject"
+ RLOGE("logcat.buckets");
+ sleep(1);
+
ASSERT_TRUE(NULL != (fp = popen(
"logcat -b radio -b events -b system -b main -d 2>/dev/null",
"r")));
- char buffer[5120];
+ char buffer[BIG_BUFFER];
int ids = 0;
int count = 0;
@@ -72,164 +87,344 @@
EXPECT_EQ(4, count);
}
-TEST(logcat, tail_3) {
+TEST(logcat, event_tag_filter) {
FILE *fp;
ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v long -b radio -b events -b system -b main -t 3 2>/dev/null",
+ "logcat -b events -d -s auditd am_proc_start am_pss am_proc_bound dvm_lock_sample am_wtf 2>/dev/null",
"r")));
- char buffer[5120];
+ char buffer[BIG_BUFFER];
int count = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')) {
- ++count;
- }
+ ++count;
}
pclose(fp);
+ EXPECT_LT(4, count);
+}
+
+// If there is not enough background noise in the logs, then spam the logs to
+// permit tail checking so that the tests can progress.
+static size_t inject(ssize_t count) {
+ if (count <= 0) return 0;
+
+ static const size_t retry = 4;
+ size_t errors = retry;
+ size_t num = 0;
+ for(;;) {
+ log_time ts(CLOCK_MONOTONIC);
+ if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) {
+ if (++num >= (size_t)count) {
+ // let data settle end-to-end
+ sleep(3);
+ return num;
+ }
+ errors = retry;
+ usleep(100); // ~32 per timer tick, we are a spammer regardless
+ } else if (--errors <= 0) {
+ return num;
+ }
+ }
+ // NOTREACH
+ return num;
+}
+
+TEST(logcat, year) {
+
+ if (android_log_clockid() == CLOCK_MONOTONIC) {
+ fprintf(stderr, "Skipping test, logd is monotonic time\n");
+ return;
+ }
+
+ int count;
+ int tries = 3; // in case run too soon after system start or buffer clear
+
+ do {
+ FILE *fp;
+
+ char needle[32];
+ time_t now;
+ time(&now);
+ struct tm *ptm;
+#if !defined(_WIN32)
+ struct tm tmBuf;
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&&now);
+#endif
+ strftime(needle, sizeof(needle), "[ %Y-", ptm);
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v long -v year -b all -t 3 2>/dev/null",
+ "r")));
+
+ char buffer[BIG_BUFFER];
+
+ count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(buffer, needle, strlen(needle))) {
+ ++count;
+ }
+ }
+ pclose(fp);
+
+ } while ((count < 3) && --tries && inject(3 - count));
+
ASSERT_EQ(3, count);
}
-TEST(logcat, tail_10) {
+// Return a pointer to each null terminated -v long time field.
+char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) {
+ while (fgets(buffer, buflen, fp)) {
+ char *cp = buffer;
+ if (*cp != '[') {
+ continue;
+ }
+ while (*++cp == ' ') {
+ ;
+ }
+ char *ep = cp;
+ while (isdigit(*ep)) {
+ ++ep;
+ }
+ if ((*ep != '-') && (*ep != '.')) {
+ continue;
+ }
+ // Find PID field
+ while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) {
+ ;
+ }
+ if (!ep) {
+ continue;
+ }
+ static const size_t pid_field_width = 7;
+ ep -= pid_field_width;
+ *ep = '\0';
+ return cp;
+ }
+ return NULL;
+}
+
+TEST(logcat, tz) {
+
+ if (android_log_clockid() == CLOCK_MONOTONIC) {
+ fprintf(stderr, "Skipping test, logd is monotonic time\n");
+ return;
+ }
+
+ int tries = 4; // in case run too soon after system start or buffer clear
+ int count;
+
+ do {
+ FILE *fp;
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
+ "r")));
+
+ char buffer[BIG_BUFFER];
+
+ count = 0;
+
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
+ ++count;
+ } else {
+ fprintf(stderr, "ts=\"%s\"\n", buffer + 2);
+ }
+ }
+
+ pclose(fp);
+
+ } while ((count < 3) && --tries && inject(3 - count));
+
+ ASSERT_EQ(3, count);
+}
+
+TEST(logcat, ntz) {
FILE *fp;
ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v long -b radio -b events -b system -b main -t 10 2>/dev/null",
+ "logcat -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
"r")));
- char buffer[5120];
+ char buffer[BIG_BUFFER];
int count = 0;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')) {
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
++count;
}
}
pclose(fp);
- ASSERT_EQ(10, count);
+ ASSERT_EQ(0, count);
+}
+
+void do_tail(int num) {
+ int tries = 4; // in case run too soon after system start or buffer clear
+ int count;
+
+ if (num > 10) ++tries;
+ if (num > 100) ++tries;
+ do {
+ char buffer[BIG_BUFFER];
+
+ snprintf(buffer, sizeof(buffer),
+ "logcat -v long -b all -t %d 2>/dev/null", num);
+
+ FILE *fp;
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ count = 0;
+
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ ++count;
+ }
+
+ pclose(fp);
+
+ } while ((count < num) && --tries && inject(num - count));
+
+ ASSERT_EQ(num, count);
+}
+
+TEST(logcat, tail_3) {
+ do_tail(3);
+}
+
+TEST(logcat, tail_10) {
+ do_tail(10);
}
TEST(logcat, tail_100) {
- FILE *fp;
-
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v long -b radio -b events -b system -b main -t 100 2>/dev/null",
- "r")));
-
- char buffer[5120];
-
- int count = 0;
-
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')) {
- ++count;
- }
- }
-
- pclose(fp);
-
- ASSERT_EQ(100, count);
+ do_tail(100);
}
TEST(logcat, tail_1000) {
- FILE *fp;
-
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v long -b radio -b events -b system -b main -t 1000 2>/dev/null",
- "r")));
-
- char buffer[5120];
-
- int count = 0;
-
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')) {
- ++count;
- }
- }
-
- pclose(fp);
-
- ASSERT_EQ(1000, count);
+ do_tail(1000);
}
TEST(logcat, tail_time) {
FILE *fp;
-
- ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
-
- char buffer[5120];
+ int count;
+ char buffer[BIG_BUFFER];
char *last_timestamp = NULL;
+ // Hard to predict 100% if first (overlap) or second line will match.
+ // -v nsec will in a substantial majority be the second line.
char *first_timestamp = NULL;
- int count = 0;
- const unsigned int time_length = 18;
- const unsigned int time_offset = 2;
+ char *second_timestamp = NULL;
+ char *input;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
- && (buffer[time_offset + 2] == '-')) {
+ int tries = 4; // in case run too soon after system start or buffer clear
+
+ do {
+ ASSERT_TRUE(NULL != (fp = popen("logcat"
+ " -v long"
+ " -v nsec"
+ " -b all"
+ " -t 10"
+ " 2>&1", "r")));
+ count = 0;
+
+ while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
++count;
- buffer[time_length + time_offset] = '\0';
if (!first_timestamp) {
- first_timestamp = strdup(buffer + time_offset);
+ first_timestamp = strdup(input);
+ } else if (!second_timestamp) {
+ second_timestamp = strdup(input);
}
free(last_timestamp);
- last_timestamp = strdup(buffer + time_offset);
+ last_timestamp = strdup(input);
}
- }
- pclose(fp);
+ pclose(fp);
- EXPECT_EQ(10, count);
+ } while ((count < 10) && --tries && inject(10 - count));
+
+ EXPECT_EQ(10, count); // We want _some_ history, too small, falses below
EXPECT_TRUE(last_timestamp != NULL);
EXPECT_TRUE(first_timestamp != NULL);
+ EXPECT_TRUE(second_timestamp != NULL);
- snprintf(buffer, sizeof(buffer), "logcat -v long -b all -t '%s' 2>&1",
- first_timestamp);
+ snprintf(buffer, sizeof(buffer), "logcat"
+ " -v long"
+ " -v nsec"
+ " -b all"
+ " -t '%s'"
+ " 2>&1",
+ first_timestamp);
ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
int second_count = 0;
int last_timestamp_count = -1;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
- && (buffer[time_offset + 2] == '-')) {
- ++second_count;
- buffer[time_length + time_offset] = '\0';
- if (first_timestamp) {
- // we can get a transitory *extremely* rare failure if hidden
- // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
- EXPECT_STREQ(buffer + time_offset, first_timestamp);
- free(first_timestamp);
- first_timestamp = NULL;
+ --count; // One less unless we match the first_timestamp
+ bool found = false;
+ while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
+ ++second_count;
+ // We want to highlight if we skip to the next entry.
+ // WAI, if the time in logd is *exactly*
+ // XX-XX XX:XX:XX.XXXXXX000 (usec) or XX-XX XX:XX:XX.XXX000000
+ // this can happen, but it should not happen with nsec.
+ // We can make this WAI behavior happen 1000 times less
+ // frequently if the caller does not use the -v usec flag,
+ // but always the second (always skip) if they use the
+ // (undocumented) -v nsec flag.
+ if (first_timestamp) {
+ found = !strcmp(input, first_timestamp);
+ if (found) {
+ ++count;
+ GTEST_LOG_(INFO) << "input = first("
+ << first_timestamp
+ << ")\n";
}
- if (!strcmp(buffer + time_offset, last_timestamp)) {
- last_timestamp_count = second_count;
+ free(first_timestamp);
+ first_timestamp = NULL;
+ }
+ if (second_timestamp) {
+ found = found || !strcmp(input, second_timestamp);
+ if (!found) {
+ GTEST_LOG_(INFO) << "input("
+ << input
+ << ") != second("
+ << second_timestamp
+ << ")\n";
}
+ free(second_timestamp);
+ second_timestamp = NULL;
+ }
+ if (!strcmp(input, last_timestamp)) {
+ last_timestamp_count = second_count;
}
}
pclose(fp);
+ EXPECT_TRUE(found);
+ if (!found) {
+ if (first_timestamp) {
+ GTEST_LOG_(INFO) << "first = " << first_timestamp << "\n";
+ }
+ if (second_timestamp) {
+ GTEST_LOG_(INFO) << "second = " << second_timestamp << "\n";
+ }
+ if (last_timestamp) {
+ GTEST_LOG_(INFO) << "last = " << last_timestamp << "\n";
+ }
+ }
free(last_timestamp);
last_timestamp = NULL;
+ free(first_timestamp);
+ free(second_timestamp);
EXPECT_TRUE(first_timestamp == NULL);
+ EXPECT_TRUE(second_timestamp == NULL);
EXPECT_LE(count, second_count);
EXPECT_LE(count, last_timestamp_count);
}
@@ -246,7 +441,7 @@
"logcat -v brief -b events -t 100 2>/dev/null",
"r")));
- char buffer[5120];
+ char buffer[BIG_BUFFER];
int count = 0;
@@ -270,21 +465,23 @@
ASSERT_EQ(1, count);
}
-TEST(logcat, get_size) {
+int get_groups(const char *cmd) {
FILE *fp;
// NB: crash log only available in user space
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null",
- "r")));
+ EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
- char buffer[5120];
+ if (fp == NULL) {
+ return 0;
+ }
+
+ char buffer[BIG_BUFFER];
int count = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
int size, consumed, max, payload;
- char size_mult[2], consumed_mult[2];
+ char size_mult[3], consumed_mult[3];
long full_size, full_consumed;
size = consumed = max = payload = 0;
@@ -338,14 +535,31 @@
pclose(fp);
- ASSERT_EQ(4, count);
+ return count;
}
-static void caught_blocking(int /*signum*/)
+TEST(logcat, get_size) {
+ ASSERT_EQ(4, get_groups(
+ "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null"));
+}
+
+// duplicate test for get_size, but use comma-separated list of buffers
+TEST(logcat, multiple_buffer) {
+ ASSERT_EQ(4, get_groups(
+ "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
+}
+
+TEST(logcat, bad_buffer) {
+ ASSERT_EQ(0, get_groups(
+ "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
+}
+
+static void caught_blocking(int signum)
{
unsigned long long v = 0xDEADBEEFA55A0000ULL;
v += getpid() & 0xFFFF;
+ if (signum == 0) ++v;
LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
}
@@ -367,7 +581,7 @@
" logcat -v brief -b events 2>&1",
"r")));
- char buffer[5120];
+ char buffer[BIG_BUFFER];
int count = 0;
@@ -410,11 +624,12 @@
EXPECT_EQ(1, signals);
}
-static void caught_blocking_tail(int /*signum*/)
+static void caught_blocking_tail(int signum)
{
unsigned long long v = 0xA55ADEADBEEF0000ULL;
v += getpid() & 0xFFFF;
+ if (signum == 0) ++v;
LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
}
@@ -436,7 +651,7 @@
" logcat -v brief -b events -T 5 2>&1",
"r")));
- char buffer[5120];
+ char buffer[BIG_BUFFER];
int count = 0;
@@ -481,6 +696,14 @@
EXPECT_EQ(1, signals);
}
+// meant to be handed to ASSERT_FALSE / EXPECT_FALSE to expand the message
+static testing::AssertionResult IsFalse(int ret, const char* command) {
+ return ret ?
+ (testing::AssertionSuccess() <<
+ "ret=" << ret << " command=\"" << command << "\"") :
+ testing::AssertionFailure();
+}
+
TEST(logcat, logrotate) {
static const char form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
char buf[sizeof(form)];
@@ -489,41 +712,40 @@
static const char comm[] = "logcat -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 7 -r 1";
char command[sizeof(buf) + sizeof(comm)];
- sprintf(command, comm, buf);
+ snprintf(command, sizeof(command), comm, buf);
int ret;
- EXPECT_FALSE((ret = system(command)));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (!ret) {
- sprintf(command, "ls -s %s 2>/dev/null", buf);
+ snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
FILE *fp;
EXPECT_TRUE(NULL != (fp = popen(command, "r")));
if (fp) {
- char buffer[5120];
+ char buffer[BIG_BUFFER];
int count = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
- static const char match_1[] = "4 log.txt";
- static const char match_2[] = "8 log.txt";
- static const char match_3[] = "12 log.txt";
- static const char match_4[] = "16 log.txt";
static const char total[] = "total ";
+ int num;
+ char c;
- if (!strncmp(buffer, match_1, sizeof(match_1) - 1)
- || !strncmp(buffer, match_2, sizeof(match_2) - 1)
- || !strncmp(buffer, match_3, sizeof(match_3) - 1)
- || !strncmp(buffer, match_4, sizeof(match_4) - 1)) {
+ if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
+ (num <= 40)) {
++count;
} else if (strncmp(buffer, total, sizeof(total) - 1)) {
fprintf(stderr, "WARNING: Parse error: %s", buffer);
}
}
pclose(fp);
+ if ((count != 7) && (count != 8)) {
+ fprintf(stderr, "count=%d\n", count);
+ }
EXPECT_TRUE(count == 7 || count == 8);
}
}
- sprintf(command, "rm -rf %s", buf);
- EXPECT_FALSE(system(command));
+ snprintf(command, sizeof(command), "rm -rf %s", buf);
+ EXPECT_FALSE(IsFalse(system(command), command));
}
TEST(logcat, logrotate_suffix) {
@@ -534,16 +756,16 @@
static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 10 -r 1";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
- sprintf(command, logcat_cmd, tmp_out_dir);
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
int ret;
- EXPECT_FALSE((ret = system(command)));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (!ret) {
- sprintf(command, "ls %s 2>/dev/null", tmp_out_dir);
+ snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
FILE *fp;
EXPECT_TRUE(NULL != (fp = popen(command, "r")));
- char buffer[5120];
+ char buffer[BIG_BUFFER];
int log_file_count = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
@@ -575,15 +797,290 @@
pclose(fp);
EXPECT_EQ(11, log_file_count);
}
- sprintf(command, "rm -rf %s", tmp_out_dir);
- EXPECT_FALSE(system(command));
+ snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
}
-static void caught_blocking_clear(int /*signum*/)
-{
+TEST(logcat, logrotate_continue) {
+ static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char tmp_out_dir[sizeof(tmp_out_dir_form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ static const char log_filename[] = "log.txt";
+ static const char logcat_cmd[] = "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
+ static const char cleanup_cmd[] = "rm -rf %s";
+ char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+
+ int ret;
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ FILE *fp;
+ snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename);
+ EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+ if (!fp) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ char *line = NULL;
+ char *last_line = NULL; // this line is allowed to stutter, one-line overlap
+ char *second_last_line = NULL; // should never stutter
+ char *first_line = NULL; // help diagnose failure?
+ size_t len = 0;
+ while (getline(&line, &len, fp) != -1) {
+ if (!first_line) {
+ first_line = line;
+ line = NULL;
+ continue;
+ }
+ free(second_last_line);
+ second_last_line = last_line;
+ last_line = line;
+ line = NULL;
+ }
+ fclose(fp);
+ free(line);
+ if (second_last_line == NULL) {
+ fprintf(stderr, "No second to last line, using last, test may fail\n");
+ second_last_line = last_line;
+ last_line = NULL;
+ }
+ free(last_line);
+ EXPECT_TRUE(NULL != second_last_line);
+ if (!second_last_line) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ free(first_line);
+ return;
+ }
+ // re-run the command, it should only add a few lines more content if it
+ // continues where it left off.
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ free(second_last_line);
+ free(first_line);
+ return;
+ }
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+ EXPECT_NE(nullptr, dir);
+ if (!dir) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ free(second_last_line);
+ free(first_line);
+ return;
+ }
+ struct dirent *entry;
+ unsigned count = 0;
+ while ((entry = readdir(dir.get()))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, entry->d_name);
+ EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+ if (!fp) {
+ fprintf(stderr, "%s ?\n", command);
+ continue;
+ }
+ line = NULL;
+ size_t number = 0;
+ while (getline(&line, &len, fp) != -1) {
+ ++number;
+ if (!strcmp(line, second_last_line)) {
+ EXPECT_TRUE(++count <= 1);
+ fprintf(stderr, "%s(%zu):\n", entry->d_name, number);
+ }
+ }
+ fclose(fp);
+ free(line);
+ unlink(command);
+ }
+ if (count > 1) {
+ char *brk = strpbrk(second_last_line, "\r\n");
+ if (!brk) brk = second_last_line + strlen(second_last_line);
+ fprintf(stderr, "\"%.*s\" occurred %u times\n",
+ (int)(brk - second_last_line), second_last_line, count);
+ if (first_line) {
+ brk = strpbrk(first_line, "\r\n");
+ if (!brk) brk = first_line + strlen(first_line);
+ fprintf(stderr, "\"%.*s\" was first line, fault?\n",
+ (int)(brk - first_line), first_line);
+ }
+ }
+ free(second_last_line);
+ free(first_line);
+
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+}
+
+TEST(logcat, logrotate_clear) {
+ static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char tmp_out_dir[sizeof(tmp_out_dir_form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ static const char log_filename[] = "log.txt";
+ static const unsigned num_val = 32;
+ static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
+ static const char clear_cmd[] = " -c";
+ static const char cleanup_cmd[] = "rm -rf %s";
+ char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename) + sizeof(clear_cmd) + 32];
+
+ // Run command with all data
+ {
+ snprintf(command, sizeof(command) - sizeof(clear_cmd),
+ logcat_cmd, tmp_out_dir, log_filename, num_val);
+
+ int ret;
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+ EXPECT_NE(nullptr, dir);
+ if (!dir) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ struct dirent *entry;
+ unsigned count = 0;
+ while ((entry = readdir(dir.get()))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ ++count;
+ }
+ EXPECT_EQ(count, num_val + 1);
+ }
+
+ {
+ // Now with -c option tacked onto the end
+ strcat(command, clear_cmd);
+
+ int ret;
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+ EXPECT_NE(nullptr, dir);
+ if (!dir) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ struct dirent *entry;
+ unsigned count = 0;
+ while ((entry = readdir(dir.get()))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ fprintf(stderr, "Found %s/%s!!!\n", tmp_out_dir, entry->d_name);
+ ++count;
+ }
+ EXPECT_EQ(count, 0U);
+ }
+
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+}
+
+static int logrotate_count_id(const char *logcat_cmd, const char *tmp_out_dir) {
+
+ static const char log_filename[] = "log.txt";
+ char command[strlen(tmp_out_dir) + strlen(logcat_cmd) + strlen(log_filename) + 32];
+
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+
+ int ret;
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (ret) {
+ return -1;
+ }
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+ EXPECT_NE(nullptr, dir);
+ if (!dir) {
+ return -1;
+ }
+ struct dirent *entry;
+ int count = 0;
+ while ((entry = readdir(dir.get()))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ ++count;
+ }
+ return count;
+}
+
+TEST(logcat, logrotate_id) {
+ static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
+ static const char logcat_short_cmd[] = "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
+ static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ static const char log_filename[] = "log.txt";
+ char tmp_out_dir[strlen(tmp_out_dir_form) + 1];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ EXPECT_EQ(34, logrotate_count_id(logcat_cmd, tmp_out_dir));
+ EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+
+ char id_file[strlen(tmp_out_dir_form) + strlen(log_filename) + 5];
+ snprintf(id_file, sizeof(id_file), "%s/%s.id", tmp_out_dir, log_filename);
+ if (getuid() != 0) {
+ chmod(id_file, 0);
+ EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+ }
+ unlink(id_file);
+ EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+
+ FILE *fp = fopen(id_file, "w");
+ if (fp) {
+ fprintf(fp, "not_a_test");
+ fclose(fp);
+ }
+ if (getuid() != 0) {
+ chmod(id_file, 0); // API to preserve content even with signature change
+ ASSERT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+ chmod(id_file, 0600);
+ }
+
+ int new_signature;
+ EXPECT_LE(2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
+ EXPECT_GT(34, new_signature);
+
+ static const char cleanup_cmd[] = "rm -rf %s";
+ char command[strlen(cleanup_cmd) + strlen(tmp_out_dir_form)];
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+}
+
+TEST(logcat, logrotate_nodir) {
+ // expect logcat to error out on writing content and exit(1) for nodir
+ EXPECT_EQ(W_EXITCODE(1, 0),
+ system("logcat -b all -d"
+ " -f /das/nein/gerfingerpoken/logcat/log.txt"
+ " -n 256 -r 1024"));
+}
+
+static void caught_blocking_clear(int signum) {
unsigned long long v = 0xDEADBEEFA55C0000ULL;
v += getpid() & 0xFFFF;
+ if (signum == 0) ++v;
LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
}
@@ -601,12 +1098,14 @@
ASSERT_TRUE(NULL != (fp = popen(
"( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
" logcat -b events -c 2>&1 ;"
+ " logcat -b events -g 2>&1 ;"
" logcat -v brief -b events 2>&1",
"r")));
- char buffer[5120];
+ char buffer[BIG_BUFFER];
int count = 0;
+ int minus_g = 0;
int signals = 0;
@@ -624,6 +1123,50 @@
break;
}
+ int size, consumed, max, payload;
+ char size_mult[3], consumed_mult[3];
+ size = consumed = max = payload = 0;
+ if (6 == sscanf(buffer, "events: ring buffer is %d%2s (%d%2s consumed),"
+ " max entry is %db, max payload is %db",
+ &size, size_mult, &consumed, consumed_mult,
+ &max, &payload)) {
+ long full_size = size, full_consumed = consumed;
+
+ switch(size_mult[0]) {
+ case 'G':
+ full_size *= 1024;
+ /* FALLTHRU */
+ case 'M':
+ full_size *= 1024;
+ /* FALLTHRU */
+ case 'K':
+ full_size *= 1024;
+ /* FALLTHRU */
+ case 'b':
+ break;
+ }
+ switch(consumed_mult[0]) {
+ case 'G':
+ full_consumed *= 1024;
+ /* FALLTHRU */
+ case 'M':
+ full_consumed *= 1024;
+ /* FALLTHRU */
+ case 'K':
+ full_consumed *= 1024;
+ /* FALLTHRU */
+ case 'b':
+ break;
+ }
+ EXPECT_GT(full_size, full_consumed);
+ EXPECT_GT(full_size, max);
+ EXPECT_GT(max, payload);
+ EXPECT_GT(max, full_consumed);
+
+ ++minus_g;
+ continue;
+ }
+
++count;
int p;
@@ -652,6 +1195,7 @@
pclose(fp);
EXPECT_LE(1, count);
+ EXPECT_EQ(1, minus_g);
EXPECT_EQ(1, signals);
}
@@ -665,12 +1209,12 @@
return false;
}
- char buffer[5120];
+ char buffer[BIG_BUFFER];
while (fgets(buffer, sizeof(buffer), fp)) {
char *hold = *list;
char *buf = buffer;
- while (isspace(*buf)) {
+ while (isspace(*buf)) {
++buf;
}
char *end = buf + strlen(buf);
@@ -694,7 +1238,7 @@
static bool set_white_black(const char *list) {
FILE *fp;
- char buffer[5120];
+ char buffer[BIG_BUFFER];
snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
fp = popen(buffer, "r");
@@ -705,7 +1249,7 @@
while (fgets(buffer, sizeof(buffer), fp)) {
char *buf = buffer;
- while (isspace(*buf)) {
+ while (isspace(*buf)) {
++buf;
}
char *end = buf + strlen(buf);
@@ -751,3 +1295,258 @@
free(list);
list = NULL;
}
+
+TEST(logcat, regex) {
+ FILE *fp;
+ int count = 0;
+
+ char buffer[BIG_BUFFER];
+
+ snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid());
+
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_ab"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_b"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaab"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaa"));
+
+ // Let the logs settle
+ sleep(1);
+
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ continue;
+ }
+
+ EXPECT_TRUE(strstr(buffer, "logcat_test_") != NULL);
+
+ count++;
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(2, count);
+}
+
+TEST(logcat, maxcount) {
+ FILE *fp;
+ int count = 0;
+
+ char buffer[BIG_BUFFER];
+
+ snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid());
+
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+
+ // Let the logs settle
+ sleep(1);
+
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ continue;
+ }
+
+ count++;
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(3, count);
+}
+
+static bool End_to_End(const char* tag, const char* fmt, ...)
+#if defined(__GNUC__)
+ __attribute__((__format__(printf, 2, 3)))
+#endif
+ ;
+
+static bool End_to_End(const char* tag, const char* fmt, ...) {
+ FILE *fp = popen("logcat -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
+ if (!fp) return false;
+
+ char buffer[BIG_BUFFER];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ va_end(ap);
+
+ char *str = NULL;
+ asprintf(&str, "I/%s ( %%d):%%c%s%%c", tag, buffer);
+ std::string expect(str);
+ free(str);
+
+ int count = 0;
+ pid_t pid = getpid();
+ std::string lastMatch;
+ int maxMatch = 1;
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ char space;
+ char newline;
+ int p;
+ int ret = sscanf(buffer, expect.c_str(), &p, &space, &newline);
+ if ((ret == 3) && (p == pid) && (space == ' ') && (newline == '\n')) {
+ ++count;
+ } else if ((ret >= maxMatch) && (p == pid) && (count == 0)) {
+ lastMatch = buffer;
+ maxMatch = ret;
+ }
+ }
+
+ pclose(fp);
+
+ if ((count == 0) && (lastMatch.length() > 0)) {
+ // Help us pinpoint where things went wrong ...
+ fprintf(stderr, "Closest match for\n %s\n is\n %s",
+ expect.c_str(), lastMatch.c_str());
+ }
+
+ return count == 1;
+}
+
+TEST(logcat, descriptive) {
+ struct tag {
+ uint32_t tagNo;
+ const char* tagStr;
+ };
+
+ {
+ static const struct tag hhgtg = { 42, "answer" };
+ android_log_event_list ctx(hhgtg.tagNo);
+ static const char theAnswer[] = "what is five by seven";
+ ctx << theAnswer;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(hhgtg.tagStr,
+ "to life the universe etc=%s", theAnswer));
+ }
+
+ {
+ static const struct tag sync = { 2720, "sync" };
+ static const char id[] = "logcat.decriptive";
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr,
+ "[id=%s,event=42,source=-1,account=0]",
+ id));
+ }
+
+ // Partial match to description
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr,
+ "[id=%s,event=43,-1,0]",
+ id));
+ }
+
+ // Negative Test of End_to_End, ensure it is working
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
+ ctx.write();
+ fprintf(stderr, "Expect a \"Closest match\" message\n");
+ EXPECT_FALSE(End_to_End(sync.tagStr,
+ "[id=%s,event=44,source=-1,account=0]",
+ id));
+ }
+ }
+
+ {
+ static const struct tag sync = { 2747, "contacts_aggregation" };
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint64_t)30 << (int32_t)2;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr,
+ "[aggregation time=30ms,count=2]"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint64_t)31570 << (int32_t)911;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr,
+ "[aggregation time=31.57s,count=911]"));
+ }
+ }
+
+ {
+ static const struct tag sync = { 75000, "sqlite_mem_alarm_current" };
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)512;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=512B"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)3072;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=3KB"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)2097152;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=2MB"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)2097153;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=2097153B"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)1073741824;
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=1GB"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
+ }
+ }
+
+ {
+ static const struct tag sync = { 27501, "notification_panel_hidden" };
+ android_log_event_list ctx(sync.tagNo);
+ ctx.write();
+ EXPECT_TRUE(End_to_End(sync.tagStr, ""));
+ }
+}
+
+static bool reportedSecurity(const char* command) {
+ FILE* fp = popen(command, "r");
+ if (!fp) return true;
+
+ std::string ret;
+ bool val = android::base::ReadFdToString(fileno(fp), &ret);
+ pclose(fp);
+
+ if (!val) return true;
+ return std::string::npos != ret.find("'security'");
+}
+
+TEST(logcat, security) {
+ EXPECT_FALSE(reportedSecurity("logcat -b all -g 2>&1"));
+ EXPECT_TRUE(reportedSecurity("logcat -b security -g 2>&1"));
+ EXPECT_TRUE(reportedSecurity("logcat -b security -c 2>&1"));
+ EXPECT_TRUE(reportedSecurity("logcat -b security -G 256K 2>&1"));
+}
diff --git a/logd/Android.mk b/logd/Android.mk
index 615d030..9211037 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -4,6 +4,8 @@
LOCAL_MODULE:= logd
+LOCAL_INIT_RC := logd.rc
+
LOCAL_SRC_FILES := \
main.cpp \
LogCommand.cpp \
@@ -19,13 +21,16 @@
libaudit.c \
LogAudit.cpp \
LogKlog.cpp \
+ LogTags.cpp \
event.logtags
LOCAL_SHARED_LIBRARIES := \
libsysutils \
liblog \
libcutils \
- libutils
+ libbase \
+ libpackagelistparser \
+ libcap
# This is what we want to do:
# event_logtags = $(shell \
@@ -34,8 +39,10 @@
# $(LOCAL_PATH)/$2/event.logtags)
# event_flag := $(call event_logtags,auditd)
# event_flag += $(call event_logtags,logd)
+# event_flag += $(call event_logtags,tag_def)
# so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DLOGD_LOG_TAG=1004
+event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004 -DTAG_DEF_LOG_TAG=1005
+event_flag += -DLIBLOG_LOG_TAG=1006
LOCAL_CFLAGS := -Werror $(event_flag)
@@ -43,13 +50,12 @@
include $(CLEAR_VARS)
-LOCAL_MODULE := logpersist.start
+LOCAL_MODULE := logtagd.rc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE_PATH := $(bin_dir)
-LOCAL_SRC_FILES := logpersist
-ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
+
include $(BUILD_PREBUILT)
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 489bea6..74e0ea5 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -15,6 +15,7 @@
*/
#include <arpa/inet.h>
+#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -25,12 +26,16 @@
#include <sys/socket.h>
#include <sys/types.h>
+#include <string>
+
+#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
#include <sysutils/SocketClient.h>
#include "CommandListener.h"
#include "LogCommand.h"
+#include "LogUtils.h"
CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
LogListener * /*swl*/) :
@@ -43,7 +48,9 @@
registerCmd(new GetStatisticsCmd(buf));
registerCmd(new SetPruneListCmd(buf));
registerCmd(new GetPruneListCmd(buf));
+ registerCmd(new GetEventTagCmd(buf));
registerCmd(new ReinitCmd());
+ registerCmd(new ExitCmd(this));
}
CommandListener::ShutdownCmd::ShutdownCmd(LogReader *reader,
@@ -93,8 +100,7 @@
return 0;
}
- mBuf.clear((log_id_t) id, uid);
- cli->sendMsg("success");
+ cli->sendMsg(mBuf.clear((log_id_t) id, uid) ? "busy" : "success");
return 0;
}
@@ -189,22 +195,13 @@
mBuf(*buf) {
}
-static void package_string(char **strp) {
- const char *a = *strp;
- if (!a) {
- a = "";
- }
-
+static std::string package_string(const std::string &str) {
// Calculate total buffer size prefix, count is the string length w/o nul
char fmt[32];
- for(size_t l = strlen(a), y = 0, x = 6; y != x; y = x, x = strlen(fmt) - 2) {
+ for(size_t l = str.length(), y = 0, x = 6; y != x; y = x, x = strlen(fmt) - 2) {
snprintf(fmt, sizeof(fmt), "%zu\n%%s\n\f", l + x);
}
-
- char *b = *strp;
- *strp = NULL;
- asprintf(strp, fmt, a);
- free(b);
+ return android::base::StringPrintf(fmt, str.c_str());
}
int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli,
@@ -216,9 +213,20 @@
}
unsigned int logMask = -1;
+ pid_t pid = 0;
if (argc > 1) {
logMask = 0;
for (int i = 1; i < argc; ++i) {
+ static const char _pid[] = "pid=";
+ if (!strncmp(argv[i], _pid, sizeof(_pid) - 1)) {
+ pid = atol(argv[i] + sizeof(_pid) - 1);
+ if (pid == 0) {
+ cli->sendMsg("PID Error");
+ return 0;
+ }
+ continue;
+ }
+
int id = atoi(argv[i]);
if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
cli->sendMsg("Range Error");
@@ -228,16 +236,8 @@
}
}
- char *buf = NULL;
-
- mBuf.formatStatistics(&buf, uid, logMask);
- if (!buf) {
- cli->sendMsg("Failed");
- } else {
- package_string(&buf);
- cli->sendMsg(buf);
- free(buf);
- }
+ cli->sendMsg(package_string(mBuf.formatStatistics(uid, pid,
+ logMask)).c_str());
return 0;
}
@@ -249,15 +249,7 @@
int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli,
int /*argc*/, char ** /*argv*/) {
setname();
- char *buf = NULL;
- mBuf.formatPrune(&buf);
- if (!buf) {
- cli->sendMsg("Failed");
- } else {
- package_string(&buf);
- cli->sendMsg(buf);
- free(buf);
- }
+ cli->sendMsg(package_string(mBuf.formatPrune()).c_str());
return 0;
}
@@ -274,20 +266,15 @@
return 0;
}
- char *cp = NULL;
+ std::string str;
for (int i = 1; i < argc; ++i) {
- char *p = cp;
- if (p) {
- cp = NULL;
- asprintf(&cp, "%s %s", p, argv[i]);
- free(p);
- } else {
- asprintf(&cp, "%s", argv[i]);
+ if (str.length()) {
+ str += " ";
}
+ str += argv[i];
}
- int ret = mBuf.initPrune(cp);
- free(cp);
+ int ret = mBuf.initPrune(str.c_str());
if (ret) {
cli->sendMsg("Invalid");
@@ -299,6 +286,41 @@
return 0;
}
+CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer *buf) :
+ LogCommand("getEventTag"),
+ mBuf(*buf) {
+}
+
+int CommandListener::GetEventTagCmd::runCommand(SocketClient *cli,
+ int argc, char ** argv) {
+ setname();
+ uid_t uid = cli->getUid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
+ }
+
+ const char *name = NULL;
+ const char *format = NULL;
+ for (int i = 1; i < argc; ++i) {
+ static const char _name[] = "name=";
+ if (!strncmp(argv[i], _name, strlen(_name))) {
+ name = argv[i] + strlen(_name);
+ continue;
+ }
+
+ static const char _format[] = "format=";
+ if (!strncmp(argv[i], _format, strlen(_format))) {
+ format = argv[i] + strlen(_format);
+ continue;
+ }
+ }
+
+ cli->sendMsg(package_string(mBuf.formatGetEventTag(uid,
+ name, format)).c_str());
+
+ return 0;
+}
+
CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
}
@@ -313,6 +335,21 @@
return 0;
}
+CommandListener::ExitCmd::ExitCmd(CommandListener *parent) :
+ LogCommand("EXIT"),
+ mParent(*parent) {
+}
+
+int CommandListener::ExitCmd::runCommand(SocketClient * cli,
+ int /*argc*/, char ** /*argv*/) {
+ setname();
+
+ cli->sendMsg("success");
+ release(cli);
+
+ return 0;
+}
+
int CommandListener::getLogSocket() {
static const char socketName[] = "logd";
int sock = android_get_control_socket(socketName);
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index 3877675..39de03b 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -49,25 +49,42 @@
class name##Cmd : public LogCommand { \
LogBuffer &mBuf; \
public: \
- name##Cmd(LogBuffer *buf); \
+ explicit name##Cmd(LogBuffer *buf); \
virtual ~name##Cmd() {} \
int runCommand(SocketClient *c, int argc, char ** argv); \
- };
+ }
- LogBufferCmd(Clear)
- LogBufferCmd(GetBufSize)
- LogBufferCmd(SetBufSize)
- LogBufferCmd(GetBufSizeUsed)
- LogBufferCmd(GetStatistics)
- LogBufferCmd(GetPruneList)
- LogBufferCmd(SetPruneList)
+ LogBufferCmd(Clear);
+ LogBufferCmd(GetBufSize);
+ LogBufferCmd(SetBufSize);
+ LogBufferCmd(GetBufSizeUsed);
+ LogBufferCmd(GetStatistics);
+ LogBufferCmd(GetPruneList);
+ LogBufferCmd(SetPruneList);
+ LogBufferCmd(GetEventTag);
- class ReinitCmd : public LogCommand {
- public:
- ReinitCmd();
- virtual ~ReinitCmd() {}
- int runCommand(SocketClient *c, int argc, char ** argv);
- };
+#define LogCmd(name) \
+ class name##Cmd : public LogCommand { \
+ public: \
+ name##Cmd(); \
+ virtual ~name##Cmd() {} \
+ int runCommand(SocketClient *c, int argc, char ** argv); \
+ }
+
+ LogCmd(Reinit);
+
+#define LogParentCmd(name) \
+ class name##Cmd : public LogCommand { \
+ CommandListener &mParent; \
+ public: \
+ name##Cmd(); \
+ explicit name##Cmd(CommandListener *parent); \
+ virtual ~name##Cmd() {} \
+ int runCommand(SocketClient *c, int argc, char ** argv); \
+ void release(SocketClient *c) { mParent.release(c); } \
+ }
+
+ LogParentCmd(Exit);
};
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index d584925..6a26d00 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -16,24 +16,30 @@
#include <stdlib.h>
+#include <private/android_filesystem_config.h>
+
#include "FlushCommand.h"
+#include "LogBuffer.h"
#include "LogBufferElement.h"
#include "LogCommand.h"
#include "LogReader.h"
#include "LogTimes.h"
+#include "LogUtils.h"
FlushCommand::FlushCommand(LogReader &reader,
bool nonBlock,
unsigned long tail,
unsigned int logMask,
pid_t pid,
- uint64_t start) :
+ uint64_t start,
+ uint64_t timeout) :
mReader(reader),
mNonBlock(nonBlock),
mTail(tail),
mLogMask(logMask),
mPid(pid),
- mStart(start) {
+ mStart(start),
+ mTimeout((start > 1) ? timeout : 0) {
}
// runSocketCommand is called once for every open client on the
@@ -54,6 +60,10 @@
while(it != times.end()) {
entry = (*it);
if (entry->mClient == client) {
+ if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
+ LogTimeEntry::unlock();
+ return;
+ }
entry->triggerReader_Locked();
if (entry->runningReader_Locked()) {
LogTimeEntry::unlock();
@@ -71,8 +81,9 @@
LogTimeEntry::unlock();
return;
}
- entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid, mStart);
- times.push_back(entry);
+ entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask,
+ mPid, mStart, mTimeout);
+ times.push_front(entry);
}
client->incRef();
@@ -85,3 +96,11 @@
bool FlushCommand::hasReadLogs(SocketClient *client) {
return clientHasLogCredentials(client);
}
+
+static bool clientHasSecurityCredentials(SocketClient *client) {
+ return (client->getUid() == AID_SYSTEM) || (client->getGid() == AID_SYSTEM);
+}
+
+bool FlushCommand::hasSecurityLogs(SocketClient *client) {
+ return clientHasSecurityCredentials(client);
+}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
index 61c6858..1e7818a 100644
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -16,7 +16,7 @@
#ifndef _FLUSH_COMMAND_H
#define _FLUSH_COMMAND_H
-#include <log/log_read.h>
+#include <android/log.h>
#include <sysutils/SocketClientCommand.h>
class LogBufferElement;
@@ -32,17 +32,20 @@
unsigned int mLogMask;
pid_t mPid;
uint64_t mStart;
+ uint64_t mTimeout;
public:
- FlushCommand(LogReader &mReader,
+ explicit FlushCommand(LogReader &mReader,
bool nonBlock = false,
unsigned long tail = -1,
unsigned int logMask = -1,
pid_t pid = 0,
- uint64_t start = 1);
+ uint64_t start = 1,
+ uint64_t timeout = 0);
virtual void runSocketCommand(SocketClient *client);
static bool hasReadLogs(SocketClient *client);
+ static bool hasSecurityLogs(SocketClient *client);
};
#endif
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 4b3547c..92d957f 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -20,16 +20,21 @@
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/prctl.h>
#include <sys/uio.h>
#include <syslog.h>
+#include <android-base/macros.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "libaudit.h"
#include "LogAudit.h"
+#include "LogBuffer.h"
#include "LogKlog.h"
+#include "LogReader.h"
+#include "LogUtils.h"
#define KMSG_PRIORITY(PRI) \
'<', \
@@ -38,23 +43,70 @@
'>'
LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) :
- SocketListener(getLogSocket(), false),
+ SocketListener(mSock = getLogSocket(), false),
logbuf(buf),
reader(reader),
fdDmesg(fdDmesg),
- initialized(false) {
+ main(__android_logger_property_get_bool("ro.logd.auditd.main",
+ BOOL_DEFAULT_TRUE)),
+ events(__android_logger_property_get_bool("ro.logd.auditd.events",
+ BOOL_DEFAULT_TRUE)),
+ initialized(false),
+ tooFast(false) {
static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
' ', 's', 't', 'a', 'r', 't', '\n' };
write(fdDmesg, auditd_message, sizeof(auditd_message));
}
+void LogAudit::checkRateLimit() {
+
+ // trim list for AUDIT_RATE_LIMIT_BURST_DURATION of history
+ log_time oldest(AUDIT_RATE_LIMIT_BURST_DURATION, 0);
+ bucket.emplace(android_log_clockid());
+ oldest = bucket.back() - oldest;
+ while (bucket.front() < oldest) bucket.pop();
+
+ static const size_t upperThreshold =
+ ((AUDIT_RATE_LIMIT_BURST_DURATION *
+ (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) + 1) /
+ 2;
+ if (bucket.size() >= upperThreshold) {
+ // Hit peak, slow down source
+ if (!tooFast) {
+ tooFast = true;
+ audit_rate_limit(mSock, AUDIT_RATE_LIMIT_MAX);
+ }
+
+ // We do not need to hold on to the full set of timing data history,
+ // let's ensure it does not grow without bounds. This also ensures
+ // that std::dequeue underneath behaves almost like a ring buffer.
+ do {
+ bucket.pop();
+ } while (bucket.size() >= upperThreshold);
+ return;
+ }
+
+ if (!tooFast) return;
+
+ static const size_t lowerThreshold = AUDIT_RATE_LIMIT_BURST_DURATION *
+ AUDIT_RATE_LIMIT_MAX;
+
+ if (bucket.size() >= lowerThreshold) return;
+
+ tooFast = false;
+ // Went below max sustained rate, allow source to speed up
+ audit_rate_limit(mSock, AUDIT_RATE_LIMIT_DEFAULT);
+}
+
bool LogAudit::onDataAvailable(SocketClient *cli) {
if (!initialized) {
prctl(PR_SET_NAME, "logd.auditd");
initialized = true;
}
+ checkRateLimit();
+
struct audit_message rep;
rep.nlh.nlmsg_type = 0;
@@ -66,8 +118,7 @@
return false;
}
- logPrint("type=%d %.*s",
- rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
+ logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
return true;
}
@@ -89,6 +140,13 @@
}
char *cp;
+ // Work around kernels missing
+ // https://github.com/torvalds/linux/commit/b8f89caafeb55fba75b74bea25adc4e4cd91be67
+ // Such kernels improperly add newlines inside audit messages.
+ while ((cp = strchr(str, '\n'))) {
+ *cp = ' ';
+ }
+
while ((cp = strstr(str, " "))) {
memmove(cp, cp + 1, strlen(cp + 1) + 1);
}
@@ -98,16 +156,78 @@
struct iovec iov[3];
static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
+ static const char newline[] = "\n";
- 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 *>("\n");
- iov[2].iov_len = 1;
+ // Dedupe messages, checking for identical messages starting with avc:
+ static unsigned count;
+ static char *last_str;
+ static bool last_info;
- writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0]));
+ if (last_str != NULL) {
+ static const char avc[] = "): avc: ";
+ char *avcl = strstr(last_str, avc);
+ bool skip = false;
+
+ if (avcl) {
+ char *avcr = strstr(str, avc);
+
+ skip = avcr && !fastcmp<strcmp>(avcl + strlen(avc),
+ avcr + strlen(avc));
+ if (skip) {
+ ++count;
+ free(last_str);
+ last_str = strdup(str);
+ last_info = info;
+ }
+ }
+ if (!skip) {
+ static const char resume[] = " duplicate messages suppressed\n";
+
+ iov[0].iov_base = last_info ?
+ const_cast<char *>(log_info) :
+ const_cast<char *>(log_warning);
+ iov[0].iov_len = last_info ?
+ sizeof(log_info) :
+ sizeof(log_warning);
+ iov[1].iov_base = last_str;
+ iov[1].iov_len = strlen(last_str);
+ if (count > 1) {
+ iov[2].iov_base = const_cast<char *>(resume);
+ iov[2].iov_len = strlen(resume);
+ } else {
+ iov[2].iov_base = const_cast<char *>(newline);
+ iov[2].iov_len = strlen(newline);
+ }
+
+ writev(fdDmesg, iov, arraysize(iov));
+ free(last_str);
+ last_str = NULL;
+ }
+ }
+ if (last_str == NULL) {
+ count = 0;
+ last_str = strdup(str);
+ last_info = info;
+ }
+ if (count == 0) {
+ 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 *>(newline);
+ iov[2].iov_len = strlen(newline);
+
+ writev(fdDmesg, iov, arraysize(iov));
+ }
+ }
+
+ if (!main && !events) {
+ free(str);
+ return 0;
}
pid_t pid = getpid();
@@ -122,17 +242,19 @@
&& (*cp == ':')) {
memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
- //
- // We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to
- // differentiate without prejudice, we use 1980 to delineate, earlier
- // is monotonic, later is real.
- //
-# define EPOCH_PLUS_10_YEARS (10 * 1461 / 4 * 24 * 60 * 60)
- if (now.tv_sec < EPOCH_PLUS_10_YEARS) {
- LogKlog::convertMonotonicToReal(now);
+ if (!isMonotonic()) {
+ if (android::isMonotonic(now)) {
+ LogKlog::convertMonotonicToReal(now);
+ }
+ } else {
+ if (!android::isMonotonic(now)) {
+ LogKlog::convertRealToMonotonic(now);
+ }
}
+ } else if (isMonotonic()) {
+ now = log_time(CLOCK_MONOTONIC);
} else {
- now.strptime("", ""); // side effect of setting CLOCK_REALTIME
+ now = log_time(CLOCK_REALTIME);
}
static const char pid_str[] = " pid=";
@@ -153,15 +275,16 @@
// log to events
- size_t l = strlen(str);
+ size_t l = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
size_t n = l + sizeof(android_log_event_string_t);
bool notify = false;
- android_log_event_string_t *event = static_cast<android_log_event_string_t *>(malloc(n));
- if (!event) {
- rc = -ENOMEM;
- } else {
+ if (events) { // begin scope for event buffer
+ uint32_t buffer[(n + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
+
+ android_log_event_string_t *event
+ = reinterpret_cast<android_log_event_string_t *>(buffer);
event->header.tag = htole32(AUDITD_LOG_TAG);
event->type = EVENT_TYPE_STRING;
event->length = htole32(l);
@@ -170,11 +293,10 @@
rc = logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid,
reinterpret_cast<char *>(event),
(n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
- free(event);
-
if (rc >= 0) {
notify = true;
}
+ // end scope for event buffer
}
// log to main
@@ -182,7 +304,7 @@
static const char comm_str[] = " comm=\"";
const char *comm = strstr(str, comm_str);
const char *estr = str + strlen(str);
- char *commfree = NULL;
+ const char *commfree = NULL;
if (comm) {
estr = comm;
comm += sizeof(comm_str) - 1;
@@ -206,27 +328,31 @@
l = strlen(comm) + 1;
ecomm = "";
}
- n = (estr - str) + strlen(ecomm) + l + 2;
+ size_t b = estr - str;
+ if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
+ b = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+ size_t e = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - b);
+ n = b + e + l + 2;
- char *newstr = static_cast<char *>(malloc(n));
- if (!newstr) {
- rc = -ENOMEM;
- } else {
+ if (main) { // begin scope for main buffer
+ char newstr[n];
+
*newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN;
strlcpy(newstr + 1, comm, l);
- strncpy(newstr + 1 + l, str, estr - str);
- strcpy(newstr + 1 + l + (estr - str), ecomm);
+ strncpy(newstr + 1 + l, str, b);
+ strncpy(newstr + 1 + l + b, ecomm, e);
rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
(n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
- free(newstr);
if (rc >= 0) {
notify = true;
}
+ // end scope for main buffer
}
- free(commfree);
+ free(const_cast<char *>(commfree));
free(str);
if (notify) {
@@ -239,9 +365,9 @@
return rc;
}
-int LogAudit::log(char *buf) {
+int LogAudit::log(char *buf, size_t len) {
char *audit = strstr(buf, " audit(");
- if (!audit) {
+ if (!audit || (audit >= &buf[len])) {
return 0;
}
@@ -249,7 +375,7 @@
int rc;
char *type = strstr(buf, "type=");
- if (type) {
+ if (type && (type < &buf[len])) {
rc = logPrint("%s %s", type, audit + 1);
} else {
rc = logPrint("%s", audit + 1);
@@ -267,5 +393,6 @@
audit_close(fd);
fd = -1;
}
+ (void)audit_rate_limit(fd, AUDIT_RATE_LIMIT_DEFAULT);
return fd;
}
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index f977be9..045d631 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -17,18 +17,31 @@
#ifndef _LOGD_LOG_AUDIT_H__
#define _LOGD_LOG_AUDIT_H__
+#include <queue>
+
#include <sysutils/SocketListener.h>
-#include "LogReader.h"
+
+#include "LogBuffer.h"
+
+class LogReader;
class LogAudit : public SocketListener {
LogBuffer *logbuf;
LogReader *reader;
- int fdDmesg;
+ int fdDmesg; // fdDmesg >= 0 is functionally bool dmesg
+ bool main;
+ bool events;
bool initialized;
+ bool tooFast;
+ int mSock;
+ std::queue<log_time> bucket;
+ void checkRateLimit();
+
public:
LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
- int log(char *buf);
+ int log(char *buf, size_t len);
+ bool isMonotonic() { return logbuf->isMonotonic(); }
protected:
virtual bool onDataAvailable(SocketClient *cli);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index b9e8973..7613c1e 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+// for manual checking of stale entries during LogBuffer::erase()
+//#define DEBUG_CHECK_FOR_STALE_ENTRIES
#include <ctype.h>
+#include <endian.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
+#include <sys/cdefs.h>
#include <sys/user.h>
#include <time.h>
#include <unistd.h>
@@ -25,115 +29,164 @@
#include <unordered_map>
#include <cutils/properties.h>
-#include <log/logger.h>
+#include <private/android_logger.h>
#include "LogBuffer.h"
+#include "LogKlog.h"
#include "LogReader.h"
+#include "LogUtils.h"
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
// Default
-#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
#define log_buffer_size(id) mMaxSize[id]
-#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
-#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
-
-static bool valid_size(unsigned long value) {
- if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
- return false;
- }
-
- long pages = sysconf(_SC_PHYS_PAGES);
- if (pages < 1) {
- return true;
- }
-
- long pagesize = sysconf(_SC_PAGESIZE);
- if (pagesize <= 1) {
- pagesize = PAGE_SIZE;
- }
-
- // maximum memory impact a somewhat arbitrary ~3%
- pages = (pages + 31) / 32;
- unsigned long maximum = pages * pagesize;
-
- if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
- return true;
- }
-
- return value <= maximum;
-}
-
-static unsigned long property_get_size(const char *key) {
- char property[PROPERTY_VALUE_MAX];
- property_get(key, property, "");
-
- char *cp;
- unsigned long value = strtoul(property, &cp, 10);
-
- switch(*cp) {
- case 'm':
- case 'M':
- value *= 1024;
- /* FALLTHRU */
- case 'k':
- case 'K':
- value *= 1024;
- /* FALLTHRU */
- case '\0':
- break;
-
- default:
- value = 0;
- }
-
- if (!valid_size(value)) {
- value = 0;
- }
-
- return value;
-}
void LogBuffer::init() {
- static const char global_tuneable[] = "persist.logd.size"; // Settings App
- static const char global_default[] = "ro.logd.size"; // BoardConfig.mk
-
- unsigned long default_size = property_get_size(global_tuneable);
- if (!default_size) {
- default_size = property_get_size(global_default);
- }
-
log_id_for_each(i) {
- char key[PROP_NAME_MAX];
+ mLastSet[i] = false;
+ mLast[i] = mLogElements.begin();
- snprintf(key, sizeof(key), "%s.%s",
- global_tuneable, android_log_id_to_name(i));
- unsigned long property_size = property_get_size(key);
-
- if (!property_size) {
- snprintf(key, sizeof(key), "%s.%s",
- global_default, android_log_id_to_name(i));
- property_size = property_get_size(key);
- }
-
- if (!property_size) {
- property_size = default_size;
- }
-
- if (!property_size) {
- property_size = LOG_BUFFER_SIZE;
- }
-
- if (setSize(i, property_size)) {
+ if (setSize(i, __android_logger_get_buffer_size(i))) {
setSize(i, LOG_BUFFER_MIN_SIZE);
}
}
+ bool lastMonotonic = monotonic;
+ monotonic = android_log_clockid() == CLOCK_MONOTONIC;
+ if (lastMonotonic != monotonic) {
+ //
+ // Fixup all timestamps, may not be 100% accurate, but better than
+ // throwing what we have away when we get 'surprised' by a change.
+ // In-place element fixup so no need to check reader-lock. Entries
+ // should already be in timestamp order, but we could end up with a
+ // few out-of-order entries if new monotonics come in before we
+ // are notified of the reinit change in status. A Typical example would
+ // be:
+ // --------- beginning of system
+ // 10.494082 184 201 D Cryptfs : Just triggered post_fs_data
+ // --------- beginning of kernel
+ // 0.000000 0 0 I : Initializing cgroup subsys
+ // as the act of mounting /data would trigger persist.logd.timestamp to
+ // be corrected. 1/30 corner case YMMV.
+ //
+ pthread_mutex_lock(&mLogElementsLock);
+ LogBufferElementCollection::iterator it = mLogElements.begin();
+ while((it != mLogElements.end())) {
+ LogBufferElement *e = *it;
+ if (monotonic) {
+ if (!android::isMonotonic(e->mRealTime)) {
+ LogKlog::convertRealToMonotonic(e->mRealTime);
+ }
+ } else {
+ if (android::isMonotonic(e->mRealTime)) {
+ LogKlog::convertMonotonicToReal(e->mRealTime);
+ }
+ }
+ ++it;
+ }
+ pthread_mutex_unlock(&mLogElementsLock);
+ }
+
+ // We may have been triggered by a SIGHUP. Release any sleeping reader
+ // threads to dump their current content.
+ //
+ // NB: this is _not_ performed in the context of a SIGHUP, it is
+ // performed during startup, and in context of reinit administrative thread
+ LogTimeEntry::lock();
+
+ LastLogTimes::iterator times = mTimes.begin();
+ while(times != mTimes.end()) {
+ LogTimeEntry *entry = (*times);
+ if (entry->owned_Locked()) {
+ entry->triggerReader_Locked();
+ }
+ times++;
+ }
+
+ LogTimeEntry::unlock();
}
-LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) {
+LogBuffer::LogBuffer(LastLogTimes *times):
+ monotonic(android_log_clockid() == CLOCK_MONOTONIC),
+ mTimes(*times) {
pthread_mutex_init(&mLogElementsLock, NULL);
+ log_id_for_each(i) {
+ lastLoggedElements[i] = NULL;
+ droppedElements[i] = NULL;
+ }
+
init();
}
+LogBuffer::~LogBuffer() {
+ log_id_for_each(i) {
+ delete lastLoggedElements[i];
+ delete droppedElements[i];
+ }
+}
+
+enum match_type {
+ DIFFERENT,
+ SAME,
+ SAME_LIBLOG
+};
+
+static enum match_type identical(LogBufferElement* elem, LogBufferElement* last) {
+ // is it mostly identical?
+// if (!elem) return DIFFERENT;
+ unsigned short lenl = elem->getMsgLen();
+ if (!lenl) return DIFFERENT;
+// if (!last) return DIFFERENT;
+ unsigned short lenr = last->getMsgLen();
+ if (!lenr) return DIFFERENT;
+// if (elem->getLogId() != last->getLogId()) return DIFFERENT;
+ if (elem->getUid() != last->getUid()) return DIFFERENT;
+ if (elem->getPid() != last->getPid()) return DIFFERENT;
+ if (elem->getTid() != last->getTid()) return DIFFERENT;
+
+ // last is more than a minute old, stop squashing identical messages
+ if (elem->getRealTime().nsec() >
+ (last->getRealTime().nsec() + 60 * NS_PER_SEC)) return DIFFERENT;
+
+ // Identical message
+ const char* msgl = elem->getMsg();
+ const char* msgr = last->getMsg();
+ if (lenl == lenr) {
+ if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
+ // liblog tagged messages (content gets summed)
+ if ((elem->getLogId() == LOG_ID_EVENTS) &&
+ (lenl == sizeof(android_log_event_int_t)) &&
+ !fastcmp<memcmp>(msgl, msgr,
+ sizeof(android_log_event_int_t) - sizeof(int32_t)) &&
+ (elem->getTag() == LIBLOG_LOG_TAG)) return SAME_LIBLOG;
+ }
+
+ // audit message (except sequence number) identical?
+ static const char avc[] = "): avc: ";
+
+ if (last->isBinary()) {
+ if (fastcmp<memcmp>(msgl, msgr,
+ sizeof(android_log_event_string_t) -
+ sizeof(int32_t))) return DIFFERENT;
+ msgl += sizeof(android_log_event_string_t);
+ lenl -= sizeof(android_log_event_string_t);
+ msgr += sizeof(android_log_event_string_t);
+ lenr -= sizeof(android_log_event_string_t);
+ }
+ const char *avcl = android::strnstr(msgl, lenl, avc);
+ if (!avcl) return DIFFERENT;
+ lenl -= avcl - msgl;
+ const char *avcr = android::strnstr(msgr, lenr, avc);
+ if (!avcr) return DIFFERENT;
+ lenr -= avcr - msgr;
+ if (lenl != lenr) return DIFFERENT;
+ if (fastcmp<memcmp>(avcl + strlen(avc),
+ avcr + strlen(avc), lenl)) return DIFFERENT;
+ return SAME;
+}
+
int LogBuffer::log(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
const char *msg, unsigned short len) {
@@ -143,33 +196,185 @@
LogBufferElement *elem = new LogBufferElement(log_id, realtime,
uid, pid, tid, msg, len);
- int prio = ANDROID_LOG_INFO;
- const char *tag = NULL;
- if (log_id == LOG_ID_EVENTS) {
- tag = android::tagToName(elem->getTag());
- } else {
- prio = *msg;
- tag = msg + 1;
- }
- if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- // Log traffic received to total
- pthread_mutex_lock(&mLogElementsLock);
- stats.add(elem);
- stats.subtract(elem);
- pthread_mutex_unlock(&mLogElementsLock);
- delete elem;
- return -EACCES;
+ if (log_id != LOG_ID_SECURITY) {
+ int prio = ANDROID_LOG_INFO;
+ const char *tag = NULL;
+ if (log_id == LOG_ID_EVENTS) {
+ tag = tagToName(elem->getTag());
+ } else {
+ prio = *msg;
+ tag = msg + 1;
+ }
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ // Log traffic received to total
+ pthread_mutex_lock(&mLogElementsLock);
+ stats.add(elem);
+ stats.subtract(elem);
+ pthread_mutex_unlock(&mLogElementsLock);
+ delete elem;
+ return -EACCES;
+ }
}
pthread_mutex_lock(&mLogElementsLock);
+ LogBufferElement* currentLast = lastLoggedElements[log_id];
+ if (currentLast) {
+ LogBufferElement *dropped = droppedElements[log_id];
+ unsigned short count = dropped ? dropped->getDropped() : 0;
+ //
+ // State Init
+ // incoming:
+ // dropped = NULL
+ // currentLast = NULL;
+ // elem = incoming message
+ // outgoing:
+ // dropped = NULL -> State 0
+ // currentLast = copy of elem
+ // log elem
+ // State 0
+ // incoming:
+ // count = 0
+ // dropped = NULL
+ // currentLast = copy of last message
+ // elem = incoming message
+ // outgoing: if match != DIFFERENT
+ // dropped = copy of first identical message -> State 1
+ // currentLast = reference to elem
+ // break: if match == DIFFERENT
+ // dropped = NULL -> State 0
+ // delete copy of last message (incoming currentLast)
+ // currentLast = copy of elem
+ // log elem
+ // State 1
+ // incoming:
+ // count = 0
+ // dropped = copy of first identical message
+ // currentLast = reference to last held-back incoming
+ // message
+ // elem = incoming message
+ // outgoing: if match == SAME
+ // delete copy of first identical message (dropped)
+ // dropped = reference to last held-back incoming
+ // message set to chatty count of 1 -> State 2
+ // currentLast = reference to elem
+ // outgoing: if match == SAME_LIBLOG
+ // dropped = copy of first identical message -> State 1
+ // take sum of currentLast and elem
+ // if sum overflows:
+ // log currentLast
+ // currentLast = reference to elem
+ // else
+ // delete currentLast
+ // currentLast = reference to elem, sum liblog.
+ // break: if match == DIFFERENT
+ // delete dropped
+ // dropped = NULL -> State 0
+ // log reference to last held-back (currentLast)
+ // currentLast = copy of elem
+ // log elem
+ // State 2
+ // incoming:
+ // count = chatty count
+ // dropped = chatty message holding count
+ // currentLast = reference to last held-back incoming
+ // message.
+ // dropped = chatty message holding count
+ // elem = incoming message
+ // outgoing: if match != DIFFERENT
+ // delete chatty message holding count
+ // dropped = reference to last held-back incoming
+ // message, set to chatty count + 1
+ // currentLast = reference to elem
+ // break: if match == DIFFERENT
+ // log dropped (chatty message)
+ // dropped = NULL -> State 0
+ // log reference to last held-back (currentLast)
+ // currentLast = copy of elem
+ // log elem
+ //
+ enum match_type match = identical(elem, currentLast);
+ if (match != DIFFERENT) {
+ if (dropped) {
+ // Sum up liblog tag messages?
+ if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
+ android_log_event_int_t* event =
+ reinterpret_cast<android_log_event_int_t*>(
+ const_cast<char*>(currentLast->getMsg()));
+ //
+ // To unit test, differentiate with something like:
+ // event->header.tag = htole32(CHATTY_LOG_TAG);
+ // here, then instead of delete currentLast below,
+ // log(currentLast) to see the incremental sums form.
+ //
+ uint32_t swab = event->payload.data;
+ unsigned long long total = htole32(swab);
+ event = reinterpret_cast<android_log_event_int_t*>(
+ const_cast<char*>(elem->getMsg()));
+ swab = event->payload.data;
+ lastLoggedElements[LOG_ID_EVENTS] = elem;
+ total += htole32(swab);
+ // check for overflow
+ if (total >= UINT32_MAX) {
+ log(currentLast);
+ pthread_mutex_unlock(&mLogElementsLock);
+ return len;
+ }
+ stats.add(currentLast);
+ stats.subtract(currentLast);
+ delete currentLast;
+ swab = total;
+ event->payload.data = htole32(swab);
+ pthread_mutex_unlock(&mLogElementsLock);
+ return len;
+ }
+ if (count == USHRT_MAX) {
+ log(dropped);
+ count = 1;
+ } else {
+ delete dropped;
+ ++count;
+ }
+ }
+ if (count) {
+ stats.add(currentLast);
+ stats.subtract(currentLast);
+ currentLast->setDropped(count);
+ }
+ droppedElements[log_id] = currentLast;
+ lastLoggedElements[log_id] = elem;
+ pthread_mutex_unlock(&mLogElementsLock);
+ return len;
+ }
+ if (dropped) { // State 1 or 2
+ if (count) { // State 2
+ log(dropped); // report chatty
+ } else { // State 1
+ delete dropped;
+ }
+ droppedElements[log_id] = NULL;
+ log(currentLast); // report last message in the series
+ } else { // State 0
+ delete currentLast;
+ }
+ }
+ lastLoggedElements[log_id] = new LogBufferElement(*elem);
+
+ log(elem);
+ pthread_mutex_unlock(&mLogElementsLock);
+
+ return len;
+}
+
+// assumes mLogElementsLock held, owns elem, will look after garbage collection
+void LogBuffer::log(LogBufferElement* elem) {
// Insert elements in time sorted order if possible
// NB: if end is region locked, place element at end of list
LogBufferElementCollection::iterator it = mLogElements.end();
LogBufferElementCollection::iterator last = it;
while (last != mLogElements.begin()) {
--it;
- if ((*it)->getRealTime() <= realtime) {
+ if ((*it)->getRealTime() <= elem->getRealTime()) {
break;
}
last = it;
@@ -184,9 +389,9 @@
LogTimeEntry::lock();
- LastLogTimes::iterator t = mTimes.begin();
- while(t != mTimes.end()) {
- LogTimeEntry *entry = (*t);
+ LastLogTimes::iterator times = mTimes.begin();
+ while(times != mTimes.end()) {
+ LogTimeEntry* entry = (*times);
if (entry->owned_Locked()) {
if (!entry->mNonBlock) {
end_always = true;
@@ -197,57 +402,126 @@
end_set = true;
}
}
- t++;
+ times++;
}
if (end_always
|| (end_set && (end >= (*last)->getSequence()))) {
mLogElements.push_back(elem);
} else {
- mLogElements.insert(last,elem);
+ mLogElements.insert(last, elem);
}
LogTimeEntry::unlock();
}
stats.add(elem);
- maybePrune(log_id);
- pthread_mutex_unlock(&mLogElementsLock);
-
- return len;
+ maybePrune(elem->getLogId());
}
-// If we're using more than 256K of memory for log entries, prune
-// at least 10% of the log entries. For sizes above 1M, prune at
-// least 1% of the log entries.
+// Prune at most 10% of the log entries or maxPrune, whichever is less.
//
// mLogElementsLock must be held when this function is called.
void LogBuffer::maybePrune(log_id_t id) {
size_t sizes = stats.sizes(id);
unsigned long maxSize = log_buffer_size(id);
if (sizes > maxSize) {
- size_t sizeOver, minElements, elements = stats.elements(id);
- if (maxSize > (4 * LOG_BUFFER_SIZE)) {
- sizeOver = sizes - ((maxSize * 99) / 100);
- minElements = elements / 100;
- } else {
- sizeOver = sizes - ((maxSize * 9) / 10);
- minElements = elements / 10;
+ size_t sizeOver = sizes - ((maxSize * 9) / 10);
+ size_t elements = stats.realElements(id);
+ size_t minElements = elements / 100;
+ if (minElements < minPrune) {
+ minElements = minPrune;
}
unsigned long pruneRows = elements * sizeOver / sizes;
- if (pruneRows <= minElements) {
+ if (pruneRows < minElements) {
pruneRows = minElements;
}
+ if (pruneRows > maxPrune) {
+ pruneRows = maxPrune;
+ }
prune(id, pruneRows);
}
}
-LogBufferElementCollection::iterator LogBuffer::erase(LogBufferElementCollection::iterator it) {
- LogBufferElement *e = *it;
+LogBufferElementCollection::iterator LogBuffer::erase(
+ LogBufferElementCollection::iterator it, bool coalesce) {
+ LogBufferElement *element = *it;
+ log_id_t id = element->getLogId();
+ // Remove iterator references in the various lists that will become stale
+ // after the element is erased from the main logging list.
+
+ { // start of scope for found iterator
+ int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) ?
+ element->getTag() : element->getUid();
+ LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
+ if ((found != mLastWorst[id].end()) && (it == found->second)) {
+ mLastWorst[id].erase(found);
+ }
+ }
+
+ { // start of scope for pid found iterator
+ // element->getUid() may not be AID_SYSTEM for next-best-watermark.
+ // will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
+ // long term code stability, find() check should be fast for those ids.
+ LogBufferPidIteratorMap::iterator found =
+ mLastWorstPidOfSystem[id].find(element->getPid());
+ if ((found != mLastWorstPidOfSystem[id].end())
+ && (it == found->second)) {
+ mLastWorstPidOfSystem[id].erase(found);
+ }
+ }
+
+ bool setLast[LOG_ID_MAX];
+ bool doSetLast = false;
+ log_id_for_each(i) {
+ doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
+ }
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+ LogBufferElementCollection::iterator bad = it;
+ int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) ?
+ element->getTag() : element->getUid();
+#endif
it = mLogElements.erase(it);
- stats.subtract(e);
- delete e;
+ if (doSetLast) {
+ log_id_for_each(i) {
+ if (setLast[i]) {
+ if (__predict_false(it == mLogElements.end())) { // impossible
+ mLastSet[i] = false;
+ mLast[i] = mLogElements.begin();
+ } else {
+ mLast[i] = it; // push down the road as next-best-watermark
+ }
+ }
+ }
+ }
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+ log_id_for_each(i) {
+ for(auto b : mLastWorst[i]) {
+ if (bad == b.second) {
+ android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n",
+ i, b.first, key);
+ }
+ }
+ for(auto b : mLastWorstPidOfSystem[i]) {
+ if (bad == b.second) {
+ android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n",
+ i, b.first);
+ }
+ }
+ if (mLastSet[i] && (bad == mLast[i])) {
+ android::prdebug("stale mLast[%d]\n", i);
+ mLastSet[i] = false;
+ mLast[i] = mLogElements.begin();
+ }
+ }
+#endif
+ if (coalesce) {
+ stats.erase(element);
+ } else {
+ stats.subtract(element);
+ }
+ delete element;
return it;
}
@@ -258,17 +532,21 @@
class LogBufferElementKey {
const union {
struct {
- uint16_t uid;
+ uint32_t uid;
uint16_t pid;
uint16_t tid;
- uint16_t padding;
} __packed;
uint64_t value;
} __packed;
public:
- LogBufferElementKey(uid_t u, pid_t p, pid_t t):uid(u),pid(p),tid(t),padding(0) { }
- LogBufferElementKey(uint64_t k):value(k) { }
+ LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid):
+ uid(uid),
+ pid(pid),
+ tid(tid)
+ {
+ }
+ explicit LogBufferElementKey(uint64_t key):value(key) { }
uint64_t getKey() { return value; }
};
@@ -280,38 +558,42 @@
public:
- bool merge(LogBufferElement *e, unsigned short dropped) {
- LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
+ bool coalesce(LogBufferElement *element, unsigned short dropped) {
+ LogBufferElementKey key(element->getUid(),
+ element->getPid(),
+ element->getTid());
LogBufferElementMap::iterator it = map.find(key.getKey());
if (it != map.end()) {
- LogBufferElement *l = it->second;
- unsigned short d = l->getDropped();
- if ((dropped + d) > USHRT_MAX) {
+ LogBufferElement *found = it->second;
+ unsigned short moreDropped = found->getDropped();
+ if ((dropped + moreDropped) > USHRT_MAX) {
map.erase(it);
} else {
- l->setDropped(dropped + d);
+ found->setDropped(dropped + moreDropped);
return true;
}
}
return false;
}
- void add(LogBufferElement *e) {
- LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
- map[key.getKey()] = e;
+ void add(LogBufferElement *element) {
+ LogBufferElementKey key(element->getUid(),
+ element->getPid(),
+ element->getTid());
+ map[key.getKey()] = element;
}
inline void clear() {
map.clear();
}
- void clear(LogBufferElement *e) {
- uint64_t current = e->getRealTime().nsec()
+ void clear(LogBufferElement *element) {
+ uint64_t current = element->getRealTime().nsec()
- (EXPIRE_RATELIMIT * NS_PER_SEC);
for(LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
- LogBufferElement *l = it->second;
- if ((l->getDropped() >= EXPIRE_THRESHOLD)
- && (current > l->getRealTime().nsec())) {
+ LogBufferElement *mapElement = it->second;
+ if ((mapElement->getDropped() >= EXPIRE_THRESHOLD)
+ && (current > mapElement->getRealTime().nsec())) {
it = map.erase(it);
} else {
++it;
@@ -323,100 +605,202 @@
// prune "pruneRows" of type "id" from the buffer.
//
+// This garbage collection task is used to expire log entries. It is called to
+// remove all logs (clear), all UID logs (unprivileged clear), or every
+// 256 or 10% of the total logs (whichever is less) to prune the logs.
+//
+// First there is a prep phase where we discover the reader region lock that
+// acts as a backstop to any pruning activity to stop there and go no further.
+//
+// There are three major pruning loops that follow. All expire from the oldest
+// entries. Since there are multiple log buffers, the Android logging facility
+// will appear to drop entries 'in the middle' when looking at multiple log
+// sources and buffers. This effect is slightly more prominent when we prune
+// the worst offender by logging source. Thus the logs slowly loose content
+// and value as you move back in time. This is preferred since chatty sources
+// invariably move the logs value down faster as less chatty sources would be
+// expired in the noise.
+//
+// The first loop performs blacklisting and worst offender pruning. Falling
+// through when there are no notable worst offenders and have not hit the
+// region lock preventing further worst offender pruning. This loop also looks
+// after managing the chatty log entries and merging to help provide
+// statistical basis for blame. The chatty entries are not a notification of
+// how much logs you may have, but instead represent how much logs you would
+// have had in a virtual log buffer that is extended to cover all the in-memory
+// logs without loss. They last much longer than the represented pruned logs
+// since they get multiplied by the gains in the non-chatty log sources.
+//
+// The second loop get complicated because an algorithm of watermarks and
+// history is maintained to reduce the order and keep processing time
+// down to a minimum at scale. These algorithms can be costly in the face
+// of larger log buffers, or severly limited processing time granted to a
+// background task at lowest priority.
+//
+// This second loop does straight-up expiration from the end of the logs
+// (again, remember for the specified log buffer id) but does some whitelist
+// preservation. Thus whitelist is a Hail Mary low priority, blacklists and
+// spam filtration all take priority. This second loop also checks if a region
+// lock is causing us to buffer too much in the logs to help the reader(s),
+// and will tell the slowest reader thread to skip log entries, and if
+// persistent and hits a further threshold, kill the reader thread.
+//
+// The third thread is optional, and only gets hit if there was a whitelist
+// and more needs to be pruned against the backstop of the region lock.
+//
// mLogElementsLock must be held when this function is called.
-void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+//
+bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
LogTimeEntry *oldest = NULL;
+ bool busy = false;
+ bool clearAll = pruneRows == ULONG_MAX;
LogTimeEntry::lock();
// Region locked?
- LastLogTimes::iterator t = mTimes.begin();
- while(t != mTimes.end()) {
- LogTimeEntry *entry = (*t);
+ LastLogTimes::iterator times = mTimes.begin();
+ while(times != mTimes.end()) {
+ LogTimeEntry *entry = (*times);
if (entry->owned_Locked() && entry->isWatching(id)
- && (!oldest || (oldest->mStart > entry->mStart))) {
+ && (!oldest ||
+ (oldest->mStart > entry->mStart) ||
+ ((oldest->mStart == entry->mStart) &&
+ (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
oldest = entry;
}
- t++;
+ times++;
}
LogBufferElementCollection::iterator it;
- if (caller_uid != AID_ROOT) {
- for(it = mLogElements.begin(); it != mLogElements.end();) {
- LogBufferElement *e = *it;
+ if (__predict_false(caller_uid != AID_ROOT)) { // unlikely
+ // Only here if clear all request from non system source, so chatty
+ // filter logistics is not required.
+ it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ while (it != mLogElements.end()) {
+ LogBufferElement *element = *it;
- if (oldest && (oldest->mStart <= e->getSequence())) {
- break;
- }
-
- if (e->getLogId() != id) {
+ if ((element->getLogId() != id) || (element->getUid() != caller_uid)) {
++it;
continue;
}
- if (e->getUid() == caller_uid) {
- it = erase(it);
- pruneRows--;
- if (pruneRows == 0) {
- break;
+ if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+ mLast[id] = it;
+ mLastSet[id] = true;
+ }
+
+ if (oldest && (oldest->mStart <= element->getSequence())) {
+ busy = true;
+ if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
+ } else {
+ oldest->triggerSkip_Locked(id, pruneRows);
}
- } else {
- ++it;
+ break;
+ }
+
+ it = erase(it);
+ if (--pruneRows == 0) {
+ break;
}
}
LogTimeEntry::unlock();
- return;
+ return busy;
}
- // prune by worst offender by uid
- bool hasBlacklist = mPrune.naughty();
- while (pruneRows > 0) {
+ // prune by worst offenders; by blacklist, UID, and by PID of system UID
+ bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
+ while (!clearAll && (pruneRows > 0)) {
// recalculate the worst offender on every batched pass
- uid_t worst = (uid_t) -1;
+ int worst = -1; // not valid for getUid() or getKey()
size_t worst_sizes = 0;
size_t second_worst_sizes = 0;
+ pid_t worstPid = 0; // POSIX guarantees PID != 0
if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
- std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);
+ // Calculate threshold as 12.5% of available storage
+ size_t threshold = log_buffer_size(id) / 8;
- if (sorted.get()) {
- if (sorted[0] && sorted[1]) {
- worst_sizes = sorted[0]->getSizes();
- // Calculate threshold as 12.5% of available storage
- size_t threshold = log_buffer_size(id) / 8;
- if (worst_sizes > threshold) {
- worst = sorted[0]->getKey();
- second_worst_sizes = sorted[1]->getSizes();
- if (second_worst_sizes < threshold) {
- second_worst_sizes = threshold;
- }
- }
+ if ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) {
+ stats.sortTags(AID_ROOT, (pid_t)0, 2, id).findWorst(
+ worst, worst_sizes, second_worst_sizes, threshold);
+ // per-pid filter for AID_SYSTEM sources is too complex
+ } else {
+ stats.sort(AID_ROOT, (pid_t)0, 2, id).findWorst(
+ worst, worst_sizes, second_worst_sizes, threshold);
+
+ if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
+ stats.sortPids(worst, (pid_t)0, 2, id).findWorst(
+ worstPid, worst_sizes, second_worst_sizes);
}
}
}
// skip if we have neither worst nor naughty filters
- if ((worst == (uid_t) -1) && !hasBlacklist) {
+ if ((worst == -1) && !hasBlacklist) {
break;
}
bool kick = false;
bool leading = true;
+ it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ // Perform at least one mandatory garbage collection cycle in following
+ // - clear leading chatty tags
+ // - coalesce chatty tags
+ // - check age-out of preserved logs
+ bool gc = pruneRows <= 1;
+ if (!gc && (worst != -1)) {
+ { // begin scope for worst found iterator
+ LogBufferIteratorMap::iterator found = mLastWorst[id].find(worst);
+ if ((found != mLastWorst[id].end())
+ && (found->second != mLogElements.end())) {
+ leading = false;
+ it = found->second;
+ }
+ }
+ if (worstPid) { // begin scope for pid worst found iterator
+ // FYI: worstPid only set if !LOG_ID_EVENTS and
+ // !LOG_ID_SECURITY, not going to make that assumption ...
+ LogBufferPidIteratorMap::iterator found
+ = mLastWorstPidOfSystem[id].find(worstPid);
+ if ((found != mLastWorstPidOfSystem[id].end())
+ && (found->second != mLogElements.end())) {
+ leading = false;
+ it = found->second;
+ }
+ }
+ }
+ static const timespec too_old = {
+ EXPIRE_HOUR_THRESHOLD * 60 * 60, 0
+ };
+ LogBufferElementCollection::iterator lastt;
+ lastt = mLogElements.end();
+ --lastt;
LogBufferElementLast last;
- for(it = mLogElements.begin(); it != mLogElements.end();) {
- LogBufferElement *e = *it;
+ while (it != mLogElements.end()) {
+ LogBufferElement *element = *it;
- if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (oldest && (oldest->mStart <= element->getSequence())) {
+ busy = true;
+ if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
+ }
break;
}
- if (e->getLogId() != id) {
+ if (element->getLogId() != id) {
++it;
continue;
}
+ // below this point element->getLogId() == id
- unsigned short dropped = e->getDropped();
+ if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
+ mLast[id] = it;
+ mLastSet[id] = true;
+ }
+
+ unsigned short dropped = element->getDropped();
// remove any leading drops
if (leading && dropped) {
@@ -424,16 +808,17 @@
continue;
}
- // merge any drops
- if (dropped && last.merge(e, dropped)) {
- it = mLogElements.erase(it);
- stats.erase(e);
- delete e;
+ if (dropped && last.coalesce(element, dropped)) {
+ it = erase(it, true);
continue;
}
- if (hasBlacklist && mPrune.naughty(e)) {
- last.clear(e);
+ int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) ?
+ element->getTag() :
+ element->getUid();
+
+ if (hasBlacklist && mPrune.naughty(element)) {
+ last.clear(element);
it = erase(it);
if (dropped) {
continue;
@@ -444,40 +829,49 @@
break;
}
- if (e->getUid() == worst) {
+ if (key == worst) {
kick = true;
if (worst_sizes < second_worst_sizes) {
break;
}
- worst_sizes -= e->getMsgLen();
+ worst_sizes -= element->getMsgLen();
}
continue;
}
+ if ((element->getRealTime() < ((*lastt)->getRealTime() - too_old))
+ || (element->getRealTime() > (*lastt)->getRealTime())) {
+ break;
+ }
+
if (dropped) {
- last.add(e);
+ last.add(element);
+ if (worstPid
+ && ((!gc && (element->getPid() == worstPid))
+ || (mLastWorstPidOfSystem[id].find(element->getPid())
+ == mLastWorstPidOfSystem[id].end()))) {
+ // element->getUid() may not be AID_SYSTEM, next best
+ // watermark if current one empty. id is not LOG_ID_EVENTS
+ // or LOG_ID_SECURITY because of worstPid check.
+ mLastWorstPidOfSystem[id][element->getPid()] = it;
+ }
+ if ((!gc && !worstPid && (key == worst))
+ || (mLastWorst[id].find(key) == mLastWorst[id].end())) {
+ mLastWorst[id][key] = it;
+ }
++it;
continue;
}
- if (e->getUid() != worst) {
- if (leading) {
- static const timespec too_old = {
- EXPIRE_HOUR_THRESHOLD * 60 * 60, 0
- };
- LogBufferElementCollection::iterator last;
- last = mLogElements.end();
- --last;
- if ((e->getRealTime() < ((*last)->getRealTime() - too_old))
- || (e->getRealTime() > (*last)->getRealTime())) {
- break;
- }
- }
+ if ((key != worst)
+ || (worstPid && (element->getPid() != worstPid))) {
leading = false;
- last.clear(e);
+ last.clear(element);
++it;
continue;
}
+ // key == worst below here
+ // If worstPid set, then element->getPid() == worstPid below here
pruneRows--;
if (pruneRows == 0) {
@@ -486,20 +880,30 @@
kick = true;
- unsigned short len = e->getMsgLen();
+ unsigned short len = element->getMsgLen();
// do not create any leading drops
if (leading) {
it = erase(it);
} else {
- stats.drop(e);
- e->setDropped(1);
- if (last.merge(e, 1)) {
- it = mLogElements.erase(it);
- stats.erase(e);
- delete e;
+ stats.drop(element);
+ element->setDropped(1);
+ if (last.coalesce(element, 1)) {
+ it = erase(it, true);
} else {
- last.add(e);
+ last.add(element);
+ if (worstPid && (!gc
+ || (mLastWorstPidOfSystem[id].find(worstPid)
+ == mLastWorstPidOfSystem[id].end()))) {
+ // element->getUid() may not be AID_SYSTEM, next best
+ // watermark if current one empty. id is not
+ // LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
+ mLastWorstPidOfSystem[id][worstPid] = it;
+ }
+ if ((!gc && !worstPid) ||
+ (mLastWorst[id].find(worst) == mLastWorst[id].end())) {
+ mLastWorst[id][worst] = it;
+ }
++it;
}
}
@@ -516,17 +920,23 @@
}
bool whitelist = false;
- bool hasWhitelist = mPrune.nice();
- it = mLogElements.begin();
+ bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
+ it = mLastSet[id] ? mLast[id] : mLogElements.begin();
while((pruneRows > 0) && (it != mLogElements.end())) {
- LogBufferElement *e = *it;
+ LogBufferElement *element = *it;
- if (e->getLogId() != id) {
+ if (element->getLogId() != id) {
it++;
continue;
}
- if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+ mLast[id] = it;
+ mLastSet[id] = true;
+ }
+
+ if (oldest && (oldest->mStart <= element->getSequence())) {
+ busy = true;
if (whitelist) {
break;
}
@@ -534,13 +944,16 @@
if (stats.sizes(id) > (2 * log_buffer_size(id))) {
// kick a misbehaving log reader client off the island
oldest->release_Locked();
+ } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
} else {
oldest->triggerSkip_Locked(id, pruneRows);
}
break;
}
- if (hasWhitelist && !e->getDropped() && mPrune.nice(e)) { // WhiteListed
+ if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
+ // WhiteListed
whitelist = true;
it++;
continue;
@@ -552,19 +965,27 @@
// Do not save the whitelist if we are reader range limited
if (whitelist && (pruneRows > 0)) {
- it = mLogElements.begin();
+ it = mLastSet[id] ? mLast[id] : mLogElements.begin();
while((it != mLogElements.end()) && (pruneRows > 0)) {
- LogBufferElement *e = *it;
+ LogBufferElement *element = *it;
- if (e->getLogId() != id) {
+ if (element->getLogId() != id) {
++it;
continue;
}
- if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+ mLast[id] = it;
+ mLastSet[id] = true;
+ }
+
+ if (oldest && (oldest->mStart <= element->getSequence())) {
+ busy = true;
if (stats.sizes(id) > (2 * log_buffer_size(id))) {
// kick a misbehaving log reader client off the island
oldest->release_Locked();
+ } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
} else {
oldest->triggerSkip_Locked(id, pruneRows);
}
@@ -577,13 +998,50 @@
}
LogTimeEntry::unlock();
+
+ return (pruneRows > 0) && busy;
}
// clear all rows of type "id" from the buffer.
-void LogBuffer::clear(log_id_t id, uid_t uid) {
- pthread_mutex_lock(&mLogElementsLock);
- prune(id, ULONG_MAX, uid);
- pthread_mutex_unlock(&mLogElementsLock);
+bool LogBuffer::clear(log_id_t id, uid_t uid) {
+ bool busy = true;
+ // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
+ for (int retry = 4;;) {
+ if (retry == 1) { // last pass
+ // Check if it is still busy after the sleep, we say prune
+ // one entry, not another clear run, so we are looking for
+ // the quick side effect of the return value to tell us if
+ // we have a _blocked_ reader.
+ pthread_mutex_lock(&mLogElementsLock);
+ busy = prune(id, 1, uid);
+ pthread_mutex_unlock(&mLogElementsLock);
+ // It is still busy, blocked reader(s), lets kill them all!
+ // otherwise, lets be a good citizen and preserve the slow
+ // readers and let the clear run (below) deal with determining
+ // if we are still blocked and return an error code to caller.
+ if (busy) {
+ LogTimeEntry::lock();
+ LastLogTimes::iterator times = mTimes.begin();
+ while (times != mTimes.end()) {
+ LogTimeEntry *entry = (*times);
+ // Killer punch
+ if (entry->owned_Locked() && entry->isWatching(id)) {
+ entry->release_Locked();
+ }
+ times++;
+ }
+ LogTimeEntry::unlock();
+ }
+ }
+ pthread_mutex_lock(&mLogElementsLock);
+ busy = prune(id, ULONG_MAX, uid);
+ pthread_mutex_unlock(&mLogElementsLock);
+ if (!busy || !--retry) {
+ break;
+ }
+ sleep (1); // Let reader(s) catch up after notification
+ }
+ return busy;
}
// get the used space associated with "id".
@@ -597,7 +1055,7 @@
// set the total space allocated to "id"
int LogBuffer::setSize(log_id_t id, unsigned long size) {
// Reasonable limits ...
- if (!valid_size(size)) {
+ if (!__android_logger_valid_buffer_size(size)) {
return -1;
}
pthread_mutex_lock(&mLogElementsLock);
@@ -615,7 +1073,8 @@
}
uint64_t LogBuffer::flushTo(
- SocketClient *reader, const uint64_t start, bool privileged,
+ SocketClient *reader, const uint64_t start,
+ bool privileged, bool security,
int (*filter)(const LogBufferElement *element, void *arg), void *arg) {
LogBufferElementCollection::iterator it;
uint64_t max = start;
@@ -639,6 +1098,10 @@
}
}
+ // Help detect if the valid message before is from the same source so
+ // we can differentiate chatty filter types.
+ pid_t lastTid[LOG_ID_MAX] = { 0 };
+
for (; it != mLogElements.end(); ++it) {
LogBufferElement *element = *it;
@@ -646,6 +1109,10 @@
continue;
}
+ if (!security && (element->getLogId() == LOG_ID_SECURITY)) {
+ continue;
+ }
+
if (element->getSequence() <= start) {
continue;
}
@@ -661,10 +1128,19 @@
}
}
+ bool sameTid = lastTid[element->getLogId()] == element->getTid();
+ // Dropped (chatty) immediately following a valid log from the
+ // same source in the same log buffer indicates we have a
+ // multiple identical squash. chatty that differs source
+ // is due to spam filter. chatty to chatty of different
+ // source is also due to spam filter.
+ lastTid[element->getLogId()] = (element->getDropped() && !sameTid) ?
+ 0 : element->getTid();
+
pthread_mutex_unlock(&mLogElementsLock);
// range locking in LastLogTimes looks after us
- max = element->flushTo(reader, this);
+ max = element->flushTo(reader, this, privileged, sameTid);
if (max == element->FLUSH_ERROR) {
return max;
@@ -677,10 +1153,13 @@
return max;
}
-void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
+std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
+ unsigned int logMask) {
pthread_mutex_lock(&mLogElementsLock);
- stats.format(strp, uid, logMask);
+ std::string ret = stats.format(uid, pid, logMask);
pthread_mutex_unlock(&mLogElementsLock);
+
+ return ret;
}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index a13fded..da63e12 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -19,18 +19,61 @@
#include <sys/types.h>
-#include <log/log.h>
-#include <sysutils/SocketClient.h>
-#include <utils/List.h>
+#include <list>
+#include <string>
+#include <android/log.h>
#include <private/android_filesystem_config.h>
+#include <sysutils/SocketClient.h>
#include "LogBufferElement.h"
+#include "LogTags.h"
#include "LogTimes.h"
#include "LogStatistics.h"
#include "LogWhiteBlackList.h"
-typedef android::List<LogBufferElement *> LogBufferElementCollection;
+//
+// We are either in 1970ish (MONOTONIC) or 2016+ish (REALTIME) so to
+// differentiate without prejudice, we use 1972 to delineate, earlier
+// is likely monotonic, later is real. Otherwise we start using a
+// dividing line between monotonic and realtime if more than a minute
+// difference between them.
+//
+namespace android {
+
+static bool isMonotonic(const log_time &mono) {
+ static const uint32_t EPOCH_PLUS_2_YEARS = 2 * 24 * 60 * 60 * 1461 / 4;
+ static const uint32_t EPOCH_PLUS_MINUTE = 60;
+
+ if (mono.tv_sec >= EPOCH_PLUS_2_YEARS) {
+ return false;
+ }
+
+ log_time now(CLOCK_REALTIME);
+
+ /* Timezone and ntp time setup? */
+ if (now.tv_sec >= EPOCH_PLUS_2_YEARS) {
+ return true;
+ }
+
+ /* no way to differentiate realtime from monotonic time */
+ if (now.tv_sec < EPOCH_PLUS_MINUTE) {
+ return false;
+ }
+
+ log_time cpu(CLOCK_MONOTONIC);
+ /* too close to call to differentiate monotonic times from realtime */
+ if ((cpu.tv_sec + EPOCH_PLUS_MINUTE) >= now.tv_sec) {
+ return false;
+ }
+
+ /* dividing line half way between monotonic and realtime */
+ return mono.tv_sec < ((cpu.tv_sec + now.tv_sec) / 2);
+}
+
+}
+
+typedef std::list<LogBufferElement *> LogBufferElementCollection;
class LogBuffer {
LogBufferElementCollection mLogElements;
@@ -39,49 +82,82 @@
LogStatistics stats;
PruneList mPrune;
+ // watermark for last per log id
+ LogBufferElementCollection::iterator mLast[LOG_ID_MAX];
+ bool mLastSet[LOG_ID_MAX];
+ // watermark of any worst/chatty uid processing
+ typedef std::unordered_map<uid_t,
+ LogBufferElementCollection::iterator>
+ LogBufferIteratorMap;
+ LogBufferIteratorMap mLastWorst[LOG_ID_MAX];
+ // watermark of any worst/chatty pid of system processing
+ typedef std::unordered_map<pid_t,
+ LogBufferElementCollection::iterator>
+ LogBufferPidIteratorMap;
+ LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
unsigned long mMaxSize[LOG_ID_MAX];
+ bool monotonic;
+
+ LogTags tags;
+
+ LogBufferElement* lastLoggedElements[LOG_ID_MAX];
+ LogBufferElement* droppedElements[LOG_ID_MAX];
+ void log(LogBufferElement* elem);
+
public:
LastLogTimes &mTimes;
- LogBuffer(LastLogTimes *times);
+ explicit LogBuffer(LastLogTimes *times);
+ ~LogBuffer();
void init();
+ bool isMonotonic() { return monotonic; }
int log(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
const char *msg, unsigned short len);
uint64_t flushTo(SocketClient *writer, const uint64_t start,
- bool privileged,
+ bool privileged, bool security,
int (*filter)(const LogBufferElement *element, void *arg) = NULL,
void *arg = NULL);
- void clear(log_id_t id, uid_t uid = AID_ROOT);
+ bool clear(log_id_t id, uid_t uid = AID_ROOT);
unsigned long getSize(log_id_t id);
int setSize(log_id_t id, unsigned long size);
unsigned long getSizeUsed(log_id_t id);
- // *strp uses malloc, use free to release.
- void formatStatistics(char **strp, uid_t uid, unsigned int logMask);
+
+ std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
void enableStatistics() {
stats.enableStatistics();
}
- int initPrune(char *cp) { return mPrune.init(cp); }
- // *strp uses malloc, use free to release.
- void formatPrune(char **strp) { mPrune.format(strp); }
+ int initPrune(const char *cp) { return mPrune.init(cp); }
+ std::string formatPrune() { return mPrune.format(); }
+
+ std::string formatGetEventTag(uid_t uid,
+ const char *name, const char *format) {
+ return tags.formatGetEventTag(uid, name, format);
+ }
+ const char *tagToName(uint32_t tag) { return tags.tagToName(tag); }
// helper must be protected directly or implicitly by lock()/unlock()
- char *pidToName(pid_t pid) { return stats.pidToName(pid); }
+ const char *pidToName(pid_t pid) { return stats.pidToName(pid); }
uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
- char *uidToName(uid_t uid) { return stats.uidToName(uid); }
+ const char *uidToName(uid_t uid) { return stats.uidToName(uid); }
void lock() { pthread_mutex_lock(&mLogElementsLock); }
void unlock() { pthread_mutex_unlock(&mLogElementsLock); }
private:
+
+ static constexpr size_t minPrune = 4;
+ static constexpr size_t maxPrune = 256;
+
void maybePrune(log_id_t id);
- void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
- LogBufferElementCollection::iterator erase(LogBufferElementCollection::iterator it);
+ bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
+ LogBufferElementCollection::iterator erase(
+ LogBufferElementCollection::iterator it, bool coalesce = false);
};
#endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 9fb1439..a651fd4 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -22,12 +22,13 @@
#include <time.h>
#include <unistd.h>
-#include <log/logger.h>
#include <private/android_logger.h>
+#include "LogBuffer.h"
#include "LogBufferElement.h"
#include "LogCommand.h"
#include "LogReader.h"
+#include "LogUtils.h"
const uint64_t LogBufferElement::FLUSH_ERROR(0);
atomic_int_fast64_t LogBufferElement::sequence(1);
@@ -35,28 +36,37 @@
LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
const char *msg, unsigned short len) :
- mLogId(log_id),
mUid(uid),
mPid(pid),
mTid(tid),
- mMsgLen(len),
mSequence(sequence.fetch_add(1, memory_order_relaxed)),
- mRealTime(realtime) {
+ mRealTime(realtime),
+ mMsgLen(len),
+ mLogId(log_id) {
mMsg = new char[len];
memcpy(mMsg, msg, len);
+ mTag = (isBinary() && (mMsgLen >= sizeof(uint32_t))) ?
+ le32toh(reinterpret_cast<android_event_header_t *>(mMsg)->tag) :
+ 0;
+}
+
+LogBufferElement::LogBufferElement(const LogBufferElement &elem) :
+ mTag(elem.mTag),
+ mUid(elem.mUid),
+ mPid(elem.mPid),
+ mTid(elem.mTid),
+ mSequence(elem.mSequence),
+ mRealTime(elem.mRealTime),
+ mMsgLen(elem.mMsgLen),
+ mLogId(elem.mLogId) {
+ mMsg = new char[mMsgLen];
+ memcpy(mMsg, elem.mMsg, mMsgLen);
}
LogBufferElement::~LogBufferElement() {
delete [] mMsg;
}
-uint32_t LogBufferElement::getTag() const {
- if ((mLogId != LOG_ID_EVENTS) || !mMsg || (mMsgLen < sizeof(uint32_t))) {
- return 0;
- }
- return le32toh(reinterpret_cast<android_event_header_t *>(mMsg)->tag);
-}
-
// caller must own and free character string
char *android::tidToName(pid_t tid) {
char *retval = NULL;
@@ -91,7 +101,8 @@
size_t retval_len = strlen(retval);
size_t name_len = strlen(name);
// KISS: ToDo: Only checks prefix truncated, not suffix, or both
- if ((retval_len < name_len) && !strcmp(retval, name + name_len - retval_len)) {
+ if ((retval_len < name_len)
+ && !fastcmp<strcmp>(retval, name + name_len - retval_len)) {
free(retval);
retval = name;
} else {
@@ -102,19 +113,21 @@
}
// assumption: mMsg == NULL
-size_t LogBufferElement::populateDroppedMessage(char *&buffer,
- LogBuffer *parent) {
+size_t LogBufferElement::populateDroppedMessage(char*& buffer,
+ LogBuffer* parent, bool lastSame) {
static const char tag[] = "chatty";
- if (!__android_log_is_loggable(ANDROID_LOG_INFO, tag, ANDROID_LOG_VERBOSE)) {
+ if (!__android_log_is_loggable_len(ANDROID_LOG_INFO,
+ tag, strlen(tag),
+ ANDROID_LOG_VERBOSE)) {
return 0;
}
- static const char format_uid[] = "uid=%u%s%s expire %u line%s";
+ static const char format_uid[] = "uid=%u%s%s %s %u line%s";
parent->lock();
- char *name = parent->uidToName(mUid);
+ const char *name = parent->uidToName(mUid);
parent->unlock();
- char *commName = android::tidToName(mTid);
+ const char *commName = android::tidToName(mTid);
if (!commName && (mTid != mPid)) {
commName = android::tidToName(mPid);
}
@@ -123,39 +136,42 @@
commName = parent->pidToName(mPid);
parent->unlock();
}
- size_t len = name ? strlen(name) : 0;
- if (len && commName && !strncmp(name, commName, len)) {
- if (commName[len] == '\0') {
- free(commName);
- commName = NULL;
- } else {
- free(name);
- name = NULL;
+ if (name && name[0] && commName && (name[0] == commName[0])) {
+ size_t len = strlen(name + 1);
+ if (!strncmp(name + 1, commName + 1, len)) {
+ if (commName[len + 1] == '\0') {
+ free(const_cast<char *>(commName));
+ commName = NULL;
+ } else {
+ free(const_cast<char *>(name));
+ name = NULL;
+ }
}
}
if (name) {
- char *p = NULL;
- asprintf(&p, "(%s)", name);
- if (p) {
- free(name);
- name = p;
+ char *buf = NULL;
+ asprintf(&buf, "(%s)", name);
+ if (buf) {
+ free(const_cast<char *>(name));
+ name = buf;
}
}
if (commName) {
- char *p = NULL;
- asprintf(&p, " %s", commName);
- if (p) {
- free(commName);
- commName = p;
+ char *buf = NULL;
+ asprintf(&buf, " %s", commName);
+ if (buf) {
+ free(const_cast<char *>(commName));
+ commName = buf;
}
}
// identical to below to calculate the buffer size required
- len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
- commName ? commName : "",
- mDropped, (mDropped > 1) ? "s" : "");
+ const char* type = lastSame ? "identical" : "expire";
+ size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
+ commName ? commName : "", type,
+ mDropped, (mDropped > 1) ? "s" : "");
size_t hdrLen;
- if (mLogId == LOG_ID_EVENTS) {
+ if (isBinary()) {
hdrLen = sizeof(android_log_event_string_t);
} else {
hdrLen = 1 + sizeof(tag);
@@ -163,18 +179,19 @@
buffer = static_cast<char *>(calloc(1, hdrLen + len + 1));
if (!buffer) {
- free(name);
- free(commName);
+ free(const_cast<char *>(name));
+ free(const_cast<char *>(commName));
return 0;
}
size_t retval = hdrLen + len;
- if (mLogId == LOG_ID_EVENTS) {
- android_log_event_string_t *e = reinterpret_cast<android_log_event_string_t *>(buffer);
+ if (isBinary()) {
+ android_log_event_string_t *event =
+ reinterpret_cast<android_log_event_string_t *>(buffer);
- e->header.tag = htole32(LOGD_LOG_TAG);
- e->type = EVENT_TYPE_STRING;
- e->length = htole32(len);
+ event->header.tag = htole32(CHATTY_LOG_TAG);
+ event->type = EVENT_TYPE_STRING;
+ event->length = htole32(len);
} else {
++retval;
buffer[0] = ANDROID_LOG_INFO;
@@ -182,34 +199,38 @@
}
snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
- commName ? commName : "",
+ commName ? commName : "", type,
mDropped, (mDropped > 1) ? "s" : "");
- free(name);
- free(commName);
+ free(const_cast<char *>(name));
+ free(const_cast<char *>(commName));
return retval;
}
-uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent) {
- struct logger_entry_v3 entry;
+uint64_t LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent,
+ bool privileged, bool lastSame) {
+ struct logger_entry_v4 entry;
- memset(&entry, 0, sizeof(struct logger_entry_v3));
+ memset(&entry, 0, sizeof(struct logger_entry_v4));
- entry.hdr_size = sizeof(struct logger_entry_v3);
+ entry.hdr_size = privileged ?
+ sizeof(struct logger_entry_v4) :
+ sizeof(struct logger_entry_v3);
entry.lid = mLogId;
entry.pid = mPid;
entry.tid = mTid;
+ entry.uid = mUid;
entry.sec = mRealTime.tv_sec;
entry.nsec = mRealTime.tv_nsec;
struct iovec iovec[2];
iovec[0].iov_base = &entry;
- iovec[0].iov_len = sizeof(struct logger_entry_v3);
+ iovec[0].iov_len = entry.hdr_size;
char *buffer = NULL;
if (!mMsg) {
- entry.len = populateDroppedMessage(buffer, parent);
+ entry.len = populateDroppedMessage(buffer, parent, lastSame);
if (!entry.len) {
return mSequence;
}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index ca2c3a6..bd98b9c 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -21,30 +21,8 @@
#include <stdlib.h>
#include <sys/types.h>
-#include <sysutils/SocketClient.h>
#include <log/log.h>
-#include <log/log_read.h>
-
-// Hijack this header as a common include file used by most all sources
-// to report some utilities defined here and there.
-
-namespace android {
-
-// Furnished in main.cpp. Caller must own and free returned value
-char *uidToName(uid_t uid);
-
-// Furnished in LogStatistics.cpp. Caller must own and free returned value
-char *pidToName(pid_t pid);
-char *tidToName(pid_t tid);
-
-// Furnished in main.cpp. Thread safe.
-const char *tagToName(uint32_t tag);
-
-}
-
-static inline bool worstUidEnabledForLogid(log_id_t id) {
- return (id != LOG_ID_CRASH) && (id != LOG_ID_KERNEL) && (id != LOG_ID_EVENTS);
-}
+#include <sysutils/SocketClient.h>
class LogBuffer;
@@ -55,50 +33,62 @@
#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
class LogBufferElement {
- const log_id_t mLogId;
- const uid_t mUid;
- const pid_t mPid;
- const pid_t mTid;
+
+ friend LogBuffer;
+
+ // sized to match reality of incoming log packets
+ uint32_t mTag; // only valid for isBinary()
+ const uint32_t mUid;
+ const uint32_t mPid;
+ const uint32_t mTid;
+ const uint64_t mSequence;
+ log_time mRealTime;
char *mMsg;
union {
- const unsigned short mMsgLen; // mMSg != NULL
- unsigned short mDropped; // mMsg == NULL
+ const uint16_t mMsgLen; // mMSg != NULL
+ uint16_t mDropped; // mMsg == NULL
};
- const uint64_t mSequence;
- const log_time mRealTime;
+ const uint8_t mLogId;
+
static atomic_int_fast64_t sequence;
// assumption: mMsg == NULL
- size_t populateDroppedMessage(char *&buffer,
- LogBuffer *parent);
-
+ size_t populateDroppedMessage(char*& buffer,
+ LogBuffer* parent,
+ bool lastSame);
public:
LogBufferElement(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
const char *msg, unsigned short len);
+ LogBufferElement(const LogBufferElement &elem);
virtual ~LogBufferElement();
- log_id_t getLogId() const { return mLogId; }
+ bool isBinary(void) const {
+ return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY);
+ }
+
+ log_id_t getLogId() const { return static_cast<log_id_t>(mLogId); }
uid_t getUid(void) const { return mUid; }
pid_t getPid(void) const { return mPid; }
pid_t getTid(void) const { return mTid; }
+ uint32_t getTag() const { return mTag; }
unsigned short getDropped(void) const { return mMsg ? 0 : mDropped; }
unsigned short setDropped(unsigned short value) {
if (mMsg) {
- free(mMsg);
+ delete [] mMsg;
mMsg = NULL;
}
return mDropped = value;
}
unsigned short getMsgLen() const { return mMsg ? mMsgLen : 0; }
+ const char* getMsg() const { return mMsg; }
uint64_t getSequence(void) const { return mSequence; }
static uint64_t getCurrentSequence(void) { return sequence.load(memory_order_relaxed); }
log_time getRealTime(void) const { return mRealTime; }
- uint32_t getTag(void) const;
-
static const uint64_t FLUSH_ERROR;
- uint64_t flushTo(SocketClient *writer, LogBuffer *parent);
+ uint64_t flushTo(SocketClient* writer, LogBuffer* parent,
+ bool privileged, bool lastSame);
};
#endif
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
index 06d865c..3b17576 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogCommand.cpp
@@ -22,6 +22,7 @@
#include <private/android_filesystem_config.h>
#include "LogCommand.h"
+#include "LogUtils.h"
LogCommand::LogCommand(const char *cmd) : FrameworkCommand(cmd) {
}
@@ -42,7 +43,6 @@
static bool groupIsLog(char *buf) {
char *ptr;
static const char ws[] = " \n";
- bool ret = false;
for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
errno = 0;
@@ -51,80 +51,100 @@
return false;
}
if (Gid == AID_LOG) {
- ret = true;
+ return true;
}
}
- return ret;
+ return false;
}
-bool clientHasLogCredentials(SocketClient * cli) {
- uid_t uid = cli->getUid();
- if (uid == AID_ROOT) {
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid) {
+ if ((uid == AID_ROOT) || (uid == AID_SYSTEM) || (uid == AID_LOG)) {
return true;
}
- gid_t gid = cli->getGid();
if ((gid == AID_ROOT) || (gid == AID_SYSTEM) || (gid == AID_LOG)) {
return true;
}
// FYI We will typically be here for 'adb logcat'
- bool ret = false;
+ char filename[256];
+ snprintf(filename, sizeof(filename), "/proc/%u/status", pid);
- char filename[1024];
- snprintf(filename, sizeof(filename), "/proc/%d/status", cli->getPid());
-
- FILE *file = fopen(filename, "r");
- if (!file) {
- return ret;
- }
-
+ bool ret;
+ bool foundLog = false;
bool foundGid = false;
bool foundUid = false;
- char line[1024];
- while (fgets(line, sizeof(line), file)) {
- static const char groups_string[] = "Groups:\t";
- static const char uid_string[] = "Uid:\t";
- static const char gid_string[] = "Gid:\t";
-
- if (strncmp(groups_string, line, strlen(groups_string)) == 0) {
- ret = groupIsLog(line + strlen(groups_string));
- if (!ret) {
- break;
- }
- } else if (strncmp(uid_string, line, strlen(uid_string)) == 0) {
- uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
-
- sscanf(line + strlen(uid_string), "%u\t%u\t%u\t%u",
- &u[0], &u[1], &u[2], &u[3]);
-
- // Protect against PID reuse by checking that the UID is the same
- if ((uid != u[0]) || (uid != u[1]) || (uid != u[2]) || (uid != u[3])) {
- ret = false;
- break;
- }
- foundUid = true;
- } else if (strncmp(gid_string, line, strlen(gid_string)) == 0) {
- gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
-
- sscanf(line + strlen(gid_string), "%u\t%u\t%u\t%u",
- &g[0], &g[1], &g[2], &g[3]);
-
- // Protect against PID reuse by checking that the GID is the same
- if ((gid != g[0]) || (gid != g[1]) || (gid != g[2]) || (gid != g[3])) {
- ret = false;
- break;
- }
- foundGid = true;
+ //
+ // Reading /proc/<pid>/status is rife with race conditions. All of /proc
+ // suffers from this and its use should be minimized. However, we have no
+ // choice.
+ //
+ // Notably the content from one 4KB page to the next 4KB page can be from a
+ // change in shape even if we are gracious enough to attempt to read
+ // atomically. getline can not even guarantee a page read is not split up
+ // and in effect can read from different vintages of the content.
+ //
+ // We are finding out in the field that a 'logcat -c' via adb occasionally
+ // is returned with permission denied when we did only one pass and thus
+ // breaking scripts. For security we still err on denying access if in
+ // doubt, but we expect the falses should be reduced significantly as
+ // three times is a charm.
+ //
+ for (int retry = 3;
+ !(ret = foundGid && foundUid && foundLog) && retry;
+ --retry) {
+ FILE *file = fopen(filename, "r");
+ if (!file) {
+ continue;
}
- }
- fclose(file);
+ char *line = NULL;
+ size_t len = 0;
+ while (getline(&line, &len, file) > 0) {
+ static const char groups_string[] = "Groups:\t";
+ static const char uid_string[] = "Uid:\t";
+ static const char gid_string[] = "Gid:\t";
- if (!foundGid || !foundUid) {
- ret = false;
+ if (strncmp(groups_string, line, sizeof(groups_string) - 1) == 0) {
+ if (groupIsLog(line + sizeof(groups_string) - 1)) {
+ foundLog = true;
+ }
+ } else if (strncmp(uid_string, line, sizeof(uid_string) - 1) == 0) {
+ uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
+
+ sscanf(line + sizeof(uid_string) - 1, "%u\t%u\t%u\t%u",
+ &u[0], &u[1], &u[2], &u[3]);
+
+ // Protect against PID reuse by checking that UID is the same
+ if ((uid == u[0])
+ && (uid == u[1])
+ && (uid == u[2])
+ && (uid == u[3])) {
+ foundUid = true;
+ }
+ } else if (strncmp(gid_string, line, sizeof(gid_string) - 1) == 0) {
+ gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
+
+ sscanf(line + sizeof(gid_string) - 1, "%u\t%u\t%u\t%u",
+ &g[0], &g[1], &g[2], &g[3]);
+
+ // Protect against PID reuse by checking that GID is the same
+ if ((gid == g[0])
+ && (gid == g[1])
+ && (gid == g[2])
+ && (gid == g[3])) {
+ foundGid = true;
+ }
+ }
+ }
+ free(line);
+ fclose(file);
}
return ret;
}
+
+bool clientHasLogCredentials(SocketClient *cli) {
+ return clientHasLogCredentials(cli->getUid(), cli->getGid(), cli->getPid());
+}
diff --git a/logd/LogCommand.h b/logd/LogCommand.h
index e3b96a2..0adc2a1 100644
--- a/logd/LogCommand.h
+++ b/logd/LogCommand.h
@@ -22,10 +22,8 @@
class LogCommand : public FrameworkCommand {
public:
- LogCommand(const char *cmd);
+ explicit LogCommand(const char *cmd);
virtual ~LogCommand() {}
};
-bool clientHasLogCredentials(SocketClient * cli);
-
#endif
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 1e6f55f..f224079 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -20,13 +20,17 @@
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/prctl.h>
#include <sys/uio.h>
#include <syslog.h>
-#include <log/logger.h>
+#include <private/android_logger.h>
+#include <private/android_filesystem_config.h>
+#include "LogBuffer.h"
#include "LogKlog.h"
+#include "LogReader.h"
#define KMSG_PRIORITY(PRI) \
'<', \
@@ -39,32 +43,36 @@
// Parsing is hard
// called if we see a '<', s is the next character, returns pointer after '>'
-static char *is_prio(char *s) {
- if (!isdigit(*s++)) {
+static char *is_prio(char *s, size_t len) {
+ if (!len || !isdigit(*s++)) {
return NULL;
}
- static const size_t max_prio_len = 4;
- size_t len = 0;
+ --len;
+ static const size_t max_prio_len = (len < 4) ? len : 4;
+ size_t priolen = 0;
char c;
- while (((c = *s++)) && (++len <= max_prio_len)) {
+ while (((c = *s++)) && (++priolen <= max_prio_len)) {
if (!isdigit(c)) {
- return (c == '>') ? s : NULL;
+ return ((c == '>') && (*s == '[')) ? s : NULL;
}
}
return NULL;
}
// called if we see a '[', s is the next character, returns pointer after ']'
-static char *is_timestamp(char *s) {
- while (*s == ' ') {
+static char *is_timestamp(char *s, size_t len) {
+ while (len && (*s == ' ')) {
++s;
+ --len;
}
- if (!isdigit(*s++)) {
+ if (!len || !isdigit(*s++)) {
return NULL;
}
+ --len;
bool first_period = true;
char c;
- while ((c = *s++)) {
+ while (len && ((c = *s++))) {
+ --len;
if ((c == '.') && first_period) {
first_period = false;
} else if (!isdigit(c)) {
@@ -77,6 +85,8 @@
// Like strtok_r with "\r\n" except that we look for log signatures (regex)
// \(\(<[0-9]\{1,4\}>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \)
// and split if we see a second one without a newline.
+// We allow nuls in content, monitoring the overall length and sub-length of
+// the discovered tokens.
#define SIGNATURE_MASK 0xF0
// <digit> following ('0' to '9' masked with ~SIGNATURE_MASK) added to signature
@@ -85,7 +95,11 @@
// space is one more than <digit> of 9
#define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10))
-char *log_strtok_r(char *s, char **last) {
+char *log_strntok_r(char *s, size_t *len, char **last, size_t *sublen) {
+ *sublen = 0;
+ if (!*len) {
+ return NULL;
+ }
if (!s) {
if (!(s = *last)) {
return NULL;
@@ -95,6 +109,7 @@
if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) {
*s = (*s & ~SIGNATURE_MASK) + '0';
*--s = '<';
+ ++*len;
}
// fixup for log signature split [,
// OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit>
@@ -105,24 +120,30 @@
*s = (*s & ~SIGNATURE_MASK) + '0';
}
*--s = '[';
+ ++*len;
}
}
- s += strspn(s, "\r\n");
+ while (*len && ((*s == '\r') || (*s == '\n'))) {
+ ++s;
+ --*len;
+ }
- if (!*s) { // no non-delimiter characters
+ if (!*len) {
*last = NULL;
return NULL;
}
char *peek, *tok = s;
for (;;) {
- char c = *s++;
- switch (c) {
- case '\0':
+ if (*len == 0) {
*last = NULL;
return tok;
-
+ }
+ char c = *s++;
+ --*len;
+ size_t adjust;
+ switch (c) {
case '\r':
case '\n':
s[-1] = '\0';
@@ -130,7 +151,7 @@
return tok;
case '<':
- peek = is_prio(s);
+ peek = is_prio(s, *len);
if (!peek) {
break;
}
@@ -141,14 +162,26 @@
*last = s;
return tok;
}
+ adjust = peek - s;
+ if (adjust > *len) {
+ adjust = *len;
+ }
+ *sublen += adjust;
+ *len -= adjust;
s = peek;
- if ((*s == '[') && ((peek = is_timestamp(s + 1)))) {
+ if ((*s == '[') && ((peek = is_timestamp(s + 1, *len - 1)))) {
+ adjust = peek - s;
+ if (adjust > *len) {
+ adjust = *len;
+ }
+ *sublen += adjust;
+ *len -= adjust;
s = peek;
}
break;
case '[':
- peek = is_timestamp(s);
+ peek = is_timestamp(s, *len);
if (!peek) {
break;
}
@@ -163,14 +196,24 @@
*last = s;
return tok;
}
+ adjust = peek - s;
+ if (adjust > *len) {
+ adjust = *len;
+ }
+ *sublen += adjust;
+ *len -= adjust;
s = peek;
break;
}
+ ++*sublen;
}
// NOTREACHED
}
-log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
+log_time LogKlog::correction =
+ (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
+ ? log_time::EPOCH
+ : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd) :
SocketListener(fdRead, false),
@@ -212,17 +255,17 @@
bool full = len == (sizeof(buffer) - 1);
char *ep = buffer + len;
*ep = '\0';
- len = 0;
+ size_t sublen;
for(char *ptr = NULL, *tok = buffer;
- ((tok = log_strtok_r(tok, &ptr)));
+ ((tok = log_strntok_r(tok, &len, &ptr, &sublen)));
tok = NULL) {
- if (((tok + strlen(tok)) == ep) && (retval != 0) && full) {
- len = strlen(tok);
- memmove(buffer, tok, len);
+ if (((tok + sublen) >= ep) && (retval != 0) && full) {
+ memmove(buffer, tok, sublen);
+ len = sublen;
break;
}
if (*tok) {
- log(tok);
+ log(tok, sublen);
}
}
}
@@ -232,9 +275,11 @@
void LogKlog::calculateCorrection(const log_time &monotonic,
- const char *real_string) {
+ const char *real_string,
+ size_t len) {
log_time real;
- if (!real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC")) {
+ const char *ep = real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC");
+ if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
return;
}
// kernel report UTC, log_time::strptime is localtime from calendar.
@@ -245,42 +290,98 @@
memset(&tm, 0, sizeof(tm));
tm.tm_isdst = -1;
localtime_r(&now, &tm);
- real.tv_sec += tm.tm_gmtoff;
- correction = real - monotonic;
+ if ((tm.tm_gmtoff < 0) && ((-tm.tm_gmtoff) > (long)real.tv_sec)) {
+ real = log_time::EPOCH;
+ } else {
+ real.tv_sec += tm.tm_gmtoff;
+ }
+ if (monotonic > real) {
+ correction = log_time::EPOCH;
+ } else {
+ correction = real - monotonic;
+ }
}
-void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) {
- const char *cp;
- if ((cp = now.strptime(*buf, "[ %s.%q]"))) {
- static const char suspend[] = "PM: suspend entry ";
- static const char resume[] = "PM: suspend exit ";
- static const char healthd[] = "healthd: battery ";
- static const char suspended[] = "Suspended for ";
+static const char suspendStr[] = "PM: suspend entry ";
+static const char resumeStr[] = "PM: suspend exit ";
+static const char suspendedStr[] = "Suspended for ";
- if (isspace(*cp)) {
+const char* android::strnstr(const char* s, size_t len, const char* needle) {
+ char c;
+
+ if (!len) return NULL;
+ if ((c = *needle++) != 0) {
+ size_t needleLen = strlen(needle);
+ do {
+ do {
+ if (len <= needleLen) return NULL;
+ --len;
+ } while (*s++ != c);
+ } while (fastcmp<memcmp>(s, needle, needleLen));
+ s--;
+ }
+ return s;
+}
+
+void LogKlog::sniffTime(log_time &now,
+ const char **buf, size_t len,
+ bool reverse) {
+ const char *cp = now.strptime(*buf, "[ %s.%q]");
+ if (cp && (cp >= &(*buf)[len])) {
+ cp = NULL;
+ }
+ if (cp) {
+ static const char healthd[] = "healthd";
+ static const char battery[] = ": battery ";
+
+ len -= cp - *buf;
+ if (len && isspace(*cp)) {
++cp;
+ --len;
}
- if (!strncmp(cp, suspend, sizeof(suspend) - 1)) {
- calculateCorrection(now, cp + sizeof(suspend) - 1);
- } else if (!strncmp(cp, resume, sizeof(resume) - 1)) {
- calculateCorrection(now, cp + sizeof(resume) - 1);
- } else if (!strncmp(cp, healthd, sizeof(healthd) - 1)) {
- // look for " 2???-??-?? ??:??:??.????????? ???"
- const char *tp;
- for (tp = cp + sizeof(healthd) - 1; *tp && (*tp != '\n'); ++tp) {
- if ((tp[0] == ' ') && (tp[1] == '2') && (tp[5] == '-')) {
- calculateCorrection(now, tp + 1);
- break;
- }
- }
- } else if (!strncmp(cp, suspended, sizeof(suspended) - 1)) {
+ *buf = cp;
+
+ if (isMonotonic()) {
+ return;
+ }
+
+ const char* b;
+ if (((b = android::strnstr(cp, len, suspendStr)))
+ && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
+ len -= b - cp;
+ calculateCorrection(now, b, len);
+ } else if (((b = android::strnstr(cp, len, resumeStr)))
+ && ((size_t)((b += sizeof(resumeStr) - 1) - cp) < len)) {
+ len -= b - cp;
+ calculateCorrection(now, b, len);
+ } else if (((b = android::strnstr(cp, len, healthd)))
+ && ((size_t)((b += sizeof(healthd) - 1) - cp) < len)
+ && ((b = android::strnstr(b, len -= b - cp, battery)))
+ && ((size_t)((b += sizeof(battery) - 1) - cp) < len)) {
+ // NB: healthd is roughly 150us late, so we use it instead to
+ // trigger a check for ntp-induced or hardware clock drift.
+ log_time real(CLOCK_REALTIME);
+ log_time mono(CLOCK_MONOTONIC);
+ correction = (real < mono) ? log_time::EPOCH : (real - mono);
+ } else if (((b = android::strnstr(cp, len, suspendedStr)))
+ && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
+ len -= b - cp;
log_time real;
char *endp;
- real.tv_sec = strtol(cp + sizeof(suspended) - 1, &endp, 10);
- if (*endp == '.') {
- real.tv_nsec = strtol(endp + 1, &endp, 10) * 1000000L;
+ real.tv_sec = strtol(b, &endp, 10);
+ if ((*endp == '.') && ((size_t)(endp - b) < len)) {
+ unsigned long multiplier = NS_PER_SEC;
+ real.tv_nsec = 0;
+ len -= endp - b;
+ while (--len && isdigit(*++endp) && (multiplier /= 10)) {
+ real.tv_nsec += (*endp - '0') * multiplier;
+ }
if (reverse) {
- correction -= real;
+ if (real > correction) {
+ correction = log_time::EPOCH;
+ } else {
+ correction -= real;
+ }
} else {
correction += real;
}
@@ -288,50 +389,67 @@
}
convertMonotonicToReal(now);
- *buf = cp;
} else {
- now = log_time(CLOCK_REALTIME);
+ if (isMonotonic()) {
+ now = log_time(CLOCK_MONOTONIC);
+ } else {
+ now = log_time(CLOCK_REALTIME);
+ }
}
}
-// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
-// compensated start time.
-void LogKlog::synchronize(const char *buf) {
- const char *cp = strstr(buf, "] PM: suspend e");
- if (!cp) {
- return;
+pid_t LogKlog::sniffPid(const char **buf, size_t len) {
+ const char *cp = *buf;
+ // HTC kernels with modified printk "c0 1648 "
+ if ((len > 9) &&
+ (cp[0] == 'c') &&
+ isdigit(cp[1]) &&
+ (isdigit(cp[2]) || (cp[2] == ' ')) &&
+ (cp[3] == ' ')) {
+ bool gotDigit = false;
+ int i;
+ for (i = 4; i < 9; ++i) {
+ if (isdigit(cp[i])) {
+ gotDigit = true;
+ } else if (gotDigit || (cp[i] != ' ')) {
+ break;
+ }
+ }
+ if ((i == 9) && (cp[i] == ' ')) {
+ int pid = 0;
+ char dummy;
+ if (sscanf(cp + 4, "%d%c", &pid, &dummy) == 2) {
+ *buf = cp + 10; // skip-it-all
+ return pid;
+ }
+ }
}
-
- do {
- --cp;
- } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
-
- log_time now;
- sniffTime(now, &cp, true);
-
- char *suspended = strstr(buf, "] Suspended for ");
- if (!suspended || (suspended > cp)) {
- return;
+ while (len) {
+ // Mediatek kernels with modified printk
+ if (*cp == '[') {
+ int pid = 0;
+ char dummy;
+ if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &dummy) == 2) {
+ return pid;
+ }
+ break; // Only the first one
+ }
+ ++cp;
+ --len;
}
- cp = suspended;
-
- do {
- --cp;
- } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
-
- sniffTime(now, &cp, true);
+ return 0;
}
// kernel log prefix, convert to a kernel log priority number
-static int parseKernelPrio(const char **buf) {
+static int parseKernelPrio(const char **buf, size_t len) {
int pri = LOG_USER | LOG_INFO;
const char *cp = *buf;
- if (*cp == '<') {
+ if (len && (*cp == '<')) {
pri = 0;
- while(isdigit(*++cp)) {
+ while(--len && isdigit(*++cp)) {
pri = (pri * 10) + *cp - '0';
}
- if (*cp == '>') {
+ if (len && (*cp == '>')) {
++cp;
} else {
cp = *buf;
@@ -342,6 +460,46 @@
return pri;
}
+// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
+// compensated start time.
+void LogKlog::synchronize(const char* buf, size_t len) {
+ const char* cp = android::strnstr(buf, len, suspendStr);
+ if (!cp) {
+ cp = android::strnstr(buf, len, resumeStr);
+ if (!cp) return;
+ } else {
+ const char* rp = android::strnstr(buf, len, resumeStr);
+ if (rp && (rp < cp)) cp = rp;
+ }
+
+ do {
+ --cp;
+ } while ((cp > buf) && (*cp != '\n'));
+ if (*cp == '\n') {
+ ++cp;
+ }
+ parseKernelPrio(&cp, len - (cp - buf));
+
+ log_time now;
+ sniffTime(now, &cp, len - (cp - buf), true);
+
+ const char* suspended = android::strnstr(buf, len, suspendedStr);
+ if (!suspended || (suspended > cp)) {
+ return;
+ }
+ cp = suspended;
+
+ do {
+ --cp;
+ } while ((cp > buf) && (*cp != '\n'));
+ if (*cp == '\n') {
+ ++cp;
+ }
+ parseKernelPrio(&cp, len - (cp - buf));
+
+ sniffTime(now, &cp, len - (cp - buf), true);
+}
+
// Convert kernel log priority number into an Android Logger priority number
static int convertKernelPrioToAndroidPrio(int pri) {
switch(pri & LOG_PRIMASK) {
@@ -372,6 +530,16 @@
return ANDROID_LOG_INFO;
}
+static const char *strnrchr(const char *s, size_t len, char c) {
+ const char *save = NULL;
+ for (;len; ++s, len--) {
+ if (*s == c) {
+ save = s;
+ }
+ }
+ return save;
+}
+
//
// log a message into the kernel log buffer
//
@@ -405,21 +573,22 @@
// logd.klogd:
// return -1 if message logd.klogd: <signature>
//
-int LogKlog::log(const char *buf) {
- if (auditd && strstr(buf, " audit(")) {
+int LogKlog::log(const char* buf, size_t len) {
+ if (auditd && android::strnstr(buf, len, " audit(")) {
return 0;
}
- int pri = parseKernelPrio(&buf);
+ const char* p = buf;
+ int pri = parseKernelPrio(&p, len);
log_time now;
- sniffTime(now, &buf, false);
+ sniffTime(now, &p, len - (p - buf), false);
// sniff for start marker
const char klogd_message[] = "logd.klogd: ";
- if (!strncmp(buf, klogd_message, sizeof(klogd_message) - 1)) {
- char *endp;
- uint64_t sig = strtoll(buf + sizeof(klogd_message) - 1, &endp, 10);
+ const char* start = android::strnstr(p, len - (p - buf), klogd_message);
+ if (start) {
+ uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10);
if (sig == signature.nsec()) {
if (initialized) {
enableLogging = true;
@@ -435,152 +604,198 @@
return 0;
}
- // Parse pid, tid and uid (not possible)
- const pid_t pid = 0;
- const pid_t tid = 0;
- const uid_t uid = 0;
+ // Parse pid, tid and uid
+ const pid_t pid = sniffPid(&p, len - (p - buf));
+ const pid_t tid = pid;
+ uid_t uid = AID_ROOT;
+ if (pid) {
+ logbuf->lock();
+ uid = logbuf->pidToUid(pid);
+ logbuf->unlock();
+ }
// Parse (rules at top) to pull out a tag from the incoming kernel message.
// Some may view the following as an ugly heuristic, the desire is to
// beautify the kernel logs into an Android Logging format; the goal is
// admirable but costly.
- while (isspace(*buf)) {
- ++buf;
+ while ((p < &buf[len]) && (isspace(*p) || !*p)) {
+ ++p;
}
- if (!*buf) {
+ if (p >= &buf[len]) { // timestamp, no content
return 0;
}
- const char *start = buf;
+ start = p;
const char *tag = "";
const char *etag = tag;
- if (!isspace(*buf)) {
- const char *bt, *et, *cp;
+ size_t taglen = len - (p - buf);
+ const char *bt = p;
- bt = buf;
- if (!strncmp(buf, "[INFO]", 6)) {
- // <PRI>[<TIME>] "[INFO]"<tag> ":" message
- bt = buf + 6;
- }
- for(et = bt; *et && (*et != ':') && !isspace(*et); ++et);
- for(cp = et; isspace(*cp); ++cp);
- size_t size;
+ static const char infoBrace[] = "[INFO]";
+ static const size_t infoBraceLen = strlen(infoBrace);
+ if ((taglen >= infoBraceLen) && !fastcmp<strncmp>(p, infoBrace, infoBraceLen)) {
+ // <PRI>[<TIME>] "[INFO]"<tag> ":" message
+ bt = p + infoBraceLen;
+ taglen -= infoBraceLen;
+ }
+ const char *et;
+ for (et = bt; taglen && *et && (*et != ':') && !isspace(*et); ++et, --taglen) {
+ // skip ':' within [ ... ]
+ if (*et == '[') {
+ while (taglen && *et && *et != ']') {
+ ++et;
+ --taglen;
+ }
+ if (!taglen) {
+ break;
+ }
+ }
+ }
+ const char *cp;
+ for (cp = et; taglen && isspace(*cp); ++cp, --taglen);
+
+ // Validate tag
+ size_t size = et - bt;
+ if (taglen && size) {
if (*cp == ':') {
+ // ToDo: handle case insensitive colon separated logging stutter:
+ // <tag> : <tag>: ...
+
// One Word
tag = bt;
etag = et;
- buf = cp + 1;
- } else {
- size = et - bt;
- if (strncmp(bt, cp, size)) {
- // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
- if (!strncmp(bt + size - 5, "_host", 5)
- && !strncmp(bt, cp, size - 5)) {
+ p = cp + 1;
+ } else if ((taglen > size) && (tolower(*bt) == tolower(*cp))) {
+ // clean up any tag stutter
+ if (!fastcmp<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
+ // <PRI>[<TIME>] <tag> <tag> : message
+ // <PRI>[<TIME>] <tag> <tag>: message
+ // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><stuff>' : message
+ const char *b = cp;
+ cp += size;
+ taglen -= size;
+ while (--taglen && !isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ if (taglen && (*cp == ':')) {
+ tag = b;
+ etag = e;
+ p = cp + 1;
+ }
+ } else {
+ // what about <PRI>[<TIME>] <tag>_host '<tag><stuff>' : message
+ static const char host[] = "_host";
+ static const size_t hostlen = strlen(host);
+ if ((size > hostlen) &&
+ !fastcmp<strncmp>(bt + size - hostlen, host, hostlen) &&
+ !fastcmp<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
const char *b = cp;
- cp += size - 5;
+ cp += size - hostlen;
+ taglen -= size - hostlen;
if (*cp == '.') {
- while (!isspace(*++cp) && (*cp != ':'));
+ while (--taglen && !isspace(*++cp) && (*cp != ':'));
const char *e;
- for(e = cp; isspace(*cp); ++cp);
- if (*cp == ':') {
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ if (taglen && (*cp == ':')) {
tag = b;
etag = e;
- buf = cp + 1;
+ p = cp + 1;
}
}
} else {
- while (!isspace(*++cp) && (*cp != ':'));
- const char *e;
- for(e = cp; isspace(*cp); ++cp);
- // Two words
- if (*cp == ':') {
- tag = bt;
- etag = e;
- buf = cp + 1;
- }
- }
- } else if (isspace(cp[size])) {
- cp += size;
- while (isspace(*++cp));
- // <PRI>[<TIME>] <tag> <tag> : message
- if (*cp == ':') {
- tag = bt;
- etag = et;
- buf = cp + 1;
- }
- } else if (cp[size] == ':') {
- // <PRI>[<TIME>] <tag> <tag> : message
- tag = bt;
- etag = et;
- buf = cp + size + 1;
- } else if ((cp[size] == '.') || isdigit(cp[size])) {
- // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
- // <PRI>[<TIME>] <tag> '<tag><num>' : message
- const char *b = cp;
- cp += size;
- while (!isspace(*++cp) && (*cp != ':'));
- const char *e = cp;
- while (isspace(*cp)) {
- ++cp;
- }
- if (*cp == ':') {
- tag = b;
- etag = e;
- buf = cp + 1;
- }
- } else {
- while (!isspace(*++cp) && (*cp != ':'));
- const char *e = cp;
- while (isspace(*cp)) {
- ++cp;
- }
- // Two words
- if (*cp == ':') {
- tag = bt;
- etag = e;
- buf = cp + 1;
+ goto twoWord;
}
}
+ } else {
+ // <PRI>[<TIME>] <tag> <stuff>' : message
+twoWord: while (--taglen && !isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ // Two words
+ if (taglen && (*cp == ':')) {
+ tag = bt;
+ etag = e;
+ p = cp + 1;
+ }
}
- size = etag - tag;
- if ((size <= 1)
- // register names like x9
- || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
- // register names like x18 but not driver names like en0
- || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
- // blacklist
- || ((size == 3) && !strncmp(tag, "CPU", 3))
- || ((size == 7) && !strncmp(tag, "WARNING", 7))
- || ((size == 5) && !strncmp(tag, "ERROR", 5))
- || ((size == 4) && !strncmp(tag, "INFO", 4))) {
- buf = start;
- etag = tag = "";
+ } // else no tag
+
+ static const char cpu[] = "CPU";
+ static const size_t cpuLen = strlen(cpu);
+ static const char warning[] = "WARNING";
+ static const size_t warningLen = strlen(warning);
+ static const char error[] = "ERROR";
+ static const size_t errorLen = strlen(error);
+ static const char info[] = "INFO";
+ static const size_t infoLen = strlen(info);
+
+ size = etag - tag;
+ if ((size <= 1)
+ // register names like x9
+ || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
+ // register names like x18 but not driver names like en0
+ || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
+ // blacklist
+ || ((size == cpuLen) && !fastcmp<strncmp>(tag, cpu, cpuLen))
+ || ((size == warningLen) && !fastcmp<strncasecmp>(tag, warning, warningLen))
+ || ((size == errorLen) && !fastcmp<strncasecmp>(tag, error, errorLen))
+ || ((size == infoLen) && !fastcmp<strncasecmp>(tag, info, infoLen))) {
+ p = start;
+ etag = tag = "";
+ }
+
+ // Suppress additional stutter in tag:
+ // eg: [143:healthd]healthd -> [143:healthd]
+ taglen = etag - tag;
+ // Mediatek-special printk induced stutter
+ const char *mp = strnrchr(tag, ']', taglen);
+ if (mp && (++mp < etag)) {
+ size_t s = etag - mp;
+ if (((s + s) < taglen) && !fastcmp<memcmp>(mp, mp - 1 - s, s)) {
+ taglen = mp - tag;
}
}
- size_t l = etag - tag;
+ // Deal with sloppy and simplistic harmless p = cp + 1 etc above.
+ if (len < (size_t)(p - buf)) {
+ p = &buf[len];
+ }
// skip leading space
- while (isspace(*buf)) {
- ++buf;
+ while ((p < &buf[len]) && (isspace(*p) || !*p)) {
+ ++p;
}
- // truncate trailing space
- size_t b = strlen(buf);
- while (b && isspace(buf[b-1])) {
+ // truncate trailing space or nuls
+ size_t b = len - (p - buf);
+ while (b && (isspace(p[b-1]) || !p[b-1])) {
--b;
}
// trick ... allow tag with empty content to be logged. log() drops empty
- if (!b && l) {
- buf = " ";
+ if (!b && taglen) {
+ p = " ";
b = 1;
}
- size_t n = 1 + l + 1 + b + 1;
-
- // Allocate a buffer to hold the interpreted log message
- int rc = n;
- char *newstr = reinterpret_cast<char *>(malloc(n));
- if (!newstr) {
- rc = -ENOMEM;
- return rc;
+ // paranoid sanity check, can not happen ...
+ if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
+ b = LOGGER_ENTRY_MAX_PAYLOAD;
}
+ if (taglen > LOGGER_ENTRY_MAX_PAYLOAD) {
+ taglen = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+ // calculate buffer copy requirements
+ size_t n = 1 + taglen + 1 + b + 1;
+ // paranoid sanity check, first two just can not happen ...
+ if ((taglen > n) || (b > n) || (n > USHRT_MAX)) {
+ return -EINVAL;
+ }
+
+ // Careful.
+ // We are using the stack to house the log buffer for speed reasons.
+ // If we malloc'd this buffer, we could get away without n's USHRT_MAX
+ // test above, but we would then required a max(n, USHRT_MAX) as
+ // truncating length argument to logbuf->log() below. Gain is protection
+ // of stack sanity and speedup, loss is truncated long-line content.
+ char newstr[n];
char *np = newstr;
// Convert priority into single-byte Android logger priority
@@ -588,19 +803,43 @@
++np;
// Copy parsed tag following priority
- strncpy(np, tag, l);
- np += l;
+ memcpy(np, tag, taglen);
+ np += taglen;
*np = '\0';
++np;
// Copy main message to the remainder
- strncpy(np, buf, b);
+ memcpy(np, p, b);
np[b] = '\0';
+ if (!isMonotonic()) {
+ // Watch out for singular race conditions with timezone causing near
+ // integer quarter-hour jumps in the time and compensate accordingly.
+ // Entries will be temporal within near_seconds * 2. b/21868540
+ static uint32_t vote_time[3];
+ vote_time[2] = vote_time[1];
+ vote_time[1] = vote_time[0];
+ vote_time[0] = now.tv_sec;
+
+ if (vote_time[1] && vote_time[2]) {
+ static const unsigned near_seconds = 10;
+ static const unsigned timezones_seconds = 900;
+ int diff0 = (vote_time[0] - vote_time[1]) / near_seconds;
+ unsigned abs0 = (diff0 < 0) ? -diff0 : diff0;
+ int diff1 = (vote_time[1] - vote_time[2]) / near_seconds;
+ unsigned abs1 = (diff1 < 0) ? -diff1 : diff1;
+ if ((abs1 <= 1) && // last two were in agreement on timezone
+ ((abs0 + 1) % (timezones_seconds / near_seconds)) <= 2) {
+ abs0 = (abs0 + 1) / (timezones_seconds / near_seconds) *
+ timezones_seconds;
+ now.tv_sec -= (diff0 < 0) ? -abs0 : abs0;
+ }
+ }
+ }
+
// Log message
- rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
- (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
- free(newstr);
+ int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
+ (unsigned short) n);
// notify readers
if (!rc) {
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 24b2685..11d88af 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -17,11 +17,13 @@
#ifndef _LOGD_LOG_KLOG_H__
#define _LOGD_LOG_KLOG_H__
+#include <private/android_logger.h>
#include <sysutils/SocketListener.h>
-#include <log/log_read.h>
-#include "LogReader.h"
-char *log_strtok_r(char *str, char **saveptr);
+char *log_strntok_r(char *s, size_t *len, char **saveptr, size_t *sublen);
+
+class LogBuffer;
+class LogReader;
class LogKlog : public SocketListener {
LogBuffer *logbuf;
@@ -40,14 +42,18 @@
public:
LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd);
- int log(const char *buf);
- void synchronize(const char *buf);
+ int log(const char *buf, size_t len);
+ void synchronize(const char *buf, size_t len);
+ bool isMonotonic() { return logbuf->isMonotonic(); }
static void convertMonotonicToReal(log_time &real) { real += correction; }
+ static void convertRealToMonotonic(log_time &real) { real -= correction; }
protected:
- void sniffTime(log_time &now, const char **buf, bool reverse);
- void calculateCorrection(const log_time &monotonic, const char *real_string);
+ void sniffTime(log_time &now, const char **buf, size_t len, bool reverse);
+ pid_t sniffPid(const char **buf, size_t len);
+ void calculateCorrection(const log_time &monotonic,
+ const char *real_string, size_t len);
virtual bool onDataAvailable(SocketClient *cli);
};
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index b29f5ab..4a30e6d 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -15,6 +15,7 @@
*/
#include <limits.h>
+#include <sys/cdefs.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -22,11 +23,12 @@
#include <unistd.h>
#include <cutils/sockets.h>
-#include <log/logger.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include "LogBuffer.h"
#include "LogListener.h"
+#include "LogUtils.h"
LogListener::LogListener(LogBuffer *buf, LogReader *reader) :
SocketListener(getLogSocket(), false),
@@ -44,9 +46,8 @@
char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time)
+ LOGGER_ENTRY_MAX_PAYLOAD];
struct iovec iov = { buffer, sizeof(buffer) };
- memset(buffer, 0, sizeof(buffer));
- char control[CMSG_SPACE(sizeof(struct ucred))];
+ alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
struct msghdr hdr = {
NULL,
0,
@@ -59,6 +60,9 @@
int socket = cli->getSocket();
+ // To clear the entire buffer is secure/safe, but this contributes to 1.68%
+ // overhead under logging load. We are safe because we check counts.
+ // memset(buffer, 0, sizeof(buffer));
ssize_t n = recvmsg(socket, &hdr, 0);
if (n <= (ssize_t)(sizeof(android_log_header_t))) {
return false;
@@ -92,6 +96,12 @@
return false;
}
+ if ((header->id == LOG_ID_SECURITY) &&
+ (!__android_log_security() ||
+ !clientHasLogCredentials(cred->uid, cred->gid, cred->pid))) {
+ return false;
+ }
+
char *msg = ((char *)buffer) + sizeof(android_log_header_t);
n -= sizeof(android_log_header_t);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index c7deec0..1b50b4e 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -18,11 +18,16 @@
#include <poll.h>
#include <sys/prctl.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <cutils/sockets.h>
+#include <private/android_logger.h>
-#include "LogReader.h"
#include "FlushCommand.h"
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReader.h"
+#include "LogUtils.h"
LogReader::LogReader(LogBuffer *logbuf) :
SocketListener(getLogSocket(), true),
@@ -67,6 +72,14 @@
start.strptime(cp + sizeof(_start) - 1, "%s.%q");
}
+ uint64_t timeout = 0;
+ static const char _timeout[] = " timeout=";
+ cp = strstr(buffer, _timeout);
+ if (cp) {
+ timeout = atol(cp + sizeof(_timeout) - 1) * NS_PER_SEC +
+ log_time(CLOCK_REALTIME).nsec();
+ }
+
unsigned int logMask = -1;
static const char _logIds[] = " lids=";
cp = strstr(buffer, _logIds);
@@ -95,7 +108,7 @@
}
bool nonBlock = false;
- if (strncmp(buffer, "dumpAndClose", 12) == 0) {
+ if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
// Allow writer to get some cycles, and wait for pending notifications
sched_yield();
LogTimeEntry::lock();
@@ -114,15 +127,17 @@
log_time &start;
uint64_t &sequence;
uint64_t last;
+ bool isMonotonic;
public:
- LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) :
+ LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence, bool isMonotonic) :
mPid(pid),
mLogMask(logMask),
startTimeSet(false),
start(start),
sequence(sequence),
- last(sequence) {
+ last(sequence),
+ isMonotonic(isMonotonic) {
}
static int callback(const LogBufferElement *element, void *obj) {
@@ -133,22 +148,27 @@
me->sequence = element->getSequence();
me->startTimeSet = true;
return -1;
- } else {
+ } else if (!me->isMonotonic ||
+ android::isMonotonic(element->getRealTime())) {
if (me->start < element->getRealTime()) {
me->sequence = me->last;
me->startTimeSet = true;
return -1;
}
me->last = element->getSequence();
+ } else {
+ me->last = element->getSequence();
}
}
return false;
}
bool found() { return startTimeSet; }
- } logFindStart(logMask, pid, start, sequence);
+ } logFindStart(logMask, pid, start, sequence,
+ logbuf().isMonotonic() && android::isMonotonic(start));
logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
+ FlushCommand::hasSecurityLogs(cli),
logFindStart.callback, &logFindStart);
if (!logFindStart.found()) {
@@ -160,7 +180,12 @@
}
}
- FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence);
+ FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
+
+ // Set acceptable upper limit to wait for slow reader processing b/27242723
+ struct timeval t = { LOGD_SNDTIMEO, 0 };
+ setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char *)&t, sizeof(t));
+
command.runSocketCommand(cli);
return true;
}
diff --git a/logd/LogReader.h b/logd/LogReader.h
index 91559a3..fdcedf1 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -18,14 +18,16 @@
#define _LOGD_LOG_WRITER_H__
#include <sysutils/SocketListener.h>
-#include "LogBuffer.h"
-#include "LogTimes.h"
+
+#define LOGD_SNDTIMEO 32
+
+class LogBuffer;
class LogReader : public SocketListener {
LogBuffer &mLogbuf;
public:
- LogReader(LogBuffer *logbuf);
+ explicit LogReader(LogBuffer *logbuf);
void notifyNewLog();
LogBuffer &logbuf(void) const { return mLogbuf; }
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 48c2fe6..7e0a6b7 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,22 +14,26 @@
* limitations under the License.
*/
-#include <algorithm> // std::max
#include <fcntl.h>
+#include <pwd.h>
#include <stdio.h>
#include <string.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <log/logger.h>
-#include <private/android_filesystem_config.h>
-#include <utils/String8.h>
+#include <list>
+
+#include <android/log.h>
#include "LogStatistics.h"
+size_t LogStatistics::SizesTotal;
+
LogStatistics::LogStatistics() : enable(false) {
log_id_for_each(id) {
mSizes[id] = 0;
mElements[id] = 0;
+ mDroppedElements[id] = 0;
mSizesTotal[id] = 0;
mElementsTotal[id] = 0;
}
@@ -37,6 +41,8 @@
namespace android {
+size_t sizesTotal() { return LogStatistics::sizesTotal(); }
+
// caller must own and free character string
char *pidToName(pid_t pid) {
char *retval = NULL;
@@ -51,7 +57,7 @@
if (ret > 0) {
buffer[sizeof(buffer)-1] = '\0';
// frameworks intermediate state
- if (strcmp(buffer, "<pre-initialized>")) {
+ if (fastcmp<strcmp>(buffer, "<pre-initialized>")) {
retval = strdup(buffer);
}
}
@@ -63,96 +69,135 @@
}
-void LogStatistics::add(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();
+void LogStatistics::add(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
mSizes[log_id] += size;
++mElements[log_id];
- mSizesTotal[log_id] += size;
- ++mElementsTotal[log_id];
+ if (element->getDropped()) {
+ ++mDroppedElements[log_id];
+ } else {
+ // When caller adding a chatty entry, they will have already
+ // called add() and subtract() for each entry as they are
+ // evaluated and trimmed, thus recording size and number of
+ // elements, but we must recognize the manufactured dropped
+ // entry as not contributing to the lifetime totals.
+ mSizesTotal[log_id] += size;
+ SizesTotal += size;
+ ++mElementsTotal[log_id];
+ }
if (log_id == LOG_ID_KERNEL) {
return;
}
- uidTable[log_id].add(e->getUid(), e);
+ uidTable[log_id].add(element->getUid(), element);
+ if (element->getUid() == AID_SYSTEM) {
+ pidSystemTable[log_id].add(element->getPid(), element);
+ }
if (!enable) {
return;
}
- pidTable.add(e->getPid(), e);
- tidTable.add(e->getTid(), e);
+ pidTable.add(element->getPid(), element);
+ tidTable.add(element->getTid(), element);
- uint32_t tag = e->getTag();
+ uint32_t tag = element->getTag();
if (tag) {
- tagTable.add(tag, e);
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.add(tag, element);
+ } else {
+ tagTable.add(tag, element);
+ }
}
}
-void LogStatistics::subtract(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();
+void LogStatistics::subtract(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
mSizes[log_id] -= size;
--mElements[log_id];
+ if (element->getDropped()) {
+ --mDroppedElements[log_id];
+ }
if (log_id == LOG_ID_KERNEL) {
return;
}
- uidTable[log_id].subtract(e->getUid(), e);
+ uidTable[log_id].subtract(element->getUid(), element);
+ if (element->getUid() == AID_SYSTEM) {
+ pidSystemTable[log_id].subtract(element->getPid(), element);
+ }
if (!enable) {
return;
}
- pidTable.subtract(e->getPid(), e);
- tidTable.subtract(e->getTid(), e);
+ pidTable.subtract(element->getPid(), element);
+ tidTable.subtract(element->getTid(), element);
- uint32_t tag = e->getTag();
+ uint32_t tag = element->getTag();
if (tag) {
- tagTable.subtract(tag, e);
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.subtract(tag, element);
+ } else {
+ tagTable.subtract(tag, element);
+ }
}
}
// Atomically set an entry to drop
// entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::drop(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();
+void LogStatistics::drop(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
mSizes[log_id] -= size;
+ ++mDroppedElements[log_id];
- uidTable[log_id].drop(e->getUid(), e);
+ uidTable[log_id].drop(element->getUid(), element);
+ if (element->getUid() == AID_SYSTEM) {
+ pidSystemTable[log_id].drop(element->getPid(), element);
+ }
if (!enable) {
return;
}
- pidTable.drop(e->getPid(), e);
- tidTable.drop(e->getTid(), e);
+ pidTable.drop(element->getPid(), element);
+ tidTable.drop(element->getTid(), element);
+
+ uint32_t tag = element->getTag();
+ if (tag) {
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.drop(tag, element);
+ } else {
+ tagTable.drop(tag, element);
+ }
+ }
}
// caller must own and free character string
-char *LogStatistics::uidToName(uid_t uid) {
+const char *LogStatistics::uidToName(uid_t uid) const {
// Local hard coded favourites
if (uid == AID_LOGD) {
return strdup("auditd");
}
- // Android hard coded
- const struct android_id_info *info = android_ids;
-
- for (size_t i = 0; i < android_id_count; ++i) {
- if (info->aid == uid) {
- return strdup(info->name);
+ // Android system
+ if (uid < AID_APP) {
+ // in bionic, thread safe as long as we copy the results
+ struct passwd *pwd = getpwuid(uid);
+ if (pwd) {
+ return strdup(pwd->pw_name);
}
- ++info;
}
// Parse /data/system/packages.list
- uid_t userId = uid % AID_USER;
- char *name = android::uidToName(userId);
+ uid_t userId = uid % AID_USER_OFFSET;
+ const char *name = android::uidToName(userId);
if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
}
@@ -160,18 +205,26 @@
return name;
}
+ // Android application
+ if (uid >= AID_APP) {
+ struct passwd *pwd = getpwuid(uid);
+ if (pwd) {
+ return strdup(pwd->pw_name);
+ }
+ }
+
// report uid -> pid(s) -> pidToName if unique
- for(pidTable_t::iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
+ for(pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
const PidEntry &entry = it->second;
if (entry.getUid() == uid) {
- const char *n = entry.getName();
+ const char *nameTmp = entry.getName();
- if (n) {
+ if (nameTmp) {
if (!name) {
- name = strdup(n);
- } else if (strcmp(name, n)) {
- free(name);
+ name = strdup(nameTmp);
+ } else if (fastcmp<strcmp>(name, nameTmp)) {
+ free(const_cast<char *>(name));
name = NULL;
break;
}
@@ -183,318 +236,374 @@
return name;
}
-static void format_line(android::String8 &output,
- android::String8 &name, android::String8 &size, android::String8 &pruned) {
- static const size_t pruned_len = 6;
- static const size_t total_len = 70 + pruned_len;
-
- ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
- ssize_t size_len = std::max(size.length() + 1,
- total_len - name.length() - drop_len - 1);
-
- if (pruned.length()) {
- output.appendFormat("%s%*s%*s\n", name.string(),
- (int)size_len, size.string(),
- (int)drop_len, pruned.string());
- } else {
- output.appendFormat("%s%*s\n", name.string(),
- (int)size_len, size.string());
- }
+std::string UidEntry::formatHeader(const std::string &name, log_id_t id) const {
+ bool isprune = worstUidEnabledForLogid(id);
+ return formatLine(android::base::StringPrintf(
+ name.c_str(), android_log_id_to_name(id)),
+ std::string("Size"),
+ std::string(isprune ? "+/- Pruned" : ""))
+ + formatLine(std::string("UID PACKAGE"),
+ std::string("BYTES"),
+ std::string(isprune ? "NUM" : ""));
}
-void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
- static const unsigned short spaces_total = 19;
-
- if (*buf) {
- free(*buf);
- *buf = NULL;
+std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const {
+ uid_t uid = getUid();
+ std::string name = android::base::StringPrintf("%u", uid);
+ const char *nameTmp = stat.uidToName(uid);
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
+ "", nameTmp);
+ free(const_cast<char *>(nameTmp));
}
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ std::string pruned = "";
+ if (worstUidEnabledForLogid(id)) {
+ size_t totalDropped = 0;
+ for (LogStatistics::uidTable_t::const_iterator it = stat.uidTable[id].begin();
+ it != stat.uidTable[id].end(); ++it) {
+ totalDropped += it->second.getDropped();
+ }
+ size_t sizes = stat.sizes(id);
+ size_t totalSize = stat.sizesTotal(id);
+ size_t totalElements = stat.elementsTotal(id);
+ float totalVirtualSize = (float)sizes + (float)totalDropped * totalSize
+ / totalElements;
+ size_t entrySize = getSizes();
+ float virtualEntrySize = entrySize;
+ int realPermille = virtualEntrySize * 1000.0 / sizes;
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ virtualEntrySize += (float)dropped * totalSize / totalElements;
+ }
+ int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
+ int permille = (realPermille - virtualPermille) * 1000L
+ / (virtualPermille ?: 1);
+ if ((permille < -1) || (1 < permille)) {
+ std::string change;
+ const char *units = "%";
+ const char *prefix = (permille > 0) ? "+" : "";
+
+ if (permille > 999) {
+ permille = (permille + 1000) / 100; // Now tenths fold
+ units = "X";
+ prefix = "";
+ }
+ if ((-99 < permille) && (permille < 99)) {
+ change = android::base::StringPrintf("%s%d.%u%s",
+ prefix,
+ permille / 10,
+ ((permille < 0) ? (-permille % 10) : (permille % 10)),
+ units);
+ } else {
+ change = android::base::StringPrintf("%s%d%s",
+ prefix,
+ (permille + 5) / 10, units);
+ }
+ ssize_t spaces = EntryBaseConstants::pruned_len
+ - 2 - pruned.length() - change.length();
+ if ((spaces <= 0) && pruned.length()) {
+ spaces = 1;
+ }
+ if (spaces > 0) {
+ change += android::base::StringPrintf("%*s", (int)spaces, "");
+ }
+ pruned = change + pruned;
+ }
+ }
+
+ std::string output = formatLine(name, size, pruned);
+
+ if (uid != AID_SYSTEM) {
+ return output;
+ }
+
+ static const size_t maximum_sorted_entries = 32;
+ std::unique_ptr<const PidEntry *[]> sorted
+ = stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
+
+ if (!sorted.get()) {
+ return output;
+ }
+ std::string byPid;
+ size_t index;
+ bool hasDropped = false;
+ for (index = 0; index < maximum_sorted_entries; ++index) {
+ const PidEntry *entry = sorted[index];
+ if (!entry) {
+ break;
+ }
+ if (entry->getSizes() <= (getSizes() / 100)) {
+ break;
+ }
+ if (entry->getDropped()) {
+ hasDropped = true;
+ }
+ byPid += entry->format(stat, id);
+ }
+ if (index > 1) { // print this only if interesting
+ std::string ditto("\" ");
+ output += formatLine(std::string(" PID/UID COMMAND LINE"),
+ ditto, hasDropped ? ditto : std::string(""));
+ output += byPid;
+ }
+
+ return output;
+}
+
+std::string PidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+ return formatLine(name,
+ std::string("Size"),
+ std::string("Pruned"))
+ + formatLine(std::string(" PID/UID COMMAND LINE"),
+ std::string("BYTES"),
+ std::string("NUM"));
+}
+
+std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+ uid_t uid = getUid();
+ pid_t pid = getPid();
+ std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
+ const char *nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ } else if ((nameTmp = stat.uidToName(uid))) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ free(const_cast<char *>(nameTmp));
+ }
+
+ std::string size = android::base::StringPrintf("%zu",
+ getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string TidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+ return formatLine(name,
+ std::string("Size"),
+ std::string("Pruned"))
+ + formatLine(std::string(" TID/UID COMM"),
+ std::string("BYTES"),
+ std::string("NUM"));
+}
+
+std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+ uid_t uid = getUid();
+ std::string name = android::base::StringPrintf("%5u/%u",
+ getTid(), uid);
+ const char *nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ } else if ((nameTmp = stat.uidToName(uid))) {
+ // if we do not have a PID name, lets punt to try UID name?
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ free(const_cast<char *>(nameTmp));
+ // We tried, better to not have a name at all, we still
+ // have TID/UID by number to report in any case.
+ }
+
+ std::string size = android::base::StringPrintf("%zu",
+ getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string TagEntry::formatHeader(const std::string &name, log_id_t id) const {
+ bool isprune = worstUidEnabledForLogid(id);
+ return formatLine(name,
+ std::string("Size"),
+ std::string(isprune ? "Prune" : ""))
+ + formatLine(std::string(" TAG/UID TAGNAME"),
+ std::string("BYTES"),
+ std::string(isprune ? "NUM" : ""));
+}
+
+std::string TagEntry::format(const LogStatistics & /* stat */, log_id_t /* id */) const {
+ std::string name;
+ uid_t uid = getUid();
+ if (uid == (uid_t)-1) {
+ name = android::base::StringPrintf("%7u",
+ getKey());
+ } else {
+ name = android::base::StringPrintf("%7u/%u",
+ getKey(), uid);
+ }
+ const char *nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+ "", nameTmp);
+ }
+
+ std::string size = android::base::StringPrintf("%zu",
+ getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string LogStatistics::format(uid_t uid, pid_t pid,
+ unsigned int logMask) const {
+ static const unsigned short spaces_total = 19;
+
// Report on total logging, current and for all time
- android::String8 output("size/num");
+ std::string output = "size/num";
size_t oldLength;
short spaces = 1;
log_id_for_each(id) {
- if (!(logMask & (1 << id))) {
- continue;
- }
+ if (!(logMask & (1 << id))) continue;
oldLength = output.length();
- if (spaces < 0) {
- spaces = 0;
- }
- output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ android_log_id_to_name(id));
spaces += spaces_total + oldLength - output.length();
}
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*sTotal", spaces, "");
- spaces = 4;
- output.appendFormat("\nTotal");
+ static const char TotalStr[] = "\nTotal";
+ spaces = 10 - strlen(TotalStr);
+ output += TotalStr;
+ size_t totalSize = 0;
+ size_t totalEls = 0;
log_id_for_each(id) {
- if (!(logMask & (1 << id))) {
- continue;
- }
+ if (!(logMask & (1 << id))) continue;
oldLength = output.length();
- if (spaces < 0) {
- spaces = 0;
- }
- output.appendFormat("%*s%zu/%zu", spaces, "",
- sizesTotal(id), elementsTotal(id));
+ if (spaces < 0) spaces = 0;
+ size_t szs = sizesTotal(id);
+ totalSize += szs;
+ size_t els = elementsTotal(id);
+ totalEls += els;
+ output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
spaces += spaces_total + oldLength - output.length();
}
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize, totalEls);
- spaces = 6;
- output.appendFormat("\nNow");
+ static const char NowStr[] = "\nNow";
+ spaces = 10 - strlen(NowStr);
+ output += NowStr;
+ totalSize = 0;
+ totalEls = 0;
log_id_for_each(id) {
- if (!(logMask & (1 << id))) {
- continue;
- }
+ if (!(logMask & (1 << id))) continue;
size_t els = elements(id);
if (els) {
oldLength = output.length();
- if (spaces < 0) {
- spaces = 0;
- }
- output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
+ if (spaces < 0) spaces = 0;
+ size_t szs = sizes(id);
+ totalSize += szs;
+ totalEls += els;
+ output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
spaces -= output.length() - oldLength;
}
spaces += spaces_total;
}
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize, totalEls);
+
+ static const char OverheadStr[] = "\nOverhead";
+ spaces = 10 - strlen(OverheadStr);
+ output += OverheadStr;
+
+ totalSize = 0;
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+
+ size_t els = elements(id);
+ if (els) {
+ oldLength = output.length();
+ if (spaces < 0) spaces = 0;
+ // estimate the std::list overhead.
+ static const size_t overhead =
+ ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
+ -sizeof(uint64_t)) +
+ sizeof(std::list<LogBufferElement*>);
+ size_t szs = sizes(id) + els * overhead;
+ totalSize += szs;
+ output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
+ spaces -= output.length() - oldLength;
+ }
+ spaces += spaces_total;
+ }
+ totalSize += sizeOf();
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%zu", spaces, "", totalSize);
// Report on Chattiest
+ std::string name;
+
// Chattiest by application (UID)
- static const size_t maximum_sorted_entries = 32;
log_id_for_each(id) {
- if (!(logMask & (1 << id))) {
- continue;
- }
+ if (!(logMask & (1 << id))) continue;
- bool headerPrinted = false;
- std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
- ssize_t index = -1;
- while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
- const UidEntry *entry = sorted[index];
- uid_t u = entry->getKey();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- if (!headerPrinted) {
- output.appendFormat("\n\n");
- android::String8 name("");
- if (uid == AID_ROOT) {
- name.appendFormat(
- "Chattiest UIDs in %s log buffer:",
- android_log_id_to_name(id));
- } else {
- name.appendFormat(
- "Logging for your UID in %s log buffer:",
- android_log_id_to_name(id));
- }
- android::String8 size("Size");
- android::String8 pruned("Pruned");
- if (!worstUidEnabledForLogid(id)) {
- pruned.setTo("");
- }
- format_line(output, name, size, pruned);
-
- name.setTo("UID PACKAGE");
- size.setTo("BYTES");
- pruned.setTo("LINES");
- if (!worstUidEnabledForLogid(id)) {
- pruned.setTo("");
- }
- format_line(output, name, size, pruned);
-
- headerPrinted = true;
- }
-
- android::String8 name("");
- name.appendFormat("%u", u);
- char *n = uidToName(u);
- if (n) {
- name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
- free(n);
- }
-
- android::String8 size("");
- size.appendFormat("%zu", entry->getSizes());
-
- android::String8 pruned("");
- size_t dropped = entry->getDropped();
- if (dropped) {
- pruned.appendFormat("%zu", dropped);
- }
-
- format_line(output, name, size, pruned);
- }
+ name = (uid == AID_ROOT)
+ ? "Chattiest UIDs in %s log buffer:"
+ : "Logging for your UID in %s log buffer:";
+ output += uidTable[id].format(*this, uid, pid, name, id);
}
if (enable) {
- // Pid table
- bool headerPrinted = false;
- std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
- ssize_t index = -1;
- while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
- const PidEntry *entry = sorted[index];
- uid_t u = entry->getUid();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- if (!headerPrinted) {
- output.appendFormat("\n\n");
- android::String8 name("");
- if (uid == AID_ROOT) {
- name.appendFormat("Chattiest PIDs:");
- } else {
- name.appendFormat("Logging for this PID:");
- }
- android::String8 size("Size");
- android::String8 pruned("Pruned");
- format_line(output, name, size, pruned);
-
- name.setTo(" PID/UID COMMAND LINE");
- size.setTo("BYTES");
- pruned.setTo("LINES");
- format_line(output, name, size, pruned);
-
- headerPrinted = true;
- }
-
- android::String8 name("");
- name.appendFormat("%5u/%u", entry->getKey(), u);
- const char *n = entry->getName();
- if (n) {
- name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
- } else {
- char *un = uidToName(u);
- if (un) {
- name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
- free(un);
- }
- }
-
- android::String8 size("");
- size.appendFormat("%zu", entry->getSizes());
-
- android::String8 pruned("");
- size_t dropped = entry->getDropped();
- if (dropped) {
- pruned.appendFormat("%zu", dropped);
- }
-
- format_line(output, name, size, pruned);
- }
- }
-
- if (enable) {
- // Tid table
- bool headerPrinted = false;
- // sort() returns list of references, unique_ptr makes sure self-delete
- std::unique_ptr<const TidEntry *[]> sorted = tidTable.sort(maximum_sorted_entries);
- ssize_t index = -1;
- while ((index = tidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
- const TidEntry *entry = sorted[index];
- uid_t u = entry->getUid();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- if (!headerPrinted) { // Only print header if we have table to print
- output.appendFormat("\n\n");
- android::String8 name("Chattiest TIDs:");
- android::String8 size("Size");
- android::String8 pruned("Pruned");
- format_line(output, name, size, pruned);
-
- name.setTo(" TID/UID COMM");
- size.setTo("BYTES");
- pruned.setTo("LINES");
- format_line(output, name, size, pruned);
-
- headerPrinted = true;
- }
-
- android::String8 name("");
- name.appendFormat("%5u/%u", entry->getKey(), u);
- const char *n = entry->getName();
- if (n) {
- name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
- } else {
- // if we do not have a PID name, lets punt to try UID name?
- char *un = uidToName(u);
- if (un) {
- name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
- free(un);
- }
- // We tried, better to not have a name at all, we still
- // have TID/UID by number to report in any case.
- }
-
- android::String8 size("");
- size.appendFormat("%zu", entry->getSizes());
-
- android::String8 pruned("");
- size_t dropped = entry->getDropped();
- if (dropped) {
- pruned.appendFormat("%zu", dropped);
- }
-
- format_line(output, name, size, pruned);
- }
+ name = ((uid == AID_ROOT) && !pid)
+ ? "Chattiest PIDs:"
+ : "Logging for this PID:";
+ output += pidTable.format(*this, uid, pid, name);
+ name = "Chattiest TIDs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output += tidTable.format(*this, uid, pid, name);
}
if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
- // Tag table
- bool headerPrinted = false;
- std::unique_ptr<const TagEntry *[]> sorted = tagTable.sort(maximum_sorted_entries);
- ssize_t index = -1;
- while ((index = tagTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
- const TagEntry *entry = sorted[index];
- uid_t u = entry->getUid();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- android::String8 pruned("");
-
- if (!headerPrinted) {
- output.appendFormat("\n\n");
- android::String8 name("Chattiest events log buffer TAGs:");
- android::String8 size("Size");
- format_line(output, name, size, pruned);
-
- name.setTo(" TAG/UID TAGNAME");
- size.setTo("BYTES");
- format_line(output, name, size, pruned);
-
- headerPrinted = true;
- }
-
- android::String8 name("");
- if (u == (uid_t)-1) {
- name.appendFormat("%7u", entry->getKey());
- } else {
- name.appendFormat("%7u/%u", entry->getKey(), u);
- }
- const char *n = entry->getName();
- if (n) {
- name.appendFormat("%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", n);
- }
-
- android::String8 size("");
- size.appendFormat("%zu", entry->getSizes());
-
- format_line(output, name, size, pruned);
- }
+ name = "Chattiest events log buffer TAGs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
}
- *buf = strdup(output.string());
+ if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
+ name = "Chattiest security log buffer TAGs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output += securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
+ }
+
+ return output;
}
namespace android {
@@ -523,8 +632,10 @@
}
// caller must free character string
-char *LogStatistics::pidToName(pid_t pid) {
- const char *name = pidTable.add(pid)->second.getName();
+const char *LogStatistics::pidToName(pid_t pid) const {
+ // An inconvenient truth ... getName() can alter the object
+ pidTable_t &writablePidTable = const_cast<pidTable_t &>(pidTable);
+ const char *name = writablePidTable.add(pid)->second.getName();
if (!name) {
return NULL;
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 760d6b2..777dc33 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -17,71 +17,105 @@
#ifndef _LOGD_LOG_STATISTICS_H__
#define _LOGD_LOG_STATISTICS_H__
-#include <memory>
+#include <ctype.h>
#include <stdlib.h>
#include <sys/types.h>
+#include <algorithm> // std::max
+#include <memory>
+#include <string> // std::string
#include <unordered_map>
-#include <log/log.h>
+#include <android/log.h>
+#include <android-base/stringprintf.h>
+#include <private/android_filesystem_config.h>
#include "LogBufferElement.h"
+#include "LogUtils.h"
#define log_id_for_each(i) \
- for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
+ for (log_id_t i = LOG_ID_MIN; (i) < LOG_ID_MAX; (i) = (log_id_t) ((i) + 1))
+
+class LogStatistics;
template <typename TKey, typename TEntry>
class LogHashtable {
std::unordered_map<TKey, TEntry> map;
+ size_t bucket_size() const {
+ size_t count = 0;
+ for (size_t idx = 0; idx < map.bucket_count(); ++idx) {
+ size_t bucket_size = map.bucket_size(idx);
+ if (bucket_size == 0) bucket_size = 1;
+ count += bucket_size;
+ }
+ float load_factor = map.max_load_factor();
+ if (load_factor < 1.0) return count;
+ return count * load_factor;
+ }
+
+ static const size_t unordered_map_per_entry_overhead = sizeof(void*);
+ static const size_t unordered_map_bucket_overhead = sizeof(void*);
+
public:
- typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
+ size_t size() const { return map.size(); }
- std::unique_ptr<const TEntry *[]> sort(size_t n) {
- if (!n) {
+ // Estimate unordered_map memory usage.
+ size_t sizeOf() const {
+ return sizeof(*this) +
+ (size() * (sizeof(TEntry) + unordered_map_per_entry_overhead)) +
+ (bucket_size() * sizeof(size_t) + unordered_map_bucket_overhead);
+ }
+
+ typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
+ typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
+
+ std::unique_ptr<const TEntry *[]> sort(uid_t uid, pid_t pid,
+ size_t len) const {
+ if (!len) {
std::unique_ptr<const TEntry *[]> sorted(NULL);
return sorted;
}
- const TEntry **retval = new const TEntry* [n];
- memset(retval, 0, sizeof(*retval) * n);
+ const TEntry **retval = new const TEntry* [len];
+ memset(retval, 0, sizeof(*retval) * len);
- for(iterator it = map.begin(); it != map.end(); ++it) {
+ for(const_iterator it = map.begin(); it != map.end(); ++it) {
const TEntry &entry = it->second;
- size_t s = entry.getSizes();
- ssize_t i = n - 1;
- while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0))
+
+ if ((uid != AID_ROOT) && (uid != entry.getUid())) {
+ continue;
+ }
+ if (pid && entry.getPid() && (pid != entry.getPid())) {
+ continue;
+ }
+
+ size_t sizes = entry.getSizes();
+ ssize_t index = len - 1;
+ while ((!retval[index] || (sizes > retval[index]->getSizes()))
+ && (--index >= 0))
;
- if (++i < (ssize_t)n) {
- size_t b = n - i - 1;
- if (b) {
- memmove(&retval[i+1], &retval[i], b * sizeof(retval[0]));
+ if (++index < (ssize_t)len) {
+ size_t num = len - index - 1;
+ if (num) {
+ memmove(&retval[index + 1], &retval[index],
+ num * sizeof(retval[0]));
}
- retval[i] = &entry;
+ retval[index] = &entry;
}
}
std::unique_ptr<const TEntry *[]> sorted(retval);
return sorted;
}
- // Iteration handler for the sort method output
- static ssize_t next(ssize_t index, std::unique_ptr<const TEntry *[]> &sorted, size_t n) {
- ++index;
- if (!sorted.get() || (index < 0) || (n <= (size_t)index) || !sorted[index]
- || (sorted[index]->getSizes() <= (sorted[0]->getSizes() / 100))) {
- return -1;
- }
- return index;
- }
-
- inline iterator add(TKey key, LogBufferElement *e) {
+ inline iterator add(TKey key, LogBufferElement *element) {
iterator it = map.find(key);
if (it == map.end()) {
- it = map.insert(std::make_pair(key, TEntry(e))).first;
+ it = map.insert(std::make_pair(key, TEntry(element))).first;
} else {
- it->second.add(e);
+ it->second.add(element);
}
return it;
}
@@ -96,65 +130,149 @@
return it;
}
- void subtract(TKey key, LogBufferElement *e) {
+ void subtract(TKey key, LogBufferElement *element) {
iterator it = map.find(key);
- if ((it != map.end()) && it->second.subtract(e)) {
+ if ((it != map.end()) && it->second.subtract(element)) {
map.erase(it);
}
}
- inline void drop(TKey key, LogBufferElement *e) {
+ inline void drop(TKey key, LogBufferElement *element) {
iterator it = map.find(key);
if (it != map.end()) {
- it->second.drop(e);
+ it->second.drop(element);
}
}
inline iterator begin() { return map.begin(); }
+ inline const_iterator begin() const { return map.begin(); }
inline iterator end() { return map.end(); }
+ inline const_iterator end() const { return map.end(); }
+
+ std::string format(
+ const LogStatistics &stat,
+ uid_t uid,
+ pid_t pid,
+ const std::string &name = std::string(""),
+ log_id_t id = LOG_ID_MAX) const {
+ static const size_t maximum_sorted_entries = 32;
+ std::string output;
+ std::unique_ptr<const TEntry *[]> sorted = sort(uid, pid,
+ maximum_sorted_entries);
+ if (!sorted.get()) {
+ return output;
+ }
+ bool headerPrinted = false;
+ for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+ const TEntry *entry = sorted[index];
+ if (!entry) {
+ break;
+ }
+ if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
+ break;
+ }
+ if (!headerPrinted) {
+ output += "\n\n";
+ output += entry->formatHeader(name, id);
+ headerPrinted = true;
+ }
+ output += entry->format(stat, id);
+ }
+ return output;
+ }
};
+namespace EntryBaseConstants {
+ static constexpr size_t pruned_len = 14;
+ static constexpr size_t total_len = 80;
+}
+
struct EntryBase {
size_t size;
EntryBase():size(0) { }
- EntryBase(LogBufferElement *e):size(e->getMsgLen()) { }
+ explicit EntryBase(LogBufferElement *element):size(element->getMsgLen()) { }
size_t getSizes() const { return size; }
- inline void add(LogBufferElement *e) { size += e->getMsgLen(); }
- inline bool subtract(LogBufferElement *e) { size -= e->getMsgLen(); return !size; }
+ inline void add(LogBufferElement *element) { size += element->getMsgLen(); }
+ inline bool subtract(LogBufferElement *element) {
+ size -= element->getMsgLen();
+ return !size;
+ }
+
+ static std::string formatLine(
+ const std::string &name,
+ const std::string &size,
+ const std::string &pruned) {
+ ssize_t drop_len = std::max(pruned.length() + 1,
+ EntryBaseConstants::pruned_len);
+ ssize_t size_len = std::max(size.length() + 1,
+ EntryBaseConstants::total_len
+ - name.length() - drop_len - 1);
+
+ std::string ret = android::base::StringPrintf("%s%*s%*s",
+ name.c_str(),
+ (int)size_len, size.c_str(),
+ (int)drop_len, pruned.c_str());
+ // remove any trailing spaces
+ size_t pos = ret.size();
+ size_t len = 0;
+ while (pos && isspace(ret[--pos])) ++len;
+ if (len) ret.erase(pos + 1, len);
+ return ret + "\n";
+ }
};
struct EntryBaseDropped : public EntryBase {
size_t dropped;
EntryBaseDropped():dropped(0) { }
- EntryBaseDropped(LogBufferElement *e):EntryBase(e),dropped(e->getDropped()){ }
+ explicit EntryBaseDropped(LogBufferElement *element):
+ EntryBase(element),
+ dropped(element->getDropped()) {
+ }
size_t getDropped() const { return dropped; }
- inline void add(LogBufferElement *e) {
- dropped += e->getDropped();
- EntryBase::add(e);
+ inline void add(LogBufferElement *element) {
+ dropped += element->getDropped();
+ EntryBase::add(element);
}
- inline bool subtract(LogBufferElement *e) {
- dropped -= e->getDropped();
- return EntryBase::subtract(e) && !dropped;
+ inline bool subtract(LogBufferElement *element) {
+ dropped -= element->getDropped();
+ return EntryBase::subtract(element) && !dropped;
}
- inline void drop(LogBufferElement *e) {
+ inline void drop(LogBufferElement *element) {
dropped += 1;
- EntryBase::subtract(e);
+ EntryBase::subtract(element);
}
};
struct UidEntry : public EntryBaseDropped {
const uid_t uid;
+ pid_t pid;
- UidEntry(LogBufferElement *e):EntryBaseDropped(e),uid(e->getUid()) { }
+ explicit UidEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ uid(element->getUid()),
+ pid(element->getPid()) {
+ }
inline const uid_t&getKey() const { return uid; }
+ inline const uid_t&getUid() const { return getKey(); }
+ inline const pid_t&getPid() const { return pid; }
+
+ inline void add(LogBufferElement *element) {
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
+ EntryBaseDropped::add(element);
+ }
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
namespace android {
@@ -166,139 +284,208 @@
uid_t uid;
char *name;
- PidEntry(pid_t p):
- EntryBaseDropped(),
- pid(p),
- uid(android::pidToUid(p)),
- name(android::pidToName(pid)) { }
- PidEntry(LogBufferElement *e):
- EntryBaseDropped(e),
- pid(e->getPid()),
- uid(e->getUid()),
- name(android::pidToName(e->getPid())) { }
- PidEntry(const PidEntry &c):
- EntryBaseDropped(c),
- pid(c.pid),
- uid(c.uid),
- name(c.name ? strdup(c.name) : NULL) { }
+ explicit PidEntry(pid_t pid):
+ EntryBaseDropped(),
+ pid(pid),
+ uid(android::pidToUid(pid)),
+ name(android::pidToName(pid)) {
+ }
+ explicit PidEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(android::pidToName(pid)) {
+ }
+ PidEntry(const PidEntry &element):
+ EntryBaseDropped(element),
+ pid(element.pid),
+ uid(element.uid),
+ name(element.name ? strdup(element.name) : NULL) {
+ }
~PidEntry() { free(name); }
const pid_t&getKey() const { return pid; }
+ const pid_t&getPid() const { return getKey(); }
const uid_t&getUid() const { return uid; }
const char*getName() const { return name; }
- inline void add(pid_t p) {
- if (name && !strncmp(name, "zygote", 6)) {
+ inline void add(pid_t newPid) {
+ if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
free(name);
name = NULL;
}
if (!name) {
- char *n = android::pidToName(p);
- if (n) {
- name = n;
- }
+ name = android::pidToName(newPid);
}
}
- inline void add(LogBufferElement *e) {
- uid_t u = e->getUid();
- if (getUid() != u) {
- uid = u;
+ inline void add(LogBufferElement *element) {
+ uid_t incomingUid = element->getUid();
+ if (getUid() != incomingUid) {
+ uid = incomingUid;
free(name);
- name = android::pidToName(e->getPid());
+ name = android::pidToName(element->getPid());
} else {
- add(e->getPid());
+ add(element->getPid());
}
- EntryBaseDropped::add(e);
+ EntryBaseDropped::add(element);
}
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
struct TidEntry : public EntryBaseDropped {
const pid_t tid;
+ pid_t pid;
uid_t uid;
char *name;
- TidEntry(pid_t t):
- EntryBaseDropped(),
- tid(t),
- uid(android::pidToUid(t)),
- name(android::tidToName(tid)) { }
- TidEntry(LogBufferElement *e):
- EntryBaseDropped(e),
- tid(e->getTid()),
- uid(e->getUid()),
- name(android::tidToName(e->getTid())) { }
- TidEntry(const TidEntry &c):
- EntryBaseDropped(c),
- tid(c.tid),
- uid(c.uid),
- name(c.name ? strdup(c.name) : NULL) { }
+ TidEntry(pid_t tid, pid_t pid):
+ EntryBaseDropped(),
+ tid(tid),
+ pid(pid),
+ uid(android::pidToUid(tid)),
+ name(android::tidToName(tid)) {
+ }
+ explicit TidEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ tid(element->getTid()),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(android::tidToName(tid)) {
+ }
+ TidEntry(const TidEntry &element):
+ EntryBaseDropped(element),
+ tid(element.tid),
+ pid(element.pid),
+ uid(element.uid),
+ name(element.name ? strdup(element.name) : NULL) {
+ }
~TidEntry() { free(name); }
const pid_t&getKey() const { return tid; }
+ const pid_t&getTid() const { return getKey(); }
+ const pid_t&getPid() const { return pid; }
const uid_t&getUid() const { return uid; }
const char*getName() const { return name; }
- inline void add(pid_t t) {
- if (name && !strncmp(name, "zygote", 6)) {
+ inline void add(pid_t incomingTid) {
+ if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
free(name);
name = NULL;
}
if (!name) {
- char *n = android::tidToName(t);
- if (n) {
- name = n;
+ name = android::tidToName(incomingTid);
+ }
+ }
+
+ inline void add(LogBufferElement *element) {
+ uid_t incomingUid = element->getUid();
+ pid_t incomingPid = element->getPid();
+ if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
+ uid = incomingUid;
+ pid = incomingPid;
+ free(name);
+ name = android::tidToName(element->getTid());
+ } else {
+ add(element->getTid());
+ }
+ EntryBaseDropped::add(element);
+ }
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
+};
+
+struct TagEntry : public EntryBaseDropped {
+ const uint32_t tag;
+ pid_t pid;
+ uid_t uid;
+
+ explicit TagEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ tag(element->getTag()),
+ pid(element->getPid()),
+ uid(element->getUid()) {
+ }
+
+ const uint32_t&getKey() const { return tag; }
+ const pid_t&getPid() const { return pid; }
+ const uid_t&getUid() const { return uid; }
+ const char*getName() const { return android::tagToName(tag); }
+
+ inline void add(LogBufferElement *element) {
+ if (uid != element->getUid()) {
+ uid = -1;
+ }
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
+ EntryBaseDropped::add(element);
+ }
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
+};
+
+template <typename TEntry>
+class LogFindWorst {
+ std::unique_ptr<const TEntry *[]> sorted;
+
+public:
+
+ explicit LogFindWorst(std::unique_ptr<const TEntry *[]> &&sorted) : sorted(std::move(sorted)) { }
+
+ void findWorst(int &worst,
+ size_t &worst_sizes, size_t &second_worst_sizes,
+ size_t threshold) {
+ if (sorted.get() && sorted[0] && sorted[1]) {
+ worst_sizes = sorted[0]->getSizes();
+ if ((worst_sizes > threshold)
+ // Allow time horizon to extend roughly tenfold, assume
+ // average entry length is 100 characters.
+ && (worst_sizes > (10 * sorted[0]->getDropped()))) {
+ worst = sorted[0]->getKey();
+ second_worst_sizes = sorted[1]->getSizes();
+ if (second_worst_sizes < threshold) {
+ second_worst_sizes = threshold;
+ }
}
}
}
- inline void add(LogBufferElement *e) {
- uid_t u = e->getUid();
- if (getUid() != u) {
- uid = u;
- free(name);
- name = android::tidToName(e->getTid());
- } else {
- add(e->getTid());
+ void findWorst(int &worst,
+ size_t worst_sizes, size_t &second_worst_sizes) {
+ if (sorted.get() && sorted[0] && sorted[1]) {
+ worst = sorted[0]->getKey();
+ second_worst_sizes = worst_sizes
+ - sorted[0]->getSizes()
+ + sorted[1]->getSizes();
}
- EntryBaseDropped::add(e);
- }
-};
-
-struct TagEntry : public EntryBase {
- const uint32_t tag;
- uid_t uid;
-
- TagEntry(LogBufferElement *e):
- EntryBase(e),
- tag(e->getTag()),
- uid(e->getUid()) { }
-
- const uint32_t&getKey() const { return tag; }
- const uid_t&getUid() const { return uid; }
- const char*getName() const { return android::tagToName(tag); }
-
- inline void add(LogBufferElement *e) {
- uid_t u = e->getUid();
- if (uid != u) {
- uid = -1;
- }
- EntryBase::add(e);
}
};
// Log Statistics
class LogStatistics {
+ friend UidEntry;
+
size_t mSizes[LOG_ID_MAX];
size_t mElements[LOG_ID_MAX];
+ size_t mDroppedElements[LOG_ID_MAX];
size_t mSizesTotal[LOG_ID_MAX];
size_t mElementsTotal[LOG_ID_MAX];
+ static size_t SizesTotal;
bool enable;
// uid to size list
typedef LogHashtable<uid_t, UidEntry> uidTable_t;
uidTable_t uidTable[LOG_ID_MAX];
+ // pid of system to size list
+ typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
+ pidSystemTable_t pidSystemTable[LOG_ID_MAX];
+
// pid to uid list
typedef LogHashtable<pid_t, PidEntry> pidTable_t;
pidTable_t pidTable;
@@ -311,7 +498,33 @@
typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
tagTable_t tagTable;
+ // security tag list
+ tagTable_t securityTagTable;
+
+ size_t sizeOf() const {
+ size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
+ tagTable.sizeOf() + securityTagTable.sizeOf() +
+ (pidTable.size() * sizeof(pidTable_t::iterator)) +
+ (tagTable.size() * sizeof(tagTable_t::iterator));
+ for(auto it : pidTable) {
+ const char* name = it.second.getName();
+ if (name) size += strlen(name) + 1;
+ }
+ for(auto it : tidTable) {
+ const char* name = it.second.getName();
+ if (name) size += strlen(name) + 1;
+ }
+ log_id_for_each(id) {
+ size += uidTable[id].sizeOf();
+ size += uidTable[id].size() * sizeof(uidTable_t::iterator);
+ size += pidSystemTable[id].sizeOf();
+ size += pidSystemTable[id].size() * sizeof(pidSystemTable_t::iterator);
+ }
+ return size;
+ }
+
public:
+
LogStatistics();
void enableStatistics() { enable = true; }
@@ -320,24 +533,39 @@
void subtract(LogBufferElement *entry);
// entry->setDropped(1) must follow this call
void drop(LogBufferElement *entry);
- // Correct for merging two entries referencing dropped content
- void erase(LogBufferElement *e) { --mElements[e->getLogId()]; }
+ // Correct for coalescing two entries referencing dropped content
+ void erase(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ --mElements[log_id];
+ --mDroppedElements[log_id];
+ }
- std::unique_ptr<const UidEntry *[]> sort(size_t n, log_id i) { return uidTable[i].sort(n); }
+ LogFindWorst<UidEntry> sort(uid_t uid, pid_t pid, size_t len, log_id id) {
+ return LogFindWorst<UidEntry>(uidTable[id].sort(uid, pid, len));
+ }
+ LogFindWorst<PidEntry> sortPids(uid_t uid, pid_t pid, size_t len, log_id id) {
+ return LogFindWorst<PidEntry>(pidSystemTable[id].sort(uid, pid, len));
+ }
+ LogFindWorst<TagEntry> sortTags(uid_t uid, pid_t pid, size_t len, log_id) {
+ return LogFindWorst<TagEntry>(tagTable.sort(uid, pid, len));
+ }
// fast track current value by id only
size_t sizes(log_id_t id) const { return mSizes[id]; }
size_t elements(log_id_t id) const { return mElements[id]; }
+ size_t realElements(log_id_t id) const {
+ return mElements[id] - mDroppedElements[id];
+ }
size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
+ static size_t sizesTotal() { return SizesTotal; }
- // *strp = malloc, balance with free
- void format(char **strp, uid_t uid, unsigned int logMask);
+ std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
// helper (must be locked directly or implicitly by mLogElementsLock)
- char *pidToName(pid_t pid);
+ const char *pidToName(pid_t pid) const;
uid_t pidToUid(pid_t pid);
- char *uidToName(uid_t uid);
+ const char *uidToName(uid_t uid) const;
};
#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
new file mode 100644
index 0000000..a109592
--- /dev/null
+++ b/logd/LogTags.cpp
@@ -0,0 +1,884 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "LogTags.h"
+#include "LogUtils.h"
+
+static LogTags* logtags;
+
+const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags";
+const char LogTags::dynamic_event_log_tags[] = "/dev/event-log-tags";
+// Only for debug
+const char LogTags::debug_event_log_tags[] = "/data/misc/logd/event-log-tags";
+
+// Sniff for first uid=%d in utf8z comment string
+static uid_t sniffUid(const char* comment, const char* endp) {
+ if (!comment) return AID_ROOT;
+
+ if (*comment == '#') ++comment;
+ while ((comment < endp) && (*comment != '\n') && isspace(*comment)) ++comment;
+ static const char uid_str[] = "uid=";
+ if (((comment + strlen(uid_str)) >= endp) ||
+ fastcmp<strncmp>(comment, uid_str, strlen(uid_str)) ||
+ !isdigit(comment[strlen(uid_str)])) return AID_ROOT;
+ char* cp;
+ unsigned long Uid = strtoul(comment + 4, &cp, 10);
+ if ((cp > endp) || (Uid >= INT_MAX)) return AID_ROOT;
+
+ return Uid;
+}
+
+// Checks for file corruption, and report false if there was no need
+// to rebuild the referenced file. Failure to rebuild is only logged,
+// does not cause a return value of false.
+bool LogTags::RebuildFileEventLogTags(const char* filename, bool warn) {
+ int fd;
+
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ if (tag2total.begin() == tag2total.end()) {
+ return false;
+ }
+
+ file2watermark_const_iterator iwater = file2watermark.find(filename);
+ if (iwater == file2watermark.end()) {
+ return false;
+ }
+
+ struct stat sb;
+ if (!stat(filename, &sb) && ((size_t)sb.st_size >= iwater->second)) {
+ return false;
+ }
+
+ // dump what we already know back into the file?
+ fd = TEMP_FAILURE_RETRY(open(filename,
+ O_WRONLY | O_TRUNC | O_CLOEXEC |
+ O_NOFOLLOW | O_BINARY));
+ if (fd >= 0) {
+ time_t now = time(NULL);
+ struct tm tm;
+ localtime_r(&now, &tm);
+ char timebuf[20];
+ size_t len = strftime(timebuf, sizeof(timebuf),
+ "%Y-%m-%d %H:%M:%S", &tm);
+ android::base::WriteStringToFd(
+ android::base::StringPrintf(
+ "# Rebuilt %.20s, content owned by logd\n", timebuf),
+ fd);
+ for (const auto& it : tag2total) {
+ android::base::WriteStringToFd(formatEntry_locked(it.first,
+ AID_ROOT),
+ fd);
+ }
+ TEMP_FAILURE_RETRY(close(fd));
+ }
+ }
+
+ if (warn) {
+ android::prdebug(((fd < 0) ?
+ "%s failed to rebuild" :
+ "%s missing, damaged or truncated; rebuilt"),
+ filename);
+ }
+
+ if (fd >= 0) {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ struct stat sb;
+ if (!stat(filename, &sb)) file2watermark[filename] = sb.st_size;
+ }
+
+ return true;
+}
+
+void LogTags::AddEventLogTags(uint32_t tag, uid_t uid,
+ const std::string& Name,
+ const std::string& Format,
+ const char* source, bool warn) {
+ std::string Key = Name;
+ if (Format.length()) Key += "+" + Format;
+
+ bool update = !source || !!strcmp(source, system_event_log_tags);
+ bool newOne;
+
+ {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+
+ // unlikely except for dupes, or updates to uid list (more later)
+ if (itot != tag2total.end()) update = false;
+
+ newOne = tag2name.find(tag) == tag2name.end();
+ key2tag[Key] = tag;
+
+ if (Format.length()) {
+ if (key2tag.find(Name) == key2tag.end()) {
+ key2tag[Name] = tag;
+ }
+ tag2format[tag] = Format;
+ }
+ tag2name[tag] = Name;
+
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut != tag2uid.end()) {
+ if (uid == AID_ROOT) {
+ tag2uid.erase(ut);
+ update = true;
+ } else if (ut->second.find(uid) == ut->second.end()) {
+ const_cast<uid_list&>(ut->second).emplace(uid);
+ update = true;
+ }
+ } else if (newOne && (uid != AID_ROOT)) {
+ tag2uid[tag].emplace(uid);
+ update = true;
+ }
+
+ // updatePersist -> trigger output on modified
+ // content, reset tag2total if available
+ if (update && (itot != tag2total.end())) tag2total[tag] = 0;
+ }
+
+ if (update) {
+ WritePersistEventLogTags(tag, uid, source);
+ } else if (warn && !newOne && source) {
+ // For the files, we want to report dupes.
+ android::prdebug("Multiple tag %" PRIu32 " %s %s %s", tag,
+ Name.c_str(), Format.c_str(), source);
+ }
+}
+
+// Read the event log tags file, and build up our internal database
+void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
+ bool etc = !strcmp(filename, system_event_log_tags);
+ bool debug = !etc && !strcmp(filename, debug_event_log_tags);
+
+ if (!etc) {
+ RebuildFileEventLogTags(filename, warn);
+ }
+ std::string content;
+ if (android::base::ReadFileToString(filename, &content)) {
+ char* cp = (char*) content.c_str();
+ char* endp = cp + content.length();
+
+ {
+ android::RWLock::AutoRLock writeLock(rwlock);
+
+ file2watermark[filename] = content.length();
+ }
+
+ char* lineStart = cp;
+ while (cp < endp) {
+ if (*cp == '\n') {
+ lineStart = cp;
+ } else if (lineStart) {
+ if (*cp == '#') {
+ /* comment; just scan to end */
+ lineStart = NULL;
+ } else if (isdigit(*cp)) {
+ unsigned long Tag = strtoul(cp, &cp, 10);
+ if (warn && (Tag > emptyTag)) {
+ android::prdebug("tag too large %lu", Tag);
+ }
+ while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+ if (cp >= endp) break;
+ if (*cp == '\n') continue;
+ const char* name = cp;
+ /* Determine whether it is a valid tag name [a-zA-Z0-9_] */
+ bool hasAlpha = false;
+ while ((cp < endp) && (isalnum(*cp) || (*cp == '_'))) {
+ if (!isdigit(*cp)) hasAlpha = true;
+ ++cp;
+ }
+ std::string Name(name, cp - name);
+#ifdef ALLOW_NOISY_LOGGING_OF_PROBLEM_WITH_LOTS_OF_TECHNICAL_DEBT
+ static const size_t maximum_official_tag_name_size = 24;
+ if (warn && (Name.length() > maximum_official_tag_name_size)) {
+ android::prdebug("tag name too long %s", Name.c_str());
+ }
+#endif
+ if (hasAlpha && ((cp >= endp) || (*cp == '#') || isspace(*cp))) {
+ if (Tag > emptyTag) {
+ if (*cp != '\n') lineStart = NULL;
+ continue;
+ }
+ while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+ const char* format = cp;
+ uid_t uid = AID_ROOT;
+ while ((cp < endp) && (*cp != '\n')) {
+ if (*cp == '#') {
+ uid = sniffUid(cp, endp);
+ lineStart = NULL;
+ break;
+ }
+ ++cp;
+ }
+ while ((cp > format) && isspace(cp[-1])) {
+ --cp;
+ lineStart = NULL;
+ }
+ std::string Format(format, cp - format);
+
+ AddEventLogTags((uint32_t)Tag, uid, Name, Format,
+ filename, warn);
+ } else {
+ if (warn) {
+ android::prdebug("tag name invalid %.*s",
+ (int)(cp - name + 1), name);
+ }
+ lineStart = NULL;
+ }
+ } else if (!isspace(*cp)) break;
+ }
+ cp++;
+ }
+ } else if (warn) {
+ android::prdebug("Cannot read %s", filename);
+ }
+}
+
+// Extract a 4-byte value from a byte stream.
+static inline uint32_t get4LE(const char* msg)
+{
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(msg);
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+// Additional persistent sources for invented log tags. Read the
+// special pmsg event for log tags, and build up our internal
+// database with any found.
+void LogTags::ReadPersistEventLogTags() {
+ struct logger_list* logger_list = android_logger_list_alloc(
+ ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK,
+ 0, (pid_t)0);
+ if (!logger_list) return;
+
+ struct logger* e = android_logger_open(logger_list, LOG_ID_EVENTS);
+ struct logger* s = android_logger_open(logger_list, LOG_ID_SECURITY);
+ if (!e && !s) {
+ android_logger_list_free(logger_list);
+ return;
+ }
+
+ for (;;) {
+ struct log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ if (ret <= 0) break;
+
+ const char* msg = log_msg.msg();
+ if (!msg) continue;
+ if (log_msg.entry.len <= sizeof(uint32_t)) continue;
+ uint32_t Tag = get4LE(msg);
+ if (Tag != TAG_DEF_LOG_TAG) continue;
+ uid_t uid = (log_msg.entry.hdr_size >= sizeof(logger_entry_v4)) ?
+ log_msg.entry.uid : AID_ROOT;
+
+ std::string Name;
+ std::string Format;
+ android_log_list_element elem;
+ {
+ android_log_event_list ctx(log_msg);
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_LIST) {
+ continue;
+ }
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_INT) {
+ continue;
+ }
+ Tag = elem.data.int32;
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Name = std::string(elem.data.string, elem.len);
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Format = std::string(elem.data.string, elem.len);
+ elem = ctx.read();
+ }
+ if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue;
+
+ AddEventLogTags(Tag, uid, Name, Format);
+ }
+ android_logger_list_free(logger_list);
+}
+
+LogTags::LogTags() {
+ ReadFileEventLogTags(system_event_log_tags);
+ // Following will likely fail on boot, but is required if logd restarts
+ ReadFileEventLogTags(dynamic_event_log_tags, false);
+ if (__android_log_is_debuggable()) {
+ ReadFileEventLogTags(debug_event_log_tags, false);
+ }
+ ReadPersistEventLogTags();
+
+ logtags = this;
+}
+
+// Converts an event tag into a name
+const char* LogTags::tagToName(uint32_t tag) const {
+ tag2name_const_iterator it;
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ it = tag2name.find(tag);
+ if ((it == tag2name.end()) || (it->second.length() == 0)) return NULL;
+
+ return it->second.c_str();
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This must be a pure reader to our database, as everything else is
+// guaranteed single-threaded except this access point which is
+// asynchonous and can be multithreaded and thus rentrant. The
+// object's rwlock is only used to guarantee atomic access to the
+// unordered_map to prevent corruption, with a requirement to be a
+// low chance of contention for this call. If we end up changing
+// this algorithm resulting in write, then we should use a different
+// lock than the object's rwlock to protect groups of associated
+// actions.
+const char* android::tagToName(uint32_t tag) {
+ LogTags* me = logtags;
+
+ if (!me) return NULL;
+ me->WritePmsgEventLogTags(tag);
+ return me->tagToName(tag);
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This only works on userdebug and eng devices to re-read the
+// /data/misc/logd/event-log-tags file right after /data is mounted.
+// The operation is near to boot and should only happen once. There
+// are races associated with its use since it can trigger a Rebuild
+// of the file, but that is a can-not-happen since the file was not
+// read yet. More dangerous if called later, but if all is well it
+// should just skip over everything and not write any new entries.
+void android::ReReadEventLogTags() {
+ LogTags* me = logtags;
+
+ if (me && __android_log_is_debuggable()) {
+ me->ReadFileEventLogTags(me->debug_event_log_tags);
+ }
+}
+
+// converts an event tag into a format
+const char* LogTags::tagToFormat(uint32_t tag) const {
+ tag2format_const_iterator iform;
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ iform = tag2format.find(tag);
+ if (iform == tag2format.end()) return NULL;
+
+ return iform->second.c_str();
+}
+
+// converts a name into an event tag
+uint32_t LogTags::nameToTag(const char* name) const {
+ uint32_t ret = emptyTag;
+
+ // Bug: Only works for a single entry, we can have multiple entries,
+ // one for each format, so we find first entry recorded, or entry with
+ // no format associated with it.
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ key2tag_const_iterator ik = key2tag.find(std::string(name));
+ if (ik != key2tag.end()) ret = ik->second;
+
+ return ret;
+}
+
+// Caller must perform locks, can be under reader (for pre-check) or
+// writer lock. We use this call to invent a new deterministically
+// random tag, unique is cleared if no conflicts. If format is NULL,
+// we are in readonly mode.
+uint32_t LogTags::nameToTag_locked(const std::string& name,
+ const char* format,
+ bool& unique) {
+ key2tag_const_iterator ik;
+
+ bool write = format != NULL;
+ unique = write;
+
+ if (!write) {
+ // Bug: Only works for a single entry, we can have multiple entries,
+ // one for each format, so we find first entry recorded, or entry with
+ // no format associated with it.
+ ik = key2tag.find(name);
+ if (ik == key2tag.end()) return emptyTag;
+ return ik->second;
+ }
+
+ std::string Key(name);
+ if (*format) Key += std::string("+") + format;
+
+ ik = key2tag.find(Key);
+ if (ik != key2tag.end()) {
+ unique = false;
+ return ik->second;
+ }
+
+ size_t Hash = key2tag.hash_function()(Key);
+ uint32_t Tag = Hash;
+ // This sets an upper limit on the conflics we are allowed to deal with.
+ for (unsigned i = 0; i < 256; ) {
+ tag2name_const_iterator it = tag2name.find(Tag);
+ if (it == tag2name.end()) return Tag;
+ std::string localKey(it->second);
+ tag2format_const_iterator iform = tag2format.find(Tag);
+ if ((iform == tag2format.end()) && iform->second.length()) {
+ localKey += "+" + iform->second;
+ }
+ unique = !!it->second.compare(localKey);
+ if (!unique) return Tag; // unlikely except in a race
+
+ ++i;
+ // Algorithm to convert hash to next tag
+ if (i < 32) {
+ Tag = (Hash >> i);
+ // size_t is 32 bits, or upper word zero, rotate
+ if ((sizeof(Hash) <= 4) ||
+ ((Hash & (uint64_t(-1LL) << 32)) == 0)) {
+ Tag |= Hash << (32 - i);
+ }
+ } else {
+ Tag = Hash + i - 31;
+ }
+ }
+ return emptyTag;
+}
+
+static int openFile(const char* name, int mode, bool warning) {
+ int fd = TEMP_FAILURE_RETRY(open(name, mode));
+ if ((fd < 0) && warning) {
+ android::prdebug("Failed open %s (%d)", name, errno);
+ }
+ return fd;
+}
+
+void LogTags::WritePmsgEventLogTags(uint32_t tag, uid_t uid) {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+ if (itot == tag2total.end()) return; // source is a static entry
+
+ size_t lastTotal = itot->second;
+
+ // Every 16K (half the smallest configurable pmsg buffer size) record
+ static const size_t rate_to_pmsg = 16 * 1024;
+ if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+ return;
+ }
+
+ static int pmsg_fd = -1;
+ if (pmsg_fd < 0) {
+ pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ // unlikely, but deal with partners with borken pmsg
+ if (pmsg_fd < 0) return;
+ }
+
+ std::string Name = tag2name[tag];
+ tag2format_const_iterator iform = tag2format.find(tag);
+ std::string Format = (iform != tag2format.end()) ? iform->second : "";
+
+ __android_log_event_list ctx(TAG_DEF_LOG_TAG);
+ ctx << tag << Name << Format;
+ std::string buffer(ctx);
+ if (buffer.length() <= 0) return; // unlikely
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsgHeader;
+ * // what we provide to file
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ android_log_header_t header = {
+ .id = LOG_ID_EVENTS,
+ .tid = (uint16_t)gettid(),
+ .realtime.tv_sec = (uint32_t)ts.tv_sec,
+ .realtime.tv_nsec = (uint32_t)ts.tv_nsec,
+ };
+
+ uint32_t outTag = TAG_DEF_LOG_TAG;
+ outTag = get4LE((const char*)&outTag);
+
+ android_pmsg_log_header_t pmsgHeader = {
+ .magic = LOGGER_MAGIC,
+ .len = (uint16_t)(sizeof(pmsgHeader) + sizeof(header) +
+ sizeof(outTag) + buffer.length()),
+ .uid = (uint16_t)AID_ROOT,
+ .pid = (uint16_t)getpid(),
+ };
+
+ struct iovec Vec[] = {
+ { (unsigned char*)&pmsgHeader, sizeof(pmsgHeader) },
+ { (unsigned char*)&header, sizeof(header) },
+ { (unsigned char*)&outTag, sizeof(outTag) },
+ { (unsigned char*)const_cast<char*>(buffer.data()), buffer.length() }
+ };
+
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut == tag2uid.end()) {
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ } else if (uid != AID_ROOT) {
+ pmsgHeader.uid = (uint16_t)uid;
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ } else {
+ for (auto &it : ut->second) {
+ pmsgHeader.uid = (uint16_t)it;
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ }
+ }
+}
+
+void LogTags::WriteDynamicEventLogTags(uint32_t tag, uid_t uid) {
+ static const int mode = O_WRONLY | O_APPEND |
+ O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+ int fd = openFile(dynamic_event_log_tags, mode, true);
+ if (fd < 0) return;
+
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ std::string ret = formatEntry_locked(tag, uid, false);
+ android::base::WriteStringToFd(ret, fd);
+ TEMP_FAILURE_RETRY(close(fd));
+
+ size_t size = 0;
+ file2watermark_const_iterator iwater;
+
+ iwater = file2watermark.find(dynamic_event_log_tags);
+ if (iwater != file2watermark.end()) size = iwater->second;
+
+ file2watermark[dynamic_event_log_tags] = size + ret.length();
+}
+
+void LogTags::WriteDebugEventLogTags(uint32_t tag, uid_t uid) {
+ static const int mode = O_WRONLY | O_APPEND |
+ O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+ static bool one = true;
+ int fd = openFile(debug_event_log_tags, mode, one);
+ one = fd >= 0;
+ if (!one) return;
+
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ std::string ret = formatEntry_locked(tag, uid, false);
+ android::base::WriteStringToFd(ret, fd);
+ TEMP_FAILURE_RETRY(close(fd));
+
+ size_t size = 0;
+ file2watermark_const_iterator iwater;
+
+ iwater = file2watermark.find(debug_event_log_tags);
+ if (iwater != file2watermark.end()) size = iwater->second;
+
+ file2watermark[debug_event_log_tags] = size + ret.length();
+}
+
+// How we maintain some runtime or reboot stickiness
+void LogTags::WritePersistEventLogTags(uint32_t tag,
+ uid_t uid, const char* source) {
+ // very unlikely
+ bool etc = source && !strcmp(source, system_event_log_tags);
+ if (etc) return;
+
+ bool dynamic = source && !strcmp(source, dynamic_event_log_tags);
+ bool debug = (!dynamic &&
+ source &&
+ !strcmp(source, debug_event_log_tags)) ||
+ !__android_log_is_debuggable();
+
+ WritePmsgEventLogTags(tag, uid);
+
+ size_t lastTotal = 0;
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+ if (itot != tag2total.end()) lastTotal = itot->second;
+ }
+
+ if (lastTotal == 0) { // denotes first time for this one
+ if (!dynamic || !RebuildFileEventLogTags(dynamic_event_log_tags)) {
+ WriteDynamicEventLogTags(tag, uid);
+ }
+
+ if (!debug && !RebuildFileEventLogTags(debug_event_log_tags)) {
+ WriteDebugEventLogTags(tag, uid);
+ }
+ }
+
+ lastTotal = android::sizesTotal();
+ if (!lastTotal) ++lastTotal;
+
+ // record totals for next watermark.
+ android::RWLock::AutoWLock writeLock(rwlock);
+ tag2total[tag] = lastTotal;
+}
+
+// nameToTag converts a name into an event tag. If format is NULL, then we
+// are in readonly mode.
+uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) {
+ std::string Name = std::string(name);
+ bool write = format != NULL;
+ bool updateUid = uid != AID_ROOT;
+ bool updateFormat = format && *format;
+ bool unique;
+ uint32_t Tag;
+
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ Tag = nameToTag_locked(Name, format, unique);
+ if (updateUid && (Tag != emptyTag) && !unique) {
+ tag2uid_const_iterator ut = tag2uid.find(Tag);
+ if (updateUid) {
+ if ((ut != tag2uid.end()) &&
+ (ut->second.find(uid) == ut->second.end())) {
+ unique = write; // write passthrough to update uid counts
+ if (!write) Tag = emptyTag; // deny read access
+ }
+ } else {
+ unique = write && (ut != tag2uid.end());
+ }
+ }
+ }
+
+ if (Tag == emptyTag) return Tag;
+ WritePmsgEventLogTags(Tag, uid); // record references periodically
+ if (!unique) return Tag;
+
+ bool updateWrite = false;
+ bool updateTag;
+
+ // Special case of AddEventLogTags, checks per-uid counter which makes
+ // no sense there, and is also optimized somewhat to reduce write times.
+ {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ // double check after switch from read lock to write lock for Tag
+ updateTag = tag2name.find(Tag) == tag2name.end();
+ // unlikely, either update, race inviting conflict or multiple uids
+ if (!updateTag) {
+ Tag = nameToTag_locked(Name, format, unique);
+ if (Tag == emptyTag) return Tag;
+ // is it multiple uid's setting this value
+ if (!unique) {
+ tag2uid_const_iterator ut = tag2uid.find(Tag);
+ if (updateUid) {
+ // Add it to the uid list
+ if ((ut == tag2uid.end()) ||
+ (ut->second.find(uid) != ut->second.end())) return Tag;
+ const_cast<uid_list&>(ut->second).emplace(uid);
+ updateWrite = true;
+ } else {
+ if (ut == tag2uid.end()) return Tag;
+ // (system) adding a global one, erase the uid list
+ tag2uid.erase(ut);
+ updateWrite = true;
+ }
+ }
+ }
+
+ // Update section
+ size_t count;
+ if (updateUid) {
+ count = 0;
+ uid2count_const_iterator ci = uid2count.find(uid);
+ if (ci != uid2count.end()) {
+ count = ci->second;
+ if (count >= max_per_uid) {
+ if (!updateWrite) return emptyTag;
+ // If we are added to the per-Uid perms, leak the Tag
+ // if it already exists.
+ updateUid = false;
+ updateTag = false;
+ updateFormat = false;
+ }
+ }
+ }
+
+ // updateWrite -> trigger output on modified content, reset tag2total
+ // also sets static to dynamic entries if they are alterred,
+ // only occurs if they have a uid, and runtime adds another uid.
+ if (updateWrite) tag2total[Tag] = 0;
+
+ if (updateTag) {
+ // mark as a dynamic entry, but do not upset current total counter
+ tag2total_const_iterator itot = tag2total.find(Tag);
+ if (itot == tag2total.end()) tag2total[Tag] = 0;
+
+ if (*format) {
+ key2tag[Name + "+" + format] = Tag;
+ if (key2tag.find(Name) == key2tag.end()) key2tag[Name] = Tag;
+ } else {
+ key2tag[Name] = Tag;
+ }
+ tag2name[Tag] = Name;
+ }
+ if (updateFormat) tag2format[Tag] = format;
+
+ if (updateUid) {
+ tag2uid[Tag].emplace(uid);
+ uid2count[uid] = count + 1;
+ }
+ }
+
+ if (updateTag || updateFormat || updateWrite) {
+ WritePersistEventLogTags(Tag, uid);
+ }
+
+ return Tag;
+}
+
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid,
+ const char* name,
+ const char* format) {
+ if (!format || !format[0]) {
+ return android::base::StringPrintf("%" PRIu32 "\t%s\n", tag, name);
+ }
+ size_t len = (strlen(name) + 7) / 8;
+ static const char tabs[] = "\t\t\t";
+ if (len > strlen(tabs)) len = strlen(tabs);
+ std::string Uid;
+ if (uid != AID_ROOT) Uid = android::base::StringPrintf(" # uid=%u", uid);
+ return android::base::StringPrintf("%" PRIu32 "\t%s%s\t%s%s\n",
+ tag, name, &tabs[len], format,
+ Uid.c_str());
+}
+
+std::string LogTags::formatEntry_locked(uint32_t tag, uid_t uid,
+ bool authenticate) {
+ const char* name = tag2name[tag].c_str();
+
+ const char* format = "";
+ tag2format_const_iterator iform = tag2format.find(tag);
+ if (iform != tag2format.end()) format = iform->second.c_str();
+
+ // Access permission test, do not report dynamic entries
+ // that do not belong to us.
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut == tag2uid.end()) {
+ return formatEntry(tag, AID_ROOT, name, format);
+ }
+ if (uid != AID_ROOT) {
+ if (authenticate && (ut->second.find(uid) == ut->second.end())) {
+ return std::string("");
+ }
+ return formatEntry(tag, uid, name, format);
+ }
+
+ // Show all, one for each registered uid (we are group root)
+ std::string ret;
+ for (auto &it : ut->second) {
+ ret += formatEntry(tag, it, name, format);
+ }
+ return ret;
+}
+
+std::string LogTags::formatGetEventTag(uid_t uid,
+ const char* name, const char* format) {
+ bool all = name && (name[0] == '*') && !name[1];
+ bool list = !name || all;
+ std::string ret;
+
+ if (!list) {
+ // switch to read entry only if format == "*"
+ if (format && (format[0] == '*') && !format[1]) format = NULL;
+
+ // WAI: for null format, only works for a single entry, we can have
+ // multiple entries, one for each format, so we find first entry
+ // recorded, or entry with no format associated with it.
+ // We may desire to print all that match the name, but we did not
+ // add a mapping table for that and the cost is too high.
+ uint32_t tag = nameToTag(uid, name, format);
+ if (tag == emptyTag) return std::string("-1 ESRCH");
+ if (uid == AID_ROOT) {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ // first uid in list so as to manufacture an accurate reference
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if ((ut != tag2uid.end()) &&
+ (ut->second.begin() != ut->second.end())) {
+ uid = *(ut->second.begin());
+ }
+ }
+ ret = formatEntry(tag, uid, name, format ?: tagToFormat(tag));
+ if (!ret.length()) return std::string("-1 ESRCH");
+ return ret;
+ }
+
+ android::RWLock::AutoRLock readLock(rwlock);
+ if (all) {
+ // everything under the sun
+ for (const auto& it : tag2name) {
+ ret += formatEntry_locked(it.first, uid);
+ }
+ } else {
+ // set entries are dynamic
+ for (const auto& it : tag2total) {
+ ret += formatEntry_locked(it.first, uid);
+ }
+ }
+ return ret;
+}
diff --git a/logd/LogTags.h b/logd/LogTags.h
new file mode 100644
index 0000000..37a6d96
--- /dev/null
+++ b/logd/LogTags.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_TAGS_H__
+#define _LOGD_LOG_TAGS_H__
+
+#include <unordered_map>
+#include <unordered_set>
+#include <string>
+
+#include <utils/RWLock.h>
+
+class LogTags {
+ // This lock protects all the unordered_map accesses below. It
+ // is a reader/writer lock so that contentions are kept to a
+ // minimum since writes are rare, even administratably when
+ // reads are extended. Resist the temptation to use the writer
+ // lock to protect anything outside the following unordered_maps
+ // as that would increase the reader contentions. Use a separate
+ // mutex to protect the other entities.
+ android::RWLock rwlock;
+
+ // key is Name + "+" + Format
+ std::unordered_map<std::string, uint32_t> key2tag;
+ typedef std::unordered_map<std::string, uint32_t>::const_iterator key2tag_const_iterator;
+
+ // Allows us to manage access permissions based on uid registrants
+ // Global entries are specifically erased.
+ typedef std::unordered_set<uid_t> uid_list;
+ std::unordered_map<uint32_t, uid_list> tag2uid;
+ typedef std::unordered_map<uint32_t, uid_list>::const_iterator tag2uid_const_iterator;
+
+ std::unordered_map<uint32_t, std::string> tag2name;
+ typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2name_const_iterator;
+
+ std::unordered_map<uint32_t, std::string> tag2format;
+ typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2format_const_iterator;
+
+ static const size_t max_per_uid = 256; // Put a cap on the tags per uid
+ std::unordered_map<uid_t, size_t> uid2count;
+ typedef std::unordered_map<uid_t, size_t>::const_iterator uid2count_const_iterator;
+
+ // Dynamic entries are assigned
+ std::unordered_map<uint32_t, size_t> tag2total;
+ typedef std::unordered_map<uint32_t, size_t>::const_iterator tag2total_const_iterator;
+
+ // emplace unique tag
+ uint32_t nameToTag(uid_t uid, const char* name, const char* format);
+ // find unique or associated tag
+ uint32_t nameToTag_locked(const std::string& name, const char* format, bool &unique);
+
+ // Record expected file watermarks to detect corruption.
+ std::unordered_map<std::string, size_t> file2watermark;
+ typedef std::unordered_map<std::string, size_t>::const_iterator file2watermark_const_iterator;
+
+ void ReadPersistEventLogTags();
+
+ // format helpers
+ // format a single entry, does not need object data
+ static std::string formatEntry(uint32_t tag, uid_t uid,
+ const char* name, const char* format);
+ // caller locks, database lookup, authenticate against uid
+ std::string formatEntry_locked(uint32_t tag, uid_t uid,
+ bool authenticate = true);
+
+ bool RebuildFileEventLogTags(const char* filename, bool warn = true);
+
+ void AddEventLogTags(uint32_t tag, uid_t uid,
+ const std::string& Name, const std::string& Format,
+ const char* source = NULL, bool warn = false);
+
+ void WriteDynamicEventLogTags(uint32_t tag, uid_t uid);
+ void WriteDebugEventLogTags(uint32_t tag, uid_t uid);
+ // push tag details to persistent storage
+ void WritePersistEventLogTags(uint32_t tag,
+ uid_t uid = AID_ROOT,
+ const char* source = NULL);
+
+ static const uint32_t emptyTag = uint32_t(-1);
+
+public:
+
+ static const char system_event_log_tags[];
+ static const char dynamic_event_log_tags[];
+ // Only for userdebug and eng
+ static const char debug_event_log_tags[];
+
+ LogTags();
+
+ void WritePmsgEventLogTags(uint32_t tag, uid_t uid = AID_ROOT);
+ void ReadFileEventLogTags(const char* filename, bool warn = true);
+
+ // reverse lookup from tag
+ const char* tagToName(uint32_t tag) const;
+ const char* tagToFormat(uint32_t tag) const;
+ // find associated tag
+ uint32_t nameToTag(const char* name) const;
+
+ // emplace tag if necessary, provide event-log-tag formated output in string
+ std::string formatGetEventTag(uid_t uid,
+ const char* name,
+ const char* format);
+};
+
+#endif // _LOGD_LOG_TAGS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 68a0680..2a04880 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <errno.h>
#include <sys/prctl.h>
#include "FlushCommand.h"
@@ -26,7 +27,7 @@
LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
bool nonBlock, unsigned long tail,
unsigned int logMask, pid_t pid,
- uint64_t start) :
+ uint64_t start, uint64_t timeout) :
mRefCount(1),
mRelease(false),
mError(false),
@@ -42,6 +43,8 @@
mStart(start),
mNonBlock(nonBlock),
mEnd(LogBufferElement::getCurrentSequence()) {
+ mTimeout.tv_sec = timeout / NS_PER_SEC;
+ mTimeout.tv_nsec = timeout % NS_PER_SEC;
pthread_cond_init(&threadTriggeredCondition, NULL);
cleanSkip_Locked();
}
@@ -87,7 +90,7 @@
while(it != times.end()) {
if (*it == me) {
times.erase(it);
- me->release_Locked();
+ me->release_nodelete_Locked();
break;
}
it++;
@@ -123,35 +126,54 @@
LogBuffer &logbuf = me->mReader.logbuf();
bool privileged = FlushCommand::hasReadLogs(client);
+ bool security = FlushCommand::hasSecurityLogs(client);
me->leadingDropped = true;
lock();
+ uint64_t start = me->mStart;
+
while (me->threadRunning && !me->isError_Locked()) {
- uint64_t start = me->mStart;
+
+ if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
+ if (pthread_cond_timedwait(&me->threadTriggeredCondition,
+ ×Lock,
+ &me->mTimeout) == ETIMEDOUT) {
+ me->mTimeout.tv_sec = 0;
+ me->mTimeout.tv_nsec = 0;
+ }
+ if (!me->threadRunning || me->isError_Locked()) {
+ break;
+ }
+ }
unlock();
if (me->mTail) {
- logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
+ logbuf.flushTo(client, start, privileged, security, FilterFirstPass, me);
me->leadingDropped = true;
}
- start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);
+ start = logbuf.flushTo(client, start, privileged, security, FilterSecondPass, me);
lock();
if (start == LogBufferElement::FLUSH_ERROR) {
me->error_Locked();
+ break;
}
+ me->mStart = start + 1;
+
if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
break;
}
me->cleanSkip_Locked();
- pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);
+ if (!me->mTimeout.tv_sec && !me->mTimeout.tv_nsec) {
+ pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);
+ }
}
unlock();
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 783bce6..12df994 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -20,11 +20,14 @@
#include <pthread.h>
#include <time.h>
#include <sys/types.h>
-#include <sysutils/SocketClient.h>
-#include <utils/List.h>
+
+#include <list>
+
#include <log/log.h>
+#include <sysutils/SocketClient.h>
class LogReader;
+class LogBufferElement;
class LogTimeEntry {
static pthread_mutex_t timesLock;
@@ -48,10 +51,11 @@
public:
LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock,
unsigned long tail, unsigned int logMask, pid_t pid,
- uint64_t start);
+ uint64_t start, uint64_t timeout);
SocketClient *mClient;
uint64_t mStart;
+ struct timespec mTimeout;
const bool mNonBlock;
const uint64_t mEnd; // only relevant if mNonBlock
@@ -71,7 +75,13 @@
void triggerSkip_Locked(log_id_t id, unsigned int skip) { skipAhead[id] = skip; }
void cleanSkip_Locked(void);
- // Called after LogTimeEntry removed from list, lock implicitly held
+ // These called after LogTimeEntry removed from list, lock implicitly held
+ void release_nodelete_Locked(void) {
+ mRelease = true;
+ pthread_cond_signal(&threadTriggeredCondition);
+ // assumes caller code path will call decRef_Locked()
+ }
+
void release_Locked(void) {
mRelease = true;
pthread_cond_signal(&threadTriggeredCondition);
@@ -107,6 +117,6 @@
static int FilterSecondPass(const LogBufferElement *element, void *me);
};
-typedef android::List<LogTimeEntry *> LastLogTimes;
+typedef std::list<LogTimeEntry *> LastLogTimes;
-#endif
+#endif // _LOGD_LOG_TIMES_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
new file mode 100644
index 0000000..f044b27
--- /dev/null
+++ b/logd/LogUtils.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012-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 _LOGD_LOG_UTILS_H__
+#define _LOGD_LOG_UTILS_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <utils/FastStrcmp.h>
+#include <private/android_logger.h>
+#include <sysutils/SocketClient.h>
+
+// Hijack this header as a common include file used by most all sources
+// to report some utilities defined here and there.
+
+namespace android {
+
+// Furnished in main.cpp. Caller must own and free returned value
+char *uidToName(uid_t uid);
+void prdebug(const char *fmt, ...) __printflike(1, 2);
+
+// Furnished in LogStatistics.cpp.
+size_t sizesTotal();
+// Caller must own and free returned value
+char *pidToName(pid_t pid);
+char *tidToName(pid_t tid);
+
+// Furnished in LogTags.cpp. Thread safe.
+const char *tagToName(uint32_t tag);
+void ReReadEventLogTags();
+
+// Furnished by LogKlog.cpp.
+const char* strnstr(const char* s, size_t len, const char* needle);
+
+}
+
+// Furnished in LogCommand.cpp
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
+bool clientHasLogCredentials(SocketClient *cli);
+
+static inline bool worstUidEnabledForLogid(log_id_t id) {
+ return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
+ (id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
+}
+
+#endif // _LOGD_LOG_UTILS_H__
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 277b3ca..ae933b5 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -16,7 +16,8 @@
#include <ctype.h>
-#include <utils/String8.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
#include "LogWhiteBlackList.h"
@@ -35,56 +36,84 @@
return uid - mUid;
}
-void Prune::format(char **strp) {
+std::string Prune::format() {
if (mUid != uid_all) {
if (mPid != pid_all) {
- asprintf(strp, "%u/%u", mUid, mPid);
- } else {
- asprintf(strp, "%u", mUid);
+ return android::base::StringPrintf("%u/%u", mUid, mPid);
}
- } else if (mPid != pid_all) {
- asprintf(strp, "/%u", mPid);
- } else { // NB: mPid == pid_all can not happen if mUid == uid_all
- asprintf(strp, "/");
+ return android::base::StringPrintf("%u", mUid);
}
+ if (mPid != pid_all) {
+ return android::base::StringPrintf("/%u", mPid);
+ }
+ // NB: mPid == pid_all can not happen if mUid == uid_all
+ return std::string("/");
}
-PruneList::PruneList() : mWorstUidEnabled(true) {
- mNaughty.clear();
- mNice.clear();
+PruneList::PruneList() {
+ init(NULL);
}
PruneList::~PruneList() {
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end();) {
- delete (*it);
it = mNice.erase(it);
}
for (it = mNaughty.begin(); it != mNaughty.end();) {
- delete (*it);
it = mNaughty.erase(it);
}
}
-int PruneList::init(char *str) {
+int PruneList::init(const char *str) {
mWorstUidEnabled = true;
+ mWorstPidOfSystemEnabled = true;
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end();) {
- delete (*it);
it = mNice.erase(it);
}
for (it = mNaughty.begin(); it != mNaughty.end();) {
- delete (*it);
it = mNaughty.erase(it);
}
- if (!str) {
- return 0;
+ static const char _default[] = "default";
+ // default here means take ro.logd.filter, persist.logd.filter then
+ // internal default in that order.
+ if (str && !strcmp(str, _default)) {
+ str = NULL;
+ }
+ static const char _disable[] = "disable";
+ if (str && !strcmp(str, _disable)) {
+ str = "";
+ }
+
+ std::string filter;
+
+ if (str) {
+ filter = str;
+ } else {
+ char property[PROPERTY_VALUE_MAX];
+ property_get("ro.logd.filter", property, _default);
+ filter = property;
+ property_get("persist.logd.filter", property, filter.c_str());
+ // default here means take ro.logd.filter
+ if (strcmp(property, _default)) {
+ filter = property;
+ }
+ }
+
+ // default here means take internal default.
+ if (filter == _default) {
+ // See README.property for description of filter format
+ filter = "~! ~1000/!";
+ }
+ if (filter == _disable) {
+ filter = "";
}
mWorstUidEnabled = false;
+ mWorstPidOfSystemEnabled = false;
- for(; *str; ++str) {
+ for(str = filter.c_str(); *str; ++str) {
if (isspace(*str)) {
continue;
}
@@ -104,6 +133,19 @@
}
continue;
}
+ // special case, translated to worst PID of System at priority
+ static const char worstPid[] = "1000/!";
+ if (!strncmp(str, worstPid, sizeof(worstPid) - 1)) {
+ mWorstPidOfSystemEnabled = true;
+ str += sizeof(worstPid) - 1;
+ if (!*str) {
+ break;
+ }
+ if (!isspace(*str)) {
+ return 1;
+ }
+ continue;
+ }
if (!*str) {
return 1;
}
@@ -142,28 +184,28 @@
// insert sequentially into list
PruneCollection::iterator it = list->begin();
while (it != list->end()) {
- Prune *p = *it;
- int m = uid - p->mUid;
+ Prune &p = *it;
+ int m = uid - p.mUid;
if (m == 0) {
- if (p->mPid == p->pid_all) {
+ if (p.mPid == p.pid_all) {
break;
}
- if ((pid == p->pid_all) && (p->mPid != p->pid_all)) {
+ if ((pid == p.pid_all) && (p.mPid != p.pid_all)) {
it = list->erase(it);
continue;
}
- m = pid - p->mPid;
+ m = pid - p.mPid;
}
if (m <= 0) {
if (m < 0) {
- list->insert(it, new Prune(uid,pid));
+ list->insert(it, Prune(uid,pid));
}
break;
}
++it;
}
if (it == list->end()) {
- list->push_back(new Prune(uid,pid));
+ list->push_back(Prune(uid,pid));
}
if (!*str) {
break;
@@ -173,47 +215,35 @@
return 0;
}
-void PruneList::format(char **strp) {
- if (*strp) {
- free(*strp);
- *strp = NULL;
- }
-
+std::string PruneList::format() {
static const char nice_format[] = " %s";
const char *fmt = nice_format + 1;
- android::String8 string;
+ std::string string;
if (mWorstUidEnabled) {
- string.setTo("~!");
+ string = "~!";
fmt = nice_format;
+ if (mWorstPidOfSystemEnabled) {
+ string += " ~1000/!";
+ }
}
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end(); ++it) {
- char *a = NULL;
- (*it)->format(&a);
-
- string.appendFormat(fmt, a);
+ string += android::base::StringPrintf(fmt, (*it).format().c_str());
fmt = nice_format;
-
- free(a);
}
static const char naughty_format[] = " ~%s";
fmt = naughty_format + (*fmt != ' ');
for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
- char *a = NULL;
- (*it)->format(&a);
-
- string.appendFormat(fmt, a);
+ string += android::base::StringPrintf(fmt, (*it).format().c_str());
fmt = naughty_format;
-
- free(a);
}
- *strp = strdup(string.string());
+ return string;
}
// ToDo: Lists are in sorted order, Prune->cmp() returns + or -
@@ -223,7 +253,7 @@
bool PruneList::naughty(LogBufferElement *element) {
PruneCollection::iterator it;
for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
- if (!(*it)->cmp(element)) {
+ if (!(*it).cmp(element)) {
return true;
}
}
@@ -233,7 +263,7 @@
bool PruneList::nice(LogBufferElement *element) {
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end(); ++it) {
- if (!(*it)->cmp(element)) {
+ if (!(*it).cmp(element)) {
return true;
}
}
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
index 5f60801..8b8e02f 100644
--- a/logd/LogWhiteBlackList.h
+++ b/logd/LogWhiteBlackList.h
@@ -19,9 +19,10 @@
#include <sys/types.h>
-#include <utils/List.h>
+#include <list>
+#include <string.h>
-#include <LogBufferElement.h>
+#include "LogBufferElement.h"
// White and Blacklist
@@ -43,31 +44,31 @@
int cmp(LogBufferElement *e) const { return cmp(e->getUid(), e->getPid()); }
- // *strp is malloc'd, use free to release
- void format(char **strp);
+ std::string format();
};
-typedef android::List<Prune *> PruneCollection;
+typedef std::list<Prune> PruneCollection;
class PruneList {
PruneCollection mNaughty;
PruneCollection mNice;
bool mWorstUidEnabled;
+ bool mWorstPidOfSystemEnabled;
public:
PruneList();
~PruneList();
- int init(char *str);
+ int init(const char *str);
bool naughty(LogBufferElement *element);
bool naughty(void) { return !mNaughty.empty(); }
bool nice(LogBufferElement *element);
bool nice(void) { return !mNice.empty(); }
bool worstUidEnabled() const { return mWorstUidEnabled; }
+ bool worstPidOfSystemEnabled() const { return mWorstPidOfSystemEnabled; }
- // *strp is malloc'd, use free to release
- void format(char **strp);
+ std::string format();
};
#endif // _LOGD_LOG_WHITE_BLACK_LIST_H__
diff --git a/logd/README.property b/logd/README.property
index a472efd..de6767a 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -1,27 +1,73 @@
-The properties that logd responds to are:
+The properties that logd and friends react to are:
name type default description
-logd.auditd bool true Enable selinux audit daemon
-logd.auditd.dmesg bool true selinux audit messages duplicated and
- sent on to dmesg log
-logd.klogd bool depends Enable klogd daemon
-logd.statistics bool depends Enable logcat -S statistics.
-ro.config.low_ram bool false if true, logd.statistics & logd.klogd
- default false
-ro.build.type string if user, logd.statistics & logd.klogd
- default false
-persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
- turns on logcat -f in logd context
-persist.logd.size number 256K default size of the buffer for all
- log ids at initial startup, at runtime
- use: logcat -b all -G <value>
-persist.logd.size.main number 256K Size of the buffer for the main log
-persist.logd.size.system number 256K Size of the buffer for the system log
-persist.logd.size.radio number 256K Size of the buffer for the radio log
-persist.logd.size.event number 256K Size of the buffer for the event log
-persist.logd.size.crash number 256K Size of the buffer for the crash log
+ro.logd.auditd bool true Enable selinux audit daemon
+ro.logd.auditd.dmesg bool true selinux audit messages sent to dmesg.
+ro.logd.auditd.main bool true selinux audit messages sent to main.
+ro.logd.auditd.events bool true selinux audit messages sent to events.
+persist.logd.security bool false Enable security buffer.
+ro.device_owner bool false Override persist.logd.security to false
+ro.logd.kernel bool+ svelte+ Enable klogd daemon
+ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
+ro.debuggable number if not "1", logd.statistics &
+ ro.logd.kernel default false.
+logd.logpersistd.enable bool auto Safe to start logpersist daemon service
+logd.logpersistd string persist Enable logpersist daemon, "logcatd"
+ turns on logcat -f in logd context.
+ Responds to logcatd, clear and stop.
+logd.logpersistd.buffer persist logpersistd buffers to collect
+logd.logpersistd.size persist logpersistd size in MB
+persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
+ turns on logcat -f in logd context.
+persist.logd.logpersistd.buffer all logpersistd buffers to collect
+persist.logd.logpersistd.size 256 logpersistd size in MB
+persist.logd.size number ro Global default size of the buffer for
+ all log ids at initial startup, at
+ runtime use: logcat -b all -G <value>
+ro.logd.size number svelte default for persist.logd.size. Larger
+ platform default sizes than 256KB are
+ known to not scale well under log spam
+ pressure. Address the spam first,
+ resist increasing the log buffer.
+persist.logd.size.<buffer> number ro Size of the buffer for <buffer> log
+ro.logd.size.<buffer> number svelte default for persist.logd.size.<buffer>
+ro.config.low_ram bool false if true, logd.statistics, logd.kernel
+ default false, logd.size 64K instead
+ of 256K.
+persist.logd.filter string Pruning filter to optimize content.
+ At runtime use: logcat -P "<string>"
+ro.logd.filter string "~! ~1000/!" default for persist.logd.filter.
+ This default means to prune the
+ oldest entries of chattiest UID, and
+ the chattiest PID of system
+ (1000, or AID_SYSTEM).
+persist.logd.timestamp string ro The recording timestamp source.
+ "m[onotonic]" is the only supported
+ key character, otherwise realtime.
+ro.logd.timestamp string realtime default for persist.logd.timestamp
+log.tag string persist The global logging level, VERBOSE,
+ DEBUG, INFO, WARN, ERROR, ASSERT or
+ SILENT. Only the first character is
+ the key character.
+persist.log.tag string build default for log.tag
+log.tag.<tag> string persist The <tag> specific logging level.
+persist.log.tag.<tag> string build default for log.tag.<tag>
NB:
-- number support multipliers (K or M) for convenience. Range is limited
- to between 64K and 256M for log buffer sizes. Individual logs override the
- global default.
+- auto - managed by /init
+- bool+ - "true", "false" and comma separated list of "eng" (forced false if
+ ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
+ true).
+- svelte - see ro.config.low_ram for details.
+- svelte+ - see ro.config.low_ram and ro.debuggable for details.
+- ro - <base property> temporary override, ro.<base property> platform default.
+- persist - <base property> override, persist.<base property> platform default.
+- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
+- number - support multipliers (K or M) for convenience. Range is limited
+ to between 64K and 256M for log buffer sizes. Individual log buffer ids
+ such as main, system, ... override global default.
+- Pruning filter is of form of a space-separated list of [~][UID][/PID]
+ references, where '~' prefix means to blacklist otherwise whitelist. For
+ blacklisting, UID or PID may be a '!' to instead reference the chattiest
+ client, with the restriction that the PID must be in the UID group 1000
+ (system or AID_SYSTEM).
diff --git a/logd/event.logtags b/logd/event.logtags
index db8c19b..39063a9 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -34,4 +34,5 @@
# TODO: generate ".java" and ".h" files with integer constants from this file.
1003 auditd (avc|3)
-1004 logd (dropped|3)
+1004 chatty (dropped|3)
+1005 tag_def (tag|1),(name|3),(format|3)
diff --git a/logd/libaudit.c b/logd/libaudit.c
index d00d579..216f1a1 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.c
@@ -22,21 +22,16 @@
#include <string.h>
#include <unistd.h>
-#define LOG_TAG "libaudit"
-#include <log/log.h>
-
#include "libaudit.h"
/**
* Waits for an ack from the kernel
* @param fd
* The netlink socket fd
- * @param seq
- * The current sequence number were acking on
* @return
* This function returns 0 on success, else -errno.
*/
-static int get_ack(int fd, int16_t seq)
+static int get_ack(int fd)
{
int rc;
struct audit_message rep;
@@ -59,11 +54,6 @@
}
}
- if ((int16_t)rep.nlh.nlmsg_seq != seq) {
- SLOGW("Expected sequence number between user space and kernel space is out of skew, "
- "expected %u got %u", seq, rep.nlh.nlmsg_seq);
- }
-
return 0;
}
@@ -108,7 +98,6 @@
/* Ensure the message is not too big */
if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) {
- SLOGE("netlink message is too large");
return -EINVAL;
}
@@ -139,7 +128,6 @@
/* Not all the bytes were sent */
if (rc < 0) {
rc = -errno;
- SLOGE("Error sending data over the netlink socket: %s", strerror(-errno));
goto out;
} else if ((uint32_t) rc != req.nlh.nlmsg_len) {
rc = -EPROTO;
@@ -147,7 +135,7 @@
}
/* We sent all the bytes, get the ack */
- rc = get_ack(fd, sequence);
+ rc = get_ack(fd);
/* If the ack failed, return the error, else return the sequence number */
rc = (rc == 0) ? (int) sequence : rc;
@@ -155,14 +143,13 @@
out:
/* Don't let sequence roll to negative */
if (sequence < 0) {
- SLOGW("Auditd to Kernel sequence number has rolled over");
sequence = 0;
}
return rc;
}
-int audit_setup(int fd, uint32_t pid)
+int audit_setup(int fd, pid_t pid)
{
int rc;
struct audit_message rep;
@@ -176,13 +163,11 @@
* and the the mask set to AUDIT_STATUS_PID
*/
status.pid = pid;
- status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
- status.rate_limit = 20; // audit entries per second
+ status.mask = AUDIT_STATUS_PID;
/* Let the kernel know this pid will be registering for audit events */
rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
if (rc < 0) {
- SLOGE("Could net set pid for audit events, error: %s", strerror(-rc));
return rc;
}
@@ -201,6 +186,27 @@
return 0;
}
+int audit_rate_limit(int fd, unsigned rate_limit)
+{
+ int rc;
+ struct audit_message rep;
+ struct audit_status status;
+
+ memset(&status, 0, sizeof(status));
+
+ status.mask = AUDIT_STATUS_RATE_LIMIT;
+ status.rate_limit = rate_limit; /* audit entries per second */
+
+ rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+ if (rc < 0) {
+ return rc;
+ }
+
+ audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
+
+ return 0;
+}
+
int audit_open()
{
return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
@@ -240,25 +246,21 @@
/* If request is non blocking and errno is EAGAIN, just return 0 */
return 0;
}
- SLOGE("Error receiving from netlink socket, error: %s", strerror(-rc));
return rc;
}
if (nladdrlen != sizeof(nladdr)) {
- SLOGE("Protocol fault, error: %s", strerror(EPROTO));
return -EPROTO;
}
/* Make sure the netlink message was not spoof'd */
if (nladdr.nl_pid) {
- SLOGE("Invalid netlink pid received, expected 0 got: %d", nladdr.nl_pid);
return -EINVAL;
}
/* Check if the reply from the kernel was ok */
if (!NLMSG_OK(&rep->nlh, (size_t)len)) {
rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE;
- SLOGE("Bad kernel response %s", strerror(-rc));
}
return rc;
@@ -266,9 +268,5 @@
void audit_close(int fd)
{
- int rc = close(fd);
- if (rc < 0) {
- SLOGE("Attempting to close invalid fd %d, error: %s", fd, strerror(errno));
- }
- return;
+ close(fd);
}
diff --git a/logd/libaudit.h b/logd/libaudit.h
index b9e330d..9865d43 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -33,7 +33,7 @@
#define MAX_AUDIT_MESSAGE_LENGTH 8970
typedef enum {
- GET_REPLY_BLOCKING=0,
+ GET_REPLY_BLOCKING = 0,
GET_REPLY_NONBLOCKING
} reply_t;
@@ -55,7 +55,7 @@
* A valid fd on success or < 0 on error with errno set.
* Returns the same errors as man 2 socket.
*/
-extern int audit_open(void);
+extern int audit_open(void);
/**
* Closes the fd returned from audit_open()
@@ -78,19 +78,36 @@
* @return
* This function returns 0 on success, else -errno.
*/
-extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block,
- int peek);
+extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block,
+ int peek);
/**
- * Sets a pid to recieve audit netlink events from the kernel
+ * Sets a pid to receive audit netlink events from the kernel
* @param fd
* The fd returned by a call to audit_open()
* @param pid
- * The pid whom to set as the reciever of audit messages
+ * The pid whom to set as the receiver of audit messages
* @return
* This function returns 0 on success, -errno on error.
*/
-extern int audit_setup(int fd, uint32_t pid);
+extern int audit_setup(int fd, pid_t pid);
+
+/**
+ * Sets the rate limit to receive audit netlink events from the kernel
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param max_rate
+ * The cap of the maximum number of audit messages a second
+ * @return
+ * This function returns 0 on success, -errno on error.
+ */
+
+/* Guidelines to follow for dynamic rate_limit */
+#define AUDIT_RATE_LIMIT_DEFAULT 20 /* acceptable burst rate */
+#define AUDIT_RATE_LIMIT_BURST_DURATION 10 /* number of seconds of burst */
+#define AUDIT_RATE_LIMIT_MAX 5 /* acceptable sustained rate */
+
+extern int audit_rate_limit(int fd, unsigned rate_limit);
__END_DECLS
diff --git a/logd/logd.rc b/logd/logd.rc
new file mode 100644
index 0000000..ee89b83
--- /dev/null
+++ b/logd/logd.rc
@@ -0,0 +1,23 @@
+service logd /system/bin/logd
+ socket logd stream 0666 logd logd
+ socket logdr seqpacket 0666 logd logd
+ socket logdw dgram 0222 logd logd
+ file /proc/kmsg r
+ file /dev/kmsg w
+ user logd
+ group logd system readproc
+ writepid /dev/cpuset/system-background/tasks
+
+service logd-reinit /system/bin/logd --reinit
+ oneshot
+ disabled
+ user logd
+ group logd
+ writepid /dev/cpuset/system-background/tasks
+
+on fs
+ write /dev/event-log-tags "# content owned by logd
+"
+ chown logd logd /dev/event-log-tags
+ chmod 0644 /dev/event-log-tags
+ restorecon /dev/event-log-tags
diff --git a/logd/logpersist b/logd/logpersist
deleted file mode 100755
index 215e1e2..0000000
--- a/logd/logpersist
+++ /dev/null
@@ -1,36 +0,0 @@
-#! /system/bin/sh
-# logpersist cat start and stop handlers
-data=/data/misc/logd
-property=persist.logd.logpersistd
-service=logcatd
-progname="${0##*/}"
-if [ X"${1}" = "-h" -o X"${1}" = X"--help" ]; then
- echo "${progname%.*}.cat - dump current ${service%d} logs"
- echo "${progname%.*}.start - start ${service} service"
- echo "${progname%.*}.stop [--clear] - stop ${service} service"
- exit 0
-fi
-case ${progname} in
-*.cat)
- su 1036 ls "${data}" |
- tr -d '\r' |
- sort -ru |
- sed "s#^#${data}/#" |
- su 1036 xargs cat
- ;;
-*.start)
- su 0 setprop ${property} ${service}
- getprop ${property}
- sleep 1
- ps -t | grep "${data##*/}.*${service%d}"
- ;;
-*.stop)
- su 0 stop ${service}
- su 0 setprop ${property} ""
- [ X"${1}" != X"-c" -a X"${1}" != X"--clear" ] ||
- ( sleep 1 ; su 1036,9998 rm -rf "${data}" )
- ;;
-*)
- echo "Unexpected command ${0##*/} ${@}" >&2
- exit 1
-esac
diff --git a/logd/logtagd.rc b/logd/logtagd.rc
new file mode 100644
index 0000000..46aa8c1
--- /dev/null
+++ b/logd/logtagd.rc
@@ -0,0 +1,9 @@
+#
+# logtagd event log tag service (debug only)
+#
+on post-fs-data
+ mkdir /data/misc/logd 0700 logd log
+ write /data/misc/logd/event-log-tags ""
+ chown logd log /data/misc/logd/event-log-tags
+ chmod 0600 /data/misc/logd/event-log-tags
+ restorecon /data/misc/logd/event-log-tags
diff --git a/logd/main.cpp b/logd/main.cpp
index 9b88983..2551f2e 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -33,13 +33,18 @@
#include <syslog.h>
#include <unistd.h>
+#include <cstdbool>
#include <memory>
+#include <android-base/macros.h>
+#include <cutils/android_get_control_file.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
+#include <packagelistparser/packagelistparser.h>
#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
#include <utils/threads.h>
#include "CommandListener.h"
@@ -47,6 +52,7 @@
#include "LogListener.h"
#include "LogAudit.h"
#include "LogKlog.h"
+#include "LogUtils.h"
#define KMSG_PRIORITY(PRI) \
'<', \
@@ -55,14 +61,14 @@
'>'
//
-// The service is designed to be run by init, it does not respond well
+// The service is designed to be run by init, it does not respond well
// to starting up manually. When starting up manually the sockets will
// fail to open typically for one of the following reasons:
// EADDRINUSE if logger is running.
// EACCESS if started without precautions (below)
//
// Here is a cookbook procedure for starting up logd manually assuming
-// init is out of the way, pedantically all permissions and selinux
+// init is out of the way, pedantically all permissions and SELinux
// security is put back in place:
//
// setenforce 0
@@ -83,75 +89,125 @@
// logd
//
-static int drop_privs() {
+static int drop_privs(bool klogd, bool auditd) {
+ // Tricky, if ro.build.type is "eng" then this is true because of the
+ // side effect that ro.debuggable == 1 as well, else it is false.
+ bool eng = __android_logger_property_get_bool("ro.build.type", BOOL_DEFAULT_FALSE);
+
struct sched_param param;
memset(¶m, 0, sizeof(param));
if (set_sched_policy(0, SP_BACKGROUND) < 0) {
- return -1;
+ android::prdebug("failed to set background scheduling policy");
+ if (!eng) return -1;
}
if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 0) {
- return -1;
+ android::prdebug("failed to set batch scheduler");
+ if (!eng) return -1;
}
if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+ android::prdebug("failed to set background cgroup");
+ if (!eng) return -1;
+ }
+
+ if (!eng && (prctl(PR_SET_DUMPABLE, 0) < 0)) {
+ android::prdebug("failed to clear PR_SET_DUMPABLE");
return -1;
}
if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- return -1;
+ android::prdebug("failed to set PR_SET_KEEPCAPS");
+ if (!eng) return -1;
+ }
+
+ std::unique_ptr<struct _cap_struct, int(*)(void *)> caps(cap_init(), cap_free);
+ if (cap_clear(caps.get()) < 0) return -1;
+ cap_value_t cap_value[] = {
+ CAP_SETGID, // must be first for below
+ klogd ? CAP_SYSLOG : CAP_SETGID,
+ auditd ? CAP_AUDIT_CONTROL : CAP_SETGID
+ };
+ if (cap_set_flag(caps.get(), CAP_PERMITTED,
+ arraysize(cap_value), cap_value,
+ CAP_SET) < 0) return -1;
+ if (cap_set_flag(caps.get(), CAP_EFFECTIVE,
+ arraysize(cap_value), cap_value,
+ CAP_SET) < 0) return -1;
+ if (cap_set_proc(caps.get()) < 0) {
+ android::prdebug("failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno);
+ if (!eng) return -1;
+ }
+
+ gid_t groups[] = { AID_READPROC };
+
+ if (setgroups(arraysize(groups), groups) == -1) {
+ android::prdebug("failed to set AID_READPROC groups");
+ if (!eng) return -1;
}
if (setgid(AID_LOGD) != 0) {
- return -1;
+ android::prdebug("failed to set AID_LOGD gid");
+ if (!eng) return -1;
}
if (setuid(AID_LOGD) != 0) {
- return -1;
+ android::prdebug("failed to set AID_LOGD uid");
+ if (!eng) return -1;
}
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
-
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
- capdata[CAP_TO_INDEX(CAP_AUDIT_CONTROL)].permitted |= CAP_TO_MASK(CAP_AUDIT_CONTROL);
-
- capdata[0].effective = capdata[0].permitted;
- capdata[1].effective = capdata[1].permitted;
- capdata[0].inheritable = 0;
- capdata[1].inheritable = 0;
-
- if (capset(&capheader, &capdata[0]) < 0) {
- return -1;
+ if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) return -1;
+ if (cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, cap_value, CAP_CLEAR) < 0) return -1;
+ if (cap_set_proc(caps.get()) < 0) {
+ android::prdebug("failed to clear CAP_SETGID (%d)", errno);
+ if (!eng) return -1;
}
return 0;
}
// Property helper
-static bool property_get_bool(const char *key, bool def) {
- char property[PROPERTY_VALUE_MAX];
- property_get(key, property, "");
-
- if (!strcasecmp(property, "true")) {
- return true;
- }
- if (!strcasecmp(property, "false")) {
+static bool check_flag(const char *prop, const char *flag) {
+ const char *cp = strcasestr(prop, flag);
+ if (!cp) {
return false;
}
-
- return def;
+ // We only will document comma (,)
+ static const char sep[] = ",:;|+ \t\f";
+ if ((cp != prop) && !strchr(sep, cp[-1])) {
+ return false;
+ }
+ cp += strlen(flag);
+ return !*cp || !!strchr(sep, *cp);
}
-// Remove the static, and use this variable
-// globally for debugging if necessary. eg:
-// write(fdDmesg, "I am here\n", 10);
static int fdDmesg = -1;
+void android::prdebug(const char *fmt, ...) {
+ if (fdDmesg < 0) {
+ return;
+ }
+
+ static const char message[] = {
+ KMSG_PRIORITY(LOG_DEBUG), 'l', 'o', 'g', 'd', ':', ' '
+ };
+ char buffer[256];
+ memcpy(buffer, message, sizeof(message));
+
+ va_list ap;
+ va_start(ap, fmt);
+ int n = vsnprintf(buffer + sizeof(message),
+ sizeof(buffer) - sizeof(message), fmt, ap);
+ va_end(ap);
+ if (n > 0) {
+ buffer[sizeof(buffer) - 1] = '\0';
+ if (!strchr(buffer, '\n')) {
+ buffer[sizeof(buffer) - 2] = '\0';
+ strlcat(buffer, "\n", sizeof(buffer));
+ }
+ write(fdDmesg, buffer, strlen(buffer));
+ }
+}
static sem_t uidName;
static uid_t uid;
@@ -161,13 +217,34 @@
static bool reinit_running = false;
static LogBuffer *logBuf = NULL;
+static bool package_list_parser_cb(pkg_info *info, void * /* userdata */) {
+
+ bool rc = true;
+ if (info->uid == uid) {
+ name = strdup(info->name);
+ // false to stop processing
+ rc = false;
+ }
+
+ packagelist_free(info);
+ return rc;
+}
+
static void *reinit_thread_start(void * /*obj*/) {
prctl(PR_SET_NAME, "logd.daemon");
set_sched_policy(0, SP_BACKGROUND);
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
- setgid(AID_SYSTEM);
- setuid(AID_SYSTEM);
+ cap_t caps = cap_init();
+ (void)cap_clear(caps);
+ (void)cap_set_proc(caps);
+ (void)cap_free(caps);
+
+ // If we are AID_ROOT, we should drop to AID_LOGD+AID_SYSTEM, if we are
+ // anything else, we have even lesser privileges and accept our fate. Not
+ // worth checking for error returns setting this thread's privileges.
+ (void)setgid(AID_SYSTEM); // readonly access to /data/system/packages.list
+ (void)setuid(AID_LOGD); // access to everything logd, eg /data/misc/logd
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
@@ -175,31 +252,8 @@
if (uid) {
name = NULL;
- FILE *fp = fopen("/data/system/packages.list", "r");
- if (fp) {
- // This simple parser is sensitive to format changes in
- // frameworks/base/services/core/java/com/android/server/pm/Settings.java
- // A dependency note has been added to that file to correct
- // this parser.
+ packagelist_parse(package_list_parser_cb, NULL);
- char *buffer = NULL;
- size_t len;
- while (getline(&buffer, &len, fp) > 0) {
- char *userId = strchr(buffer, ' ');
- if (!userId) {
- continue;
- }
- *userId = '\0';
- unsigned long value = strtoul(userId + 1, NULL, 10);
- if (value != uid) {
- continue;
- }
- name = strdup(buffer);
- break;
- }
- free(buffer);
- fclose(fp);
- }
uid = 0;
sem_post(&uidName);
continue;
@@ -215,7 +269,9 @@
// Anything that reads persist.<property>
if (logBuf) {
logBuf->init();
+ logBuf->initPrune(NULL);
}
+ android::ReReadEventLogTags();
}
return NULL;
@@ -249,73 +305,79 @@
sem_post(&reinit);
}
-// tagToName converts an events tag into a name
-const char *android::tagToName(uint32_t tag) {
- static const EventTagMap *map;
-
- if (!map) {
- sem_wait(&sem_name);
- if (!map) {
- map = android_openEventTagMap(EVENT_TAG_MAP_FILE);
- }
- sem_post(&sem_name);
- if (!map) {
- return NULL;
- }
- }
- return android_lookupEventTag(map, tag);
-}
-
-static bool property_get_bool_svelte(const char *key) {
- bool not_user;
- {
- char property[PROPERTY_VALUE_MAX];
- property_get("ro.build.type", property, "");
- not_user = !!strcmp(property, "user");
- }
- return property_get_bool(key, not_user
- && !property_get_bool("ro.config.low_ram", false));
-}
-
static void readDmesg(LogAudit *al, LogKlog *kl) {
if (!al && !kl) {
return;
}
- int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
- if (len <= 0) {
- return;
- }
-
- len += 1024; // Margin for additional input race or trailing nul
- std::unique_ptr<char []> buf(new char[len]);
-
- int rc = klogctl(KLOG_READ_ALL, buf.get(), len);
+ int rc = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
if (rc <= 0) {
return;
}
- if (rc < len) {
+ size_t len = rc + 1024; // Margin for additional input race or trailing nul
+ std::unique_ptr<char []> buf(new char[len]);
+
+ rc = klogctl(KLOG_READ_ALL, buf.get(), len);
+ if (rc <= 0) {
+ return;
+ }
+
+ if ((size_t)rc < len) {
len = rc + 1;
}
- buf[len - 1] = '\0';
+ buf[--len] = '\0';
- if (kl) {
- kl->synchronize(buf.get());
+ if (kl && kl->isMonotonic()) {
+ kl->synchronize(buf.get(), len);
}
+ size_t sublen;
for (char *ptr = NULL, *tok = buf.get();
- (rc >= 0) && ((tok = log_strtok_r(tok, &ptr)));
+ (rc >= 0) && ((tok = log_strntok_r(tok, &len, &ptr, &sublen)));
tok = NULL) {
if (al) {
- rc = al->log(tok);
+ rc = al->log(tok, sublen);
}
if (kl) {
- rc = kl->log(tok);
+ rc = kl->log(tok, sublen);
}
}
}
+static int issueReinit() {
+ cap_t caps = cap_init();
+ (void)cap_clear(caps);
+ (void)cap_set_proc(caps);
+ (void)cap_free(caps);
+
+ int sock = TEMP_FAILURE_RETRY(
+ socket_local_client("logd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM));
+ if (sock < 0) return -errno;
+
+ static const char reinitStr[] = "reinit";
+ ssize_t ret = TEMP_FAILURE_RETRY(write(sock, reinitStr, sizeof(reinitStr)));
+ if (ret < 0) return -errno;
+
+ struct pollfd p;
+ memset(&p, 0, sizeof(p));
+ p.fd = sock;
+ p.events = POLLIN;
+ ret = TEMP_FAILURE_RETRY(poll(&p, 1, 1000));
+ if (ret < 0) return -errno;
+ if ((ret == 0) || !(p.revents & POLLIN)) return -ETIME;
+
+ static const char success[] = "success";
+ char buffer[sizeof(success) - 1];
+ memset(buffer, 0, sizeof(buffer));
+ ret = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
+ if (ret < 0) return -errno;
+
+ return strncmp(buffer, success, sizeof(success) - 1) != 0;
+}
+
// Foreground waits for exit of the main persistent threads
// that are started here. The threads are created to manage
// UNIX domain client sockets for writing, reading and
@@ -323,46 +385,31 @@
// logging plugins like auditd and restart control. Additional
// transitory per-client threads are created for each reader.
int main(int argc, char *argv[]) {
- int fdPmesg = -1;
- bool klogd = property_get_bool_svelte("logd.klogd");
- if (klogd) {
- fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY);
- }
- fdDmesg = open("/dev/kmsg", O_WRONLY);
-
// issue reinit command. KISS argument parsing.
if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
- int sock = TEMP_FAILURE_RETRY(
- socket_local_client("logd",
- ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM));
- if (sock < 0) {
- return -errno;
+ return issueReinit();
+ }
+
+ static const char dev_kmsg[] = "/dev/kmsg";
+ fdDmesg = android_get_control_file(dev_kmsg);
+ if (fdDmesg < 0) {
+ fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));
+ }
+
+ int fdPmesg = -1;
+ bool klogd = __android_logger_property_get_bool("logd.kernel",
+ BOOL_DEFAULT_TRUE |
+ BOOL_DEFAULT_FLAG_PERSIST |
+ BOOL_DEFAULT_FLAG_ENG |
+ BOOL_DEFAULT_FLAG_SVELTE);
+ if (klogd) {
+ static const char proc_kmsg[] = "/proc/kmsg";
+ fdPmesg = android_get_control_file(proc_kmsg);
+ if (fdPmesg < 0) {
+ fdPmesg = TEMP_FAILURE_RETRY(open(proc_kmsg,
+ O_RDONLY | O_NDELAY | O_CLOEXEC));
}
- static const char reinit[] = "reinit";
- ssize_t ret = TEMP_FAILURE_RETRY(write(sock, reinit, sizeof(reinit)));
- if (ret < 0) {
- return -errno;
- }
- struct pollfd p;
- memset(&p, 0, sizeof(p));
- p.fd = sock;
- p.events = POLLIN;
- ret = TEMP_FAILURE_RETRY(poll(&p, 1, 100));
- if (ret < 0) {
- return -errno;
- }
- if ((ret == 0) || !(p.revents & POLLIN)) {
- return -ETIME;
- }
- static const char success[] = "success";
- char buffer[sizeof(success) - 1];
- memset(buffer, 0, sizeof(buffer));
- ret = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
- if (ret < 0) {
- return -errno;
- }
- return strncmp(buffer, success, sizeof(success) - 1) != 0;
+ if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
}
// Reinit Thread
@@ -387,7 +434,9 @@
pthread_attr_destroy(&attr);
}
- if (drop_privs() != 0) {
+ bool auditd = __android_logger_property_get_bool("ro.logd.auditd",
+ BOOL_DEFAULT_TRUE);
+ if (drop_privs(klogd, auditd) != 0) {
return -1;
}
@@ -404,7 +453,11 @@
signal(SIGHUP, reinit_signal_handler);
- if (property_get_bool_svelte("logd.statistics")) {
+ if (__android_logger_property_get_bool("logd.statistics",
+ BOOL_DEFAULT_TRUE |
+ BOOL_DEFAULT_FLAG_PERSIST |
+ BOOL_DEFAULT_FLAG_ENG |
+ BOOL_DEFAULT_FLAG_SVELTE)) {
logBuf->enableStatistics();
}
@@ -422,7 +475,7 @@
LogListener *swl = new LogListener(logBuf, reader);
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
- if (swl->startListener(300)) {
+ if (swl->startListener(600)) {
exit(1);
}
@@ -438,12 +491,14 @@
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
- bool auditd = property_get_bool("logd.auditd", true);
-
LogAudit *al = NULL;
if (auditd) {
- bool dmesg = property_get_bool("logd.auditd.dmesg", true);
- al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
+ al = new LogAudit(logBuf, reader,
+ __android_logger_property_get_bool(
+ "ro.logd.auditd.dmesg",
+ BOOL_DEFAULT_TRUE)
+ ? fdDmesg
+ : -1);
}
LogKlog *kl = NULL;
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index a7c6b53..c053993 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -27,12 +27,15 @@
# Unit tests.
# -----------------------------------------------------------------------------
+event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
+
test_c_flags := \
-fstack-protector-all \
-g \
-Wall -Wextra \
-Werror \
-fno-builtin \
+ $(event_flag)
test_src_files := \
logd_test.cpp
@@ -43,6 +46,6 @@
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 44fa95c..adf583b 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -15,25 +15,32 @@
*/
#include <fcntl.h>
+#include <inttypes.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <cutils/sockets.h>
#include <gtest/gtest.h>
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#ifdef __ANDROID__
+#include <selinux/selinux.h>
+#endif
-#include "cutils/sockets.h"
-#include "log/log.h"
-#include "log/logger.h"
+#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
+#include "../LogReader.h" // pickup LOGD_SNDTIMEO
-#define __unused __attribute__((__unused__))
-
-/*
- * returns statistics
- */
-static void my_android_logger_get_statistics(char *buf, size_t len)
+static void send_to_control(char* buf, size_t len)
{
- snprintf(buf, len, "getStatistics 0 1 2 3 4");
int sock = socket_local_client("logd",
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
@@ -41,7 +48,7 @@
if (write(sock, buf, strlen(buf) + 1) > 0) {
ssize_t ret;
while ((ret = read(sock, buf, len)) > 0) {
- if ((size_t)ret == len) {
+ if (((size_t)ret == len) || (len < PAGE_SIZE)) {
break;
}
len -= ret;
@@ -63,6 +70,15 @@
}
}
+/*
+ * returns statistics
+ */
+static void my_android_logger_get_statistics(char *buf, size_t len)
+{
+ snprintf(buf, len, "getStatistics 0 1 2 3 4");
+ send_to_control(buf, len);
+}
+
static void alloc_statistics(char **buffer, size_t *length)
{
size_t len = 8192;
@@ -112,18 +128,38 @@
++cp;
}
benchmark = cp;
+#ifdef DEBUG
+ char *end = strstr(benchmark, "\n");
+ if (end == NULL) {
+ end = benchmark + strlen(benchmark);
+ }
+ fprintf(stderr, "parse for spam counter in \"%.*s\"\n",
+ (int)(end - benchmark), benchmark);
+#endif
+ // content
while (isdigit(*cp)) {
++cp;
}
while (isspace(*cp)) {
++cp;
}
+ // optional +/- field?
+ if ((*cp == '-') || (*cp == '+')) {
+ while (isdigit(*++cp) ||
+ (*cp == '.') || (*cp == '%') || (*cp == 'X')) {
+ ;
+ }
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ }
+ // number of entries pruned
unsigned long value = 0;
while (isdigit(*cp)) {
value = value * 10ULL + *cp - '0';
++cp;
}
- if (value > 100000UL) {
+ if (value > 10UL) {
break;
}
benchmark = NULL;
@@ -165,7 +201,9 @@
EXPECT_TRUE(NULL != main_logs);
char *radio_logs = strstr(cp, "\nChattiest UIDs in radio ");
- EXPECT_TRUE(NULL != radio_logs);
+ if (!radio_logs) GTEST_LOG_(INFO) << "Value of: NULL != radio_logs\n"
+ "Actual: false\n"
+ "Expected: false\n";
char *system_logs = strstr(cp, "\nChattiest UIDs in system ");
EXPECT_TRUE(NULL != system_logs);
@@ -176,18 +214,28 @@
delete [] buf;
}
-static void caught_signal(int signum __unused) { }
+static void caught_signal(int /* signum */) { }
static void dump_log_msg(const char *prefix,
log_msg *msg, unsigned int version, int lid) {
+ std::cout << std::flush;
+ std::cerr << std::flush;
+ fflush(stdout);
+ fflush(stderr);
switch(msg->entry.hdr_size) {
case 0:
version = 1;
break;
- case sizeof(msg->entry_v2):
+ case sizeof(msg->entry_v2): /* PLUS case sizeof(msg->entry_v3): */
if (version == 0) {
- version = 2;
+ version = (msg->entry_v3.lid < LOG_ID_MAX) ? 3 : 2;
+ }
+ break;
+
+ case sizeof(msg->entry_v4):
+ if (version == 0) {
+ version = 4;
}
break;
}
@@ -223,6 +271,15 @@
case 3:
fprintf(stderr, "lid=system ");
break;
+ case 4:
+ fprintf(stderr, "lid=crash ");
+ break;
+ case 5:
+ fprintf(stderr, "lid=security ");
+ break;
+ case 6:
+ fprintf(stderr, "lid=kernel ");
+ break;
default:
if (lid >= 0) {
fprintf(stderr, "lid=%d ", lid);
@@ -232,6 +289,11 @@
unsigned int len = msg->entry.len;
fprintf(stderr, "msg[%u]={", len);
unsigned char *cp = reinterpret_cast<unsigned char *>(msg->msg());
+ if (!cp) {
+ static const unsigned char garbage[] = "<INVALID>";
+ cp = const_cast<unsigned char *>(garbage);
+ len = strlen(reinterpret_cast<const char *>(garbage));
+ }
while(len) {
unsigned char *p = cp;
while (*p && (((' ' <= *p) && (*p < 0x7F)) || (*p == '\n'))) {
@@ -258,6 +320,7 @@
}
}
fprintf(stderr, "}\n");
+ fflush(stderr);
}
TEST(logd, both) {
@@ -304,7 +367,7 @@
"/dev/log/system", "/dev/log_system",
};
- for (unsigned int i = 0; i < (sizeof(loggers) / sizeof(loggers[0])); ++i) {
+ for (unsigned int i = 0; i < arraysize(loggers); ++i) {
fd = open(loggers[i], O_RDONLY);
if (fd < 0) {
continue;
@@ -367,7 +430,13 @@
// Introduce some extreme spam for the worst UID filter
ASSERT_TRUE(NULL != (fp = popen(
- "/data/nativetest/liblog-benchmarks/liblog-benchmarks",
+ "/data/nativetest/liblog-benchmarks/liblog-benchmarks"
+ " BM_log_maximum_retry"
+ " BM_log_maximum"
+ " BM_clock_overhead"
+ " BM_log_overhead"
+ " BM_log_latency"
+ " BM_log_delay",
"r")));
char buffer[5120];
@@ -387,12 +456,12 @@
static const unsigned int log_latency = 4;
static const unsigned int log_delay = 5;
- unsigned long ns[sizeof(benchmarks) / sizeof(benchmarks[0])];
+ unsigned long ns[arraysize(benchmarks)];
memset(ns, 0, sizeof(ns));
while (fgets(buffer, sizeof(buffer), fp)) {
- for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
+ for (unsigned i = 0; i < arraysize(ns); ++i) {
char *cp = strstr(buffer, benchmarks[i]);
if (!cp) {
continue;
@@ -419,11 +488,11 @@
EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user
- EXPECT_GE(10000UL, ns[log_latency]); // 5669 user space
+ EXPECT_GE(10000000UL, ns[log_latency]); // 1453559 user space (background cgroup)
EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
- for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
+ for (unsigned i = 0; i < arraysize(ns); ++i) {
EXPECT_NE(0UL, ns[i]);
}
@@ -499,3 +568,559 @@
// 50% threshold for SPAM filter (<20% typical, lots of engineering margin)
ASSERT_GT(totalSize, nowSpamSize * 2);
}
+
+// b/26447386 confirm fixed
+void timeout_negative(const char *command) {
+ log_msg msg_wrap, msg_timeout;
+ bool content_wrap = false, content_timeout = false, written = false;
+ unsigned int alarm_wrap = 0, alarm_timeout = 0;
+ // A few tries to get it right just in case wrap kicks in due to
+ // content providers being active during the test.
+ int i = 3;
+
+ while (--i) {
+ int fd = socket_local_client("logdr",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ ASSERT_LT(0, fd);
+
+ std::string ask(command);
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ unsigned int old_alarm = alarm(3);
+
+ size_t len = ask.length() + 1;
+ written = write(fd, ask.c_str(), len) == (ssize_t)len;
+ if (!written) {
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+ close(fd);
+ continue;
+ }
+
+ // alarm triggers at 50% of the --wrap time out
+ content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
+
+ alarm_wrap = alarm(5);
+
+ // alarm triggers at 133% of the --wrap time out
+ content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+ if (!content_timeout) { // make sure we hit dumpAndClose
+ content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+ }
+
+ alarm_timeout = alarm((old_alarm <= 0)
+ ? old_alarm
+ : (old_alarm > (1 + 3 - alarm_wrap))
+ ? old_alarm - 3 + alarm_wrap
+ : 2);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+
+ close(fd);
+
+ if (!content_wrap && !alarm_wrap && content_timeout && alarm_timeout) {
+ break;
+ }
+ }
+
+ if (content_wrap) {
+ dump_log_msg("wrap", &msg_wrap, 3, -1);
+ }
+
+ if (content_timeout) {
+ dump_log_msg("timeout", &msg_timeout, 3, -1);
+ }
+
+ EXPECT_TRUE(written);
+ EXPECT_TRUE(content_wrap);
+ EXPECT_NE(0U, alarm_wrap);
+ EXPECT_TRUE(content_timeout);
+ EXPECT_NE(0U, alarm_timeout);
+}
+
+TEST(logd, timeout_no_start) {
+ timeout_negative("dumpAndClose lids=0,1,2,3,4,5 timeout=6");
+}
+
+TEST(logd, timeout_start_epoch) {
+ timeout_negative("dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=0.000000000");
+}
+
+// b/26447386 refined behavior
+TEST(logd, timeout) {
+ // b/33962045 This test interferes with other log reader tests that
+ // follow because of file descriptor socket persistence in the same
+ // process. So let's fork it to isolate it from giving us pain.
+
+ pid_t pid = fork();
+
+ if (pid) {
+ siginfo_t info = {};
+ ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
+ ASSERT_EQ(0, info.si_status);
+ return;
+ }
+
+ log_msg msg_wrap, msg_timeout;
+ bool content_wrap = false, content_timeout = false, written = false;
+ unsigned int alarm_wrap = 0, alarm_timeout = 0;
+ // A few tries to get it right just in case wrap kicks in due to
+ // content providers being active during the test.
+ int i = 5;
+ log_time now(android_log_clockid());
+ now.tv_sec -= 30; // reach back a moderate period of time
+
+ while (--i) {
+ int fd = socket_local_client("logdr",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ EXPECT_LT(0, fd);
+ if (fd < 0) _exit(fd);
+
+ std::string ask = android::base::StringPrintf(
+ "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%"
+ PRIu32 ".%09" PRIu32,
+ now.tv_sec, now.tv_nsec);
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ unsigned int old_alarm = alarm(3);
+
+ size_t len = ask.length() + 1;
+ written = write(fd, ask.c_str(), len) == (ssize_t)len;
+ if (!written) {
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+ close(fd);
+ continue;
+ }
+
+ // alarm triggers at 50% of the --wrap time out
+ content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
+
+ alarm_wrap = alarm(5);
+
+ // alarm triggers at 133% of the --wrap time out
+ content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+ if (!content_timeout) { // make sure we hit dumpAndClose
+ content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+ }
+
+ alarm_timeout = alarm((old_alarm <= 0)
+ ? old_alarm
+ : (old_alarm > (1 + 3 - alarm_wrap))
+ ? old_alarm - 3 + alarm_wrap
+ : 2);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+
+ close(fd);
+
+ if (!content_wrap && !alarm_wrap && content_timeout && alarm_timeout) {
+ break;
+ }
+
+ // modify start time in case content providers are relatively
+ // active _or_ inactive during the test.
+ if (content_timeout) {
+ log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
+ EXPECT_FALSE(msg < now);
+ if (msg < now) _exit(-1);
+ if (msg > now) {
+ now = msg;
+ now.tv_sec += 30;
+ msg = log_time(android_log_clockid());
+ if (now > msg) {
+ now = msg;
+ --now.tv_sec;
+ }
+ }
+ } else {
+ now.tv_sec -= 120; // inactive, reach further back!
+ }
+ }
+
+ if (content_wrap) {
+ dump_log_msg("wrap", &msg_wrap, 3, -1);
+ }
+
+ if (content_timeout) {
+ dump_log_msg("timeout", &msg_timeout, 3, -1);
+ }
+
+ if (content_wrap || !content_timeout) {
+ fprintf(stderr, "now=%" PRIu32 ".%09" PRIu32 "\n",
+ now.tv_sec, now.tv_nsec);
+ }
+
+ EXPECT_TRUE(written);
+ EXPECT_FALSE(content_wrap);
+ EXPECT_EQ(0U, alarm_wrap);
+ EXPECT_TRUE(content_timeout);
+ EXPECT_NE(0U, alarm_timeout);
+
+ _exit(!written + content_wrap + alarm_wrap + !content_timeout + !alarm_timeout);
+}
+
+// b/27242723 confirmed fixed
+TEST(logd, SNDTIMEO) {
+ static const unsigned sndtimeo = LOGD_SNDTIMEO; // <sigh> it has to be done!
+ static const unsigned sleep_time = sndtimeo + 3;
+ static const unsigned alarm_time = sleep_time + 5;
+
+ int fd;
+
+ ASSERT_TRUE((fd = socket_local_client("logdr",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET)) > 0);
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ unsigned int old_alarm = alarm(alarm_time);
+
+ static const char ask[] = "stream lids=0,1,2,3,4,5,6"; // all sources
+ bool reader_requested = write(fd, ask, sizeof(ask)) == sizeof(ask);
+ EXPECT_TRUE(reader_requested);
+
+ log_msg msg;
+ bool read_one = recv(fd, msg.buf, sizeof(msg), 0) > 0;
+
+ EXPECT_TRUE(read_one);
+ if (read_one) {
+ dump_log_msg("user", &msg, 3, -1);
+ }
+
+ fprintf (stderr, "Sleep for >%d seconds logd SO_SNDTIMEO ...\n", sndtimeo);
+ sleep(sleep_time);
+
+ // flush will block if we did not trigger. if it did, last entry returns 0
+ int recv_ret;
+ do {
+ recv_ret = recv(fd, msg.buf, sizeof(msg), 0);
+ } while (recv_ret > 0);
+ int save_errno = (recv_ret < 0) ? errno : 0;
+
+ EXPECT_NE(0U, alarm(old_alarm));
+ sigaction(SIGALRM, &old_sigaction, NULL);
+
+ EXPECT_EQ(0, recv_ret);
+ if (recv_ret > 0) {
+ dump_log_msg("user", &msg, 3, -1);
+ }
+ EXPECT_EQ(0, save_errno);
+
+ close(fd);
+}
+
+TEST(logd, getEventTag_list) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ snprintf(buffer, sizeof(buffer), "getEventTag name=*");
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char *cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 4096);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, getEventTag_newentry) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ log_time now(CLOCK_MONOTONIC);
+ char name[64];
+ snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+ snprintf(buffer, sizeof(buffer),
+ "getEventTag name=%s format=\"(new|1)\"", name);
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char *cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 16);
+ EXPECT_TRUE(strstr(buffer, "\t(new|1)") != NULL);
+ EXPECT_TRUE(strstr(buffer, name) != NULL);
+ // ToDo: also look for this in /data/misc/logd/event-log-tags and
+ // /dev/event-log-tags.
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+static inline int32_t get4LE(const char* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+void __android_log_btwrite_multiple__helper(int count) {
+ log_time ts(CLOCK_MONOTONIC);
+
+ log_time ts1(CLOCK_MONOTONIC);
+
+ // We fork to create a unique pid for the submitted log messages
+ // so that we do not collide with the other _multiple_ tests.
+
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ // child
+ for (int i = count; i; --i) {
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ usleep(100);
+ }
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+ usleep(1000000);
+
+ _exit(0);
+ }
+
+ siginfo_t info = {};
+ ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
+ ASSERT_EQ(0, info.si_status);
+
+ struct logger_list *logger_list;
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)));
+
+ int expected_count = (count < 2) ? count : 2;
+ int expected_chatty_count = (count <= 2) ? 0 : 1;
+ int expected_identical_count = (count < 2) ? 0 : (count - 2);
+ static const int expected_expire_count = 0;
+
+ count = 0;
+ int second_count = 0;
+ int chatty_count = 0;
+ int identical_count = 0;
+ int expire_count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+ if ((log_msg.entry.pid != pid) ||
+ (log_msg.entry.len < (4 + 1 + 8)) ||
+ (log_msg.id() != LOG_ID_EVENTS)) continue;
+
+ char *eventData = log_msg.msg();
+ if (!eventData) continue;
+
+ uint32_t tag = get4LE(eventData);
+
+ if ((eventData[4] == EVENT_TYPE_LONG) && (log_msg.entry.len == (4 + 1 + 8))) {
+ if (tag != 0) continue;
+
+ log_time tx(eventData + 4 + 1);
+ if (ts == tx) {
+ ++count;
+ } else if (ts1 == tx) {
+ ++second_count;
+ }
+ } else if (eventData[4] == EVENT_TYPE_STRING) {
+ if (tag != CHATTY_LOG_TAG) continue;
+ ++chatty_count;
+ // int len = get4LE(eventData + 4 + 1);
+ log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
+ const char *cp;
+ if ((cp = strstr(eventData + 4 + 1 + 4, " identical "))) {
+ unsigned val = 0;
+ sscanf(cp, " identical %u lines", &val);
+ identical_count += val;
+ } else if ((cp = strstr(eventData + 4 + 1 + 4, " expire "))) {
+ unsigned val = 0;
+ sscanf(cp, " expire %u lines", &val);
+ expire_count += val;
+ }
+ }
+ }
+
+ android_logger_list_close(logger_list);
+
+ EXPECT_EQ(expected_count, count);
+ EXPECT_EQ(1, second_count);
+ EXPECT_EQ(expected_chatty_count, chatty_count);
+ EXPECT_EQ(expected_identical_count, identical_count);
+ EXPECT_EQ(expected_expire_count, expire_count);
+}
+
+TEST(logd, multiple_test_1) {
+ __android_log_btwrite_multiple__helper(1);
+}
+
+TEST(logd, multiple_test_2) {
+ __android_log_btwrite_multiple__helper(2);
+}
+
+TEST(logd, multiple_test_3) {
+ __android_log_btwrite_multiple__helper(3);
+}
+
+TEST(logd, multiple_test_10) {
+ __android_log_btwrite_multiple__helper(10);
+}
+
+#ifdef __ANDROID__
+// returns violating pid
+static pid_t sepolicy_rate(unsigned rate, unsigned num) {
+ pid_t pid = fork();
+
+ if (pid) {
+ siginfo_t info = {};
+ if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return 0;
+ if (info.si_status) return 0;
+ return pid;
+ }
+
+ // We may have DAC, but let's not have MAC
+ if (setcon("u:object_r:shell:s0") < 0) {
+ int save_errno = errno;
+ security_context_t context;
+ getcon(&context);
+ fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+ context, strerror(save_errno));
+ freecon(context);
+ _exit(-1);
+ // NOTREACHED
+ return 0;
+ }
+
+ // The key here is we are root, but we are in u:r:shell:s0,
+ // and the directory does not provide us DAC access
+ // (eg: 0700 system system) so we trigger the pair dac_override
+ // and dac_read_search on every try to get past the message
+ // de-duper. We will also rotate the file name in the directory
+ // as another measure.
+ static const char file[] = "/data/backup/cannot_access_directory_%u";
+ static const unsigned avc_requests_per_access = 2;
+
+ rate /= avc_requests_per_access;
+ useconds_t usec;
+ if (rate == 0) {
+ rate = 1;
+ usec = 2000000;
+ } else {
+ usec = (1000000 + (rate / 2)) / rate;
+ }
+ num = (num + (avc_requests_per_access / 2)) / avc_requests_per_access;
+
+ if (usec < 2) usec = 2;
+
+ while (num > 0) {
+ if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
+ _exit(-1);
+ // NOTREACHED
+ return 0;
+ }
+ usleep(usec);
+ --num;
+ }
+ _exit(0);
+ // NOTREACHED
+ return 0;
+}
+
+static int count_avc(pid_t pid) {
+ int count = 0;
+
+ if (pid == 0) return count;
+
+ struct logger_list *logger_list;
+ if (!(logger_list = android_logger_list_open(LOG_ID_EVENTS,
+ ANDROID_LOG_RDONLY |
+ ANDROID_LOG_NONBLOCK,
+ 0,
+ pid))) return count;
+ for (;;) {
+ log_msg log_msg;
+
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+ if ((log_msg.entry.pid != pid) ||
+ (log_msg.entry.len < (4 + 1 + 8)) ||
+ (log_msg.id() != LOG_ID_EVENTS)) continue;
+
+ char *eventData = log_msg.msg();
+ if (!eventData) continue;
+
+ uint32_t tag = get4LE(eventData);
+ if (tag != AUDITD_LOG_TAG) continue;
+
+ if (eventData[4] != EVENT_TYPE_STRING) continue;
+
+ // int len = get4LE(eventData + 4 + 1);
+ log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
+ const char *cp = strstr(eventData + 4 + 1 + 4, "): avc: denied");
+ if (!cp) continue;
+
+ ++count;
+ }
+
+ android_logger_list_close(logger_list);
+
+ return count;
+}
+#endif
+
+TEST(logd, sepolicy_rate_limiter_maximum) {
+#ifdef __ANDROID__
+ static const int rate = AUDIT_RATE_LIMIT_MAX;
+ static const int duration = 2;
+ // Two seconds of a liveable sustained rate
+ EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, sepolicy_rate_limiter_sub_burst) {
+#ifdef __ANDROID__
+ // maximum period below half way between sustainable and burst rate.
+ static const int threshold = ((AUDIT_RATE_LIMIT_BURST_DURATION *
+ (AUDIT_RATE_LIMIT_DEFAULT +
+ AUDIT_RATE_LIMIT_MAX)) +
+ 1) / 2;
+ static const int rate = (threshold / AUDIT_RATE_LIMIT_BURST_DURATION) - 1;
+ static const int duration = AUDIT_RATE_LIMIT_BURST_DURATION;
+ EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, sepolicy_rate_limiter_spam) {
+#ifdef __ANDROID__
+ // maximum period of double the maximum burst rate
+ static const int threshold = ((AUDIT_RATE_LIMIT_BURST_DURATION *
+ (AUDIT_RATE_LIMIT_DEFAULT +
+ AUDIT_RATE_LIMIT_MAX)) +
+ 1) / 2;
+ static const int rate = AUDIT_RATE_LIMIT_DEFAULT * 2;
+ static const int duration = threshold / AUDIT_RATE_LIMIT_DEFAULT;
+ EXPECT_GE(((AUDIT_RATE_LIMIT_DEFAULT * duration) * 115) /
+ 100, // +15% margin
+ count_avc(sepolicy_rate(rate, rate * duration)));
+ // give logd another 3 seconds to react to the burst before checking
+ sepolicy_rate(rate, rate * 3);
+ // maximum period at double the maximum burst rate (spam filter kicked in)
+ EXPECT_GE(threshold * 2,
+ count_avc(sepolicy_rate(rate,
+ rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
+ // cool down, and check unspammy rate still works
+ sleep(2);
+ EXPECT_LE(AUDIT_RATE_LIMIT_BURST_DURATION - 1, // allow _one_ to be lost
+ count_avc(sepolicy_rate(1, AUDIT_RATE_LIMIT_BURST_DURATION)));
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
new file mode 100644
index 0000000..7ee0464
--- /dev/null
+++ b/logwrapper/Android.bp
@@ -0,0 +1,34 @@
+
+
+// ========================================================
+// Static and shared library
+// ========================================================
+cc_library {
+ name: "liblogwrap",
+ srcs: ["logwrap.c"],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+ cflags: [
+ "-Werror",
+ ],
+}
+
+// ========================================================
+// Executable
+// ========================================================
+cc_binary {
+ name: "logwrapper",
+ srcs: ["logwrapper.c"],
+ static_libs: [
+ "liblog",
+ "liblogwrap",
+ "libcutils",
+ ],
+ cflags: [
+ "-Werror",
+ ],
+}
diff --git a/logwrapper/Android.mk b/logwrapper/Android.mk
deleted file mode 100644
index 61b4659..0000000
--- a/logwrapper/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# ========================================================
-# Static library
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := liblogwrap
-LOCAL_SRC_FILES := logwrap.c
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-# ========================================================
-# Shared library
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := liblogwrap
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-# ========================================================
-# Executable
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= logwrapper.c
-LOCAL_MODULE := logwrapper
-LOCAL_STATIC_LIBRARIES := liblog liblogwrap libcutils
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
index 449461f..89a8fdd 100644
--- a/logwrapper/include/logwrap/logwrap.h
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -73,7 +73,9 @@
#define LOG_FILE 4
/* Write data to child's stdin. */
-#define FORK_EXECVP_OPTION_INPUT 0
+#define FORK_EXECVP_OPTION_INPUT 0
+/* Capture data from child's stdout and stderr. */
+#define FORK_EXECVP_OPTION_CAPTURE_OUTPUT 1
struct AndroidForkExecvpOption {
int opt_type;
@@ -82,6 +84,12 @@
const uint8_t* input;
size_t input_len;
} opt_input;
+ struct {
+ void (*on_output)(const uint8_t* /*output*/,
+ size_t /*output_len*/,
+ void* /* user_pointer */);
+ void* user_pointer;
+ } opt_capture_output;
};
};
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index c7b4835..3ad0983 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -14,24 +14,24 @@
* limitations under the License.
*/
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <poll.h>
-#include <sys/wait.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
-#include <stdbool.h>
+#include <poll.h>
#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
-#include <logwrap/logwrap.h>
-#include "private/android_filesystem_config.h"
-#include "cutils/log.h"
#include <cutils/klog.h>
+#include <log/log.h>
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#define MIN(a,b) (((a)<(b))?(a):(b))
@@ -291,7 +291,8 @@
}
static int parent(const char *tag, int parent_read, pid_t pid,
- int *chld_sts, int log_target, bool abbreviated, char *file_path) {
+ int *chld_sts, int log_target, bool abbreviated, char *file_path,
+ const struct AndroidForkExecvpOption* opts, size_t opts_len) {
int status = 0;
char buffer[4096];
struct pollfd poll_fds[] = {
@@ -325,7 +326,7 @@
if (log_target & LOG_KLOG) {
snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt),
- "<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag);
+ "<6>%.*s: %%s\n", MAX_KLOG_TAG, log_info.btag);
}
if ((log_target & LOG_FILE) && !file_path) {
@@ -358,6 +359,13 @@
sz = TEMP_FAILURE_RETRY(
read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
+ for (size_t i = 0; sz > 0 && i < opts_len; ++i) {
+ if (opts[i].opt_type == FORK_EXECVP_OPTION_CAPTURE_OUTPUT) {
+ opts[i].opt_capture_output.on_output(
+ (uint8_t*)&buffer[b], sz, opts[i].opt_capture_output.user_pointer);
+ }
+ }
+
sz += b;
// Log one line at a time
for (b = 0; b < sz; b++) {
@@ -400,7 +408,7 @@
if (poll_fds[0].revents & POLLHUP) {
int ret;
- ret = waitpid(pid, &status, WNOHANG);
+ ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (ret < 0) {
rc = errno;
ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
@@ -484,7 +492,6 @@
sigset_t blockset;
sigset_t oldset;
int rc = 0;
- size_t i;
rc = pthread_mutex_lock(&fd_mutex);
if (rc) {
@@ -532,7 +539,7 @@
close(parent_ptty);
// redirect stdin, stdout and stderr
- for (i = 0; i < opts_len; ++i) {
+ for (size_t i = 0; i < opts_len; ++i) {
if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
dup2(child_ptty, 0);
break;
@@ -554,7 +561,7 @@
sigaction(SIGQUIT, &ignact, &quitact);
}
- for (i = 0; i < opts_len; ++i) {
+ for (size_t i = 0; i < opts_len; ++i) {
if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
size_t left = opts[i].opt_input.input_len;
const uint8_t* input = opts[i].opt_input.input;
@@ -571,7 +578,7 @@
}
rc = parent(argv[0], parent_ptty, pid, status, log_target,
- abbreviated, file_path);
+ abbreviated, file_path, opts, opts_len);
}
if (ignore_int_quit) {
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
index 55b71c7..33454c6 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.c
@@ -20,10 +20,9 @@
#include <sys/wait.h>
#include <unistd.h>
-#include <logwrap/logwrap.h>
#include <cutils/klog.h>
-
-#include "cutils/log.h"
+#include <log/log.h>
+#include <logwrap/logwrap.h>
void fatal(const char *msg) {
fprintf(stderr, "%s", msg);
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
deleted file mode 100644
index 9edba6e..0000000
--- a/metricsd/Android.mk
+++ /dev/null
@@ -1,116 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-ifeq ($(HOST_OS),linux)
-
-metrics_cpp_extension := .cc
-libmetrics_sources := \
- c_metrics_library.cc \
- metrics_library.cc \
- serialization/metric_sample.cc \
- serialization/serialization_utils.cc
-
-metrics_client_sources := \
- metrics_client.cc
-
-metrics_daemon_sources := \
- metrics_daemon.cc \
- metrics_daemon_main.cc \
- persistent_integer.cc \
- uploader/metrics_hashes.cc \
- uploader/metrics_log_base.cc \
- uploader/metrics_log.cc \
- uploader/sender_http.cc \
- uploader/system_profile_cache.cc \
- uploader/upload_service.cc \
- serialization/metric_sample.cc \
- serialization/serialization_utils.cc
-
-metrics_CFLAGS := -Wall \
- -D__BRILLO__ \
- -Wno-char-subscripts \
- -Wno-missing-field-initializers \
- -Wno-unused-function \
- -Wno-unused-parameter \
- -Werror \
- -fvisibility=default
-metrics_CPPFLAGS := -Wno-non-virtual-dtor \
- -Wno-sign-promo \
- -Wno-strict-aliasing \
- -fvisibility=default
-metrics_includes := external/gtest/include \
- $(LOCAL_PATH)/include
-metrics_shared_libraries := libchrome libchromeos
-
-# Shared library for metrics.
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libmetrics
-LOCAL_C_INCLUDES := $(metrics_includes)
-LOCAL_CFLAGS := $(metrics_CFLAGS)
-LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
-LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries)
-LOCAL_SRC_FILES := $(libmetrics_sources)
-include $(BUILD_SHARED_LIBRARY)
-
-# CLI client for metrics.
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := metrics_client
-LOCAL_C_INCLUDES := $(metrics_includes)
-LOCAL_CFLAGS := $(metrics_CFLAGS)
-LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
-LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
- libmetrics
-LOCAL_SRC_FILES := $(metrics_client_sources)
-include $(BUILD_EXECUTABLE)
-
-# Protobuf library for metrics_daemon.
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := metrics_daemon_protos
-LOCAL_MODULE_CLASS := STATIC_LIBRARIES
-generated_sources_dir := $(call local-generated-sources-dir)
-LOCAL_EXPORT_C_INCLUDE_DIRS += \
- $(generated_sources_dir)/proto/system/core/metricsd
-LOCAL_SRC_FILES := $(call all-proto-files-under,uploader/proto)
-LOCAL_STATIC_LIBRARIES := libprotobuf-cpp-lite
-include $(BUILD_STATIC_LIBRARY)
-
-# metrics daemon.
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := metrics_daemon
-LOCAL_C_INCLUDES := $(metrics_includes) \
- external/libchromeos
-LOCAL_CFLAGS := $(metrics_CFLAGS)
-LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
-LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
- libmetrics \
- libprotobuf-cpp-lite \
- libchromeos-http \
- libchromeos-dbus \
- libdbus
-LOCAL_SRC_FILES := $(metrics_daemon_sources)
-LOCAL_STATIC_LIBRARIES := metrics_daemon_protos
-include $(BUILD_EXECUTABLE)
-
-endif # HOST_OS == linux
diff --git a/metricsd/OWNERS b/metricsd/OWNERS
deleted file mode 100644
index 7f5e50d..0000000
--- a/metricsd/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-semenzato@chromium.org
-derat@chromium.org
-bsimonnet@chromium.org
diff --git a/metricsd/README b/metricsd/README
deleted file mode 100644
index d4c9a0e..0000000
--- a/metricsd/README
+++ /dev/null
@@ -1,150 +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.
-
-================================================================================
-
-The Chrome OS "metrics" package contains utilities for client-side user metric
-collection.
-When Chrome is installed, Chrome will take care of aggregating and uploading the
-metrics to the UMA server.
-When Chrome is not installed (embedded build) and the metrics_uploader USE flag
-is set, metrics_daemon will aggregate and upload the metrics itself.
-
-
-================================================================================
-The Metrics Library: libmetrics
-================================================================================
-
-libmetrics is a small library that implements the basic C and C++ API for
-metrics collection. All metrics collection is funneled through this library. The
-easiest and recommended way for a client-side module to collect user metrics is
-to link libmetrics and use its APIs to send metrics to Chrome for transport to
-UMA. In order to use the library in a module, you need to do the following:
-
-- Add a dependence (DEPEND and RDEPEND) on chromeos-base/metrics to the module's
- ebuild.
-
-- Link the module with libmetrics (for example, by passing -lmetrics to the
- module's link command). Both libmetrics.so and libmetrics.a are built and
- installed under $SYSROOT/usr/lib/. Note that by default -lmetrics will link
- against libmetrics.so, which is preferred.
-
-- To access the metrics library API in the module, include the
- <metrics/metrics_library.h> header file. The file is installed in
- $SYSROOT/usr/include/ when the metrics library is built and installed.
-
-- The API is documented in metrics_library.h under src/platform/metrics/. Before
- using the API methods, a MetricsLibrary object needs to be constructed and
- initialized through its Init method.
-
- For more information on the C API see c_metrics_library.h.
-
-- Samples are sent to Chrome only if the "/home/chronos/Consent To Send Stats"
- file exists or the metrics are declared enabled in the policy file (see the
- AreMetricsEnabled API method).
-
-- On the target platform, shortly after the sample is sent, it should be visible
- in Chrome through "about:histograms".
-
-
-================================================================================
-Histogram Naming Convention
-================================================================================
-
-Use TrackerArea.MetricName. For example:
-
-Platform.DailyUseTime
-Network.TimeToDrop
-
-
-================================================================================
-Server Side
-================================================================================
-
-If the histogram data is visible in about:histograms, it will be sent by an
-official Chrome build to UMA, assuming the user has opted into metrics
-collection. To make the histogram visible on "chromedashboard", the histogram
-description XML file needs to be updated (steps 2 and 3 after following the
-"Details on how to add your own histograms" link under the Histograms tab).
-Include the string "Chrome OS" in the histogram description so that it's easier
-to distinguish Chrome OS specific metrics from general Chrome histograms.
-
-The UMA server logs and keeps the collected field data even if the metric's name
-is not added to the histogram XML. However, the dashboard histogram for that
-metric will show field data as of the histogram XML update date; it will not
-include data for older dates. If past data needs to be displayed, manual
-server-side intervention is required. In other words, one should assume that
-field data collection starts only after the histogram XML has been updated.
-
-
-================================================================================
-The Metrics Client: metrics_client
-================================================================================
-
-metrics_client is a simple shell command-line utility for sending histogram
-samples and user actions. It's installed under /usr/bin on the target platform
-and uses libmetrics to send the data to Chrome. The utility is useful for
-generating metrics from shell scripts.
-
-For usage information and command-line options, run "metrics_client" on the
-target platform or look for "Usage:" in metrics_client.cc.
-
-
-================================================================================
-The Metrics Daemon: metrics_daemon
-================================================================================
-
-metrics_daemon is a daemon that runs in the background on the target platform
-and is intended for passive or ongoing metrics collection, or metrics collection
-requiring feedback from multiple modules. For example, it listens to D-Bus
-signals related to the user session and screen saver states to determine if the
-user is actively using the device or not and generates the corresponding
-data. The metrics daemon uses libmetrics to send the data to Chrome.
-
-The recommended way to generate metrics data from a module is to link and use
-libmetrics directly. However, the module could instead send signals to or
-communicate in some alternative way with the metrics daemon. Then the metrics
-daemon needs to monitor for the relevant events and take appropriate action --
-for example, aggregate data and send the histogram samples.
-
-
-================================================================================
-FAQ
-================================================================================
-
-Q. What should my histogram's |min| and |max| values be set at?
-
-A. You should set the values to a range that covers the vast majority of samples
- that would appear in the field. Note that samples below the |min| will still
- be collected in the underflow bucket and samples above the |max| will end up
- in the overflow bucket. Also, the reported mean of the data will be correct
- regardless of the range.
-
-Q. How many buckets should I use in my histogram?
-
-A. You should allocate as many buckets as necessary to perform proper analysis
- on the collected data. Note, however, that the memory allocated in Chrome for
- each histogram is proportional to the number of buckets. Therefore, it is
- strongly recommended to keep this number low (e.g., 50 is normal, while 100
- is probably high).
-
-Q. When should I use an enumeration (linear) histogram vs. a regular
- (exponential) histogram?
-
-A. Enumeration histograms should really be used only for sampling enumerated
- events and, in some cases, percentages. Normally, you should use a regular
- histogram with exponential bucket layout that provides higher resolution at
- the low end of the range and lower resolution at the high end. Regular
- histograms are generally used for collecting performance data (e.g., timing,
- memory usage, power) as well as aggregated event counts.
diff --git a/metricsd/WATCHLISTS b/metricsd/WATCHLISTS
deleted file mode 100644
index a051f35..0000000
--- a/metricsd/WATCHLISTS
+++ /dev/null
@@ -1,16 +0,0 @@
-# See http://dev.chromium.org/developers/contributing-code/watchlists for
-# a description of this file's format.
-# Please keep these keys in alphabetical order.
-
-{
- 'WATCHLIST_DEFINITIONS': {
- 'all': {
- 'filepath': '.',
- },
- },
- 'WATCHLISTS': {
- 'all': ['petkov@chromium.org',
- 'semenzato@chromium.org',
- 'sosa@chromium.org']
- },
-}
diff --git a/metricsd/c_metrics_library.cc b/metricsd/c_metrics_library.cc
deleted file mode 100644
index 0503876..0000000
--- a/metricsd/c_metrics_library.cc
+++ /dev/null
@@ -1,90 +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.
- */
-
-//
-// C wrapper to libmetrics
-//
-
-#include "metrics/c_metrics_library.h"
-
-#include <string>
-
-#include "metrics/metrics_library.h"
-
-extern "C" CMetricsLibrary CMetricsLibraryNew(void) {
- MetricsLibrary* lib = new MetricsLibrary;
- return reinterpret_cast<CMetricsLibrary>(lib);
-}
-
-extern "C" void CMetricsLibraryDelete(CMetricsLibrary handle) {
- MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
- delete lib;
-}
-
-extern "C" void CMetricsLibraryInit(CMetricsLibrary handle) {
- MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
- if (lib != NULL)
- lib->Init();
-}
-
-extern "C" int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
- const char* name, int sample,
- int min, int max, int nbuckets) {
- MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
- if (lib == NULL)
- return 0;
- return lib->SendToUMA(std::string(name), sample, min, max, nbuckets);
-}
-
-extern "C" int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
- const char* name, int sample,
- int max) {
- MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
- if (lib == NULL)
- return 0;
- return lib->SendEnumToUMA(std::string(name), sample, max);
-}
-
-extern "C" int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle,
- const char* name, int sample) {
- MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
- if (lib == NULL)
- return 0;
- return lib->SendSparseToUMA(std::string(name), sample);
-}
-
-extern "C" int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
- const char* action) {
- MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
- if (lib == NULL)
- return 0;
- return lib->SendUserActionToUMA(std::string(action));
-}
-
-extern "C" int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle,
- const char* crash_kind) {
- MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
- if (lib == NULL)
- return 0;
- return lib->SendCrashToUMA(crash_kind);
-}
-
-extern "C" int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle) {
- MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
- if (lib == NULL)
- return 0;
- return lib->AreMetricsEnabled();
-}
diff --git a/metricsd/constants.h b/metricsd/constants.h
deleted file mode 100644
index d65e0e0..0000000
--- a/metricsd/constants.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef METRICS_CONSTANTS_H_
-#define METRICS_CONSTANTS_H_
-
-namespace metrics {
-static const char kMetricsDirectory[] = "/data/misc/metrics/";
-static const char kMetricsEventsFilePath[] = "/data/misc/metrics/uma-events";
-static const char kMetricsGUIDFilePath[] = "/data/misc/metrics/Sysinfo.GUID";
-static const char kMetricsServer[] = "http://clients4.google.com/uma/v2";
-static const char kConsentFilePath[] = "/data/misc/metrics/enabled";
-static const char kDefaultVersion[] = "0.0.0.0";
-} // namespace metrics
-
-#endif // METRICS_CONSTANTS_H_
diff --git a/metricsd/include/metrics/c_metrics_library.h b/metricsd/include/metrics/c_metrics_library.h
deleted file mode 100644
index 4e7e666..0000000
--- a/metricsd/include/metrics/c_metrics_library.h
+++ /dev/null
@@ -1,61 +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 METRICS_C_METRICS_LIBRARY_H_
-#define METRICS_C_METRICS_LIBRARY_H_
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-typedef struct CMetricsLibraryOpaque* CMetricsLibrary;
-
-// C wrapper for MetricsLibrary::MetricsLibrary.
-CMetricsLibrary CMetricsLibraryNew(void);
-
-// C wrapper for MetricsLibrary::~MetricsLibrary.
-void CMetricsLibraryDelete(CMetricsLibrary handle);
-
-// C wrapper for MetricsLibrary::Init.
-void CMetricsLibraryInit(CMetricsLibrary handle);
-
-// C wrapper for MetricsLibrary::SendToUMA.
-int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
- const char* name, int sample,
- int min, int max, int nbuckets);
-
-// C wrapper for MetricsLibrary::SendEnumToUMA.
-int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
- const char* name, int sample, int max);
-
-// C wrapper for MetricsLibrary::SendSparseToUMA.
-int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle,
- const char* name, int sample);
-
-// C wrapper for MetricsLibrary::SendUserActionToUMA.
-int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
- const char* action);
-
-// C wrapper for MetricsLibrary::SendCrashToUMA.
-int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle,
- const char* crash_kind);
-
-// C wrapper for MetricsLibrary::AreMetricsEnabled.
-int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle);
-
-#if defined(__cplusplus)
-}
-#endif
-#endif // METRICS_C_METRICS_LIBRARY_H_
diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
deleted file mode 100644
index 4917a7c..0000000
--- a/metricsd/include/metrics/metrics_library.h
+++ /dev/null
@@ -1,152 +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 METRICS_METRICS_LIBRARY_H_
-#define METRICS_METRICS_LIBRARY_H_
-
-#include <sys/types.h>
-#include <string>
-#include <unistd.h>
-
-#include <base/compiler_specific.h>
-#include <base/macros.h>
-#include <base/memory/scoped_ptr.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-class MetricsLibraryInterface {
- public:
- virtual void Init() = 0;
- virtual bool AreMetricsEnabled() = 0;
- virtual bool SendToUMA(const std::string& name, int sample,
- int min, int max, int nbuckets) = 0;
- virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0;
- virtual bool SendSparseToUMA(const std::string& name, int sample) = 0;
- virtual bool SendUserActionToUMA(const std::string& action) = 0;
- virtual ~MetricsLibraryInterface() {}
-};
-
-// Library used to send metrics to Chrome/UMA.
-class MetricsLibrary : public MetricsLibraryInterface {
- public:
- MetricsLibrary();
- virtual ~MetricsLibrary();
-
- // Initializes the library.
- void Init() override;
-
- // Returns whether or not the machine is running in guest mode.
- bool IsGuestMode();
-
- // Returns whether or not metrics collection is enabled.
- bool AreMetricsEnabled() override;
-
- // Sends histogram data to Chrome for transport to UMA and returns
- // true on success. This method results in the equivalent of an
- // asynchronous non-blocking RPC to UMA_HISTOGRAM_CUSTOM_COUNTS
- // inside Chrome (see base/histogram.h).
- //
- // |sample| is the sample value to be recorded (|min| <= |sample| < |max|).
- // |min| is the minimum value of the histogram samples (|min| > 0).
- // |max| is the maximum value of the histogram samples.
- // |nbuckets| is the number of histogram buckets.
- // [0,min) is the implicit underflow bucket.
- // [|max|,infinity) is the implicit overflow bucket.
- //
- // Note that the memory allocated in Chrome for each histogram is
- // proportional to the number of buckets. Therefore, it is strongly
- // recommended to keep this number low (e.g., 50 is normal, while
- // 100 is high).
- bool SendToUMA(const std::string& name, int sample,
- int min, int max, int nbuckets) override;
-
- // Sends linear histogram data to Chrome for transport to UMA and
- // returns true on success. This method results in the equivalent of
- // an asynchronous non-blocking RPC to UMA_HISTOGRAM_ENUMERATION
- // inside Chrome (see base/histogram.h).
- //
- // |sample| is the sample value to be recorded (1 <= |sample| < |max|).
- // |max| is the maximum value of the histogram samples.
- // 0 is the implicit underflow bucket.
- // [|max|,infinity) is the implicit overflow bucket.
- //
- // An enumeration histogram requires |max| + 1 number of
- // buckets. Note that the memory allocated in Chrome for each
- // histogram is proportional to the number of buckets. Therefore, it
- // is strongly recommended to keep this number low (e.g., 50 is
- // normal, while 100 is high).
- bool SendEnumToUMA(const std::string& name, int sample, int max) override;
-
- // Sends sparse histogram sample to Chrome for transport to UMA. Returns
- // true on success.
- //
- // |sample| is the 32-bit integer value to be recorded.
- bool SendSparseToUMA(const std::string& name, int sample) override;
-
- // Sends a user action to Chrome for transport to UMA and returns true on
- // success. This method results in the equivalent of an asynchronous
- // non-blocking RPC to UserMetrics::RecordAction. The new metric must be
- // added to chrome/tools/extract_actions.py in the Chromium repository, which
- // should then be run to generate a hash for the new action.
- //
- // Until http://crosbug.com/11125 is fixed, the metric must also be added to
- // chrome/browser/chromeos/external_metrics.cc.
- //
- // |action| is the user-generated event (e.g., "MuteKeyPressed").
- bool SendUserActionToUMA(const std::string& action) override;
-
- // Sends a signal to UMA that a crash of the given |crash_kind|
- // has occurred. Used by UMA to generate stability statistics.
- bool SendCrashToUMA(const char *crash_kind);
-
- // Sends a "generic Chrome OS event" to UMA. This is an event name
- // that is translated into an enumerated histogram entry. Event names
- // are added to metrics_library.cc. Optionally, they can be added
- // to histograms.xml---but part of the reason for this is to simplify
- // the addition of events (at the cost of having to look them up by
- // number in the histograms dashboard).
- bool SendCrosEventToUMA(const std::string& event);
-
- private:
- friend class CMetricsLibraryTest;
- friend class MetricsLibraryTest;
- FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled);
- FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage);
- FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong);
- FRIEND_TEST(MetricsLibraryTest, IsDeviceMounted);
- FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome);
- FRIEND_TEST(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation);
-
- // Sets |*result| to whether or not the |mounts_file| indicates that
- // the |device_name| is currently mounted. Uses |buffer| of
- // |buffer_size| to read the file. Returns false if any error.
- bool IsDeviceMounted(const char* device_name,
- const char* mounts_file,
- char* buffer, int buffer_size,
- bool* result);
-
- // Time at which we last checked if metrics were enabled.
- static time_t cached_enabled_time_;
-
- // Cached state of whether or not metrics were enabled.
- static bool cached_enabled_;
-
- std::string uma_events_file_;
- std::string consent_file_;
-
- DISALLOW_COPY_AND_ASSIGN(MetricsLibrary);
-};
-
-#endif // METRICS_METRICS_LIBRARY_H_
diff --git a/metricsd/libmetrics-334380.gyp b/metricsd/libmetrics-334380.gyp
deleted file mode 100644
index 9771821..0000000
--- a/metricsd/libmetrics-334380.gyp
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- 'variables': {
- 'libbase_ver': 334380,
- },
- 'includes': [
- 'libmetrics.gypi',
- ],
-}
diff --git a/metricsd/libmetrics.gypi b/metricsd/libmetrics.gypi
deleted file mode 100644
index 5b90a55..0000000
--- a/metricsd/libmetrics.gypi
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- 'target_defaults': {
- 'variables': {
- 'deps': [
- 'libchrome-<(libbase_ver)',
- 'libchromeos-<(libbase_ver)',
- ]
- },
- 'cflags_cc': [
- '-fno-exceptions',
- ],
- },
- 'targets': [
- {
- 'target_name': 'libmetrics-<(libbase_ver)',
- 'type': 'shared_library',
- 'cflags': [
- '-fvisibility=default',
- ],
- 'libraries+': [
- '-lpolicy-<(libbase_ver)',
- ],
- 'sources': [
- 'c_metrics_library.cc',
- 'metrics_library.cc',
- 'serialization/metric_sample.cc',
- 'serialization/serialization_utils.cc',
- 'timer.cc',
- ],
- 'include_dirs': ['.'],
- },
- ],
-}
diff --git a/metricsd/libmetrics.pc.in b/metricsd/libmetrics.pc.in
deleted file mode 100644
index 233f318..0000000
--- a/metricsd/libmetrics.pc.in
+++ /dev/null
@@ -1,7 +0,0 @@
-bslot=@BSLOT@
-
-Name: libmetrics
-Description: Chrome OS metrics library
-Version: ${bslot}
-Requires.private: libchrome-${bslot}
-Libs: -lmetrics-${bslot}
diff --git a/metricsd/metrics.gyp b/metricsd/metrics.gyp
deleted file mode 100644
index 276ec78..0000000
--- a/metricsd/metrics.gyp
+++ /dev/null
@@ -1,184 +0,0 @@
-{
- 'target_defaults': {
- 'variables': {
- 'deps': [
- 'dbus-1',
- 'libchrome-<(libbase_ver)',
- 'libchromeos-<(libbase_ver)',
- ]
- },
- 'cflags_cc': [
- '-fno-exceptions',
- ],
- },
- 'targets': [
- {
- 'target_name': 'libmetrics_daemon',
- 'type': 'static_library',
- 'dependencies': [
- '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
- 'libupload_service',
- 'metrics_proto',
- ],
- 'link_settings': {
- 'libraries': [
- '-lrootdev',
- ],
- },
- 'sources': [
- 'persistent_integer.cc',
- 'metrics_daemon.cc',
- 'metrics_daemon_main.cc',
- ],
- 'include_dirs': ['.'],
- },
- {
- 'target_name': 'metrics_client',
- 'type': 'executable',
- 'dependencies': [
- '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
- ],
- 'sources': [
- 'metrics_client.cc',
- ]
- },
- {
- 'target_name': 'libupload_service',
- 'type': 'static_library',
- 'dependencies': [
- 'metrics_proto',
- '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
- ],
- 'link_settings': {
- 'libraries': [
- '-lvboot_host',
- ],
- },
- 'variables': {
- 'exported_deps': [
- 'protobuf-lite',
- ],
- 'deps': [
- '<@(exported_deps)',
- ],
- },
- 'all_dependent_settings': {
- 'variables': {
- 'deps+': [
- '<@(exported_deps)',
- ],
- },
- },
- 'sources': [
- 'uploader/upload_service.cc',
- 'uploader/metrics_hashes.cc',
- 'uploader/metrics_log.cc',
- 'uploader/metrics_log_base.cc',
- 'uploader/system_profile_cache.cc',
- 'uploader/sender_http.cc',
- ],
- 'include_dirs': ['.']
- },
- {
- 'target_name': 'metrics_proto',
- 'type': 'static_library',
- 'variables': {
- 'proto_in_dir': 'uploader/proto',
- 'proto_out_dir': 'include/metrics/uploader/proto',
- },
- 'sources': [
- '<(proto_in_dir)/chrome_user_metrics_extension.proto',
- '<(proto_in_dir)/histogram_event.proto',
- '<(proto_in_dir)/system_profile.proto',
- '<(proto_in_dir)/user_action_event.proto',
- ],
- 'includes': [
- '../common-mk/protoc.gypi'
- ],
- },
- ],
- 'conditions': [
- ['USE_passive_metrics == 1', {
- 'targets': [
- {
- 'target_name': 'metrics_daemon',
- 'type': 'executable',
- 'dependencies': ['libmetrics_daemon'],
- },
- ],
- }],
- ['USE_test == 1', {
- 'targets': [
- {
- 'target_name': 'persistent_integer_test',
- 'type': 'executable',
- 'includes': ['../common-mk/common_test.gypi'],
- 'sources': [
- 'persistent_integer.cc',
- 'persistent_integer_test.cc',
- ]
- },
- {
- 'target_name': 'metrics_library_test',
- 'type': 'executable',
- 'dependencies': [
- '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
- ],
- 'includes': ['../common-mk/common_test.gypi'],
- 'sources': [
- 'metrics_library_test.cc',
- 'serialization/serialization_utils_unittest.cc',
- ],
- 'link_settings': {
- 'libraries': [
- '-lpolicy-<(libbase_ver)',
- ]
- }
- },
- {
- 'target_name': 'timer_test',
- 'type': 'executable',
- 'includes': ['../common-mk/common_test.gypi'],
- 'sources': [
- 'timer.cc',
- 'timer_test.cc',
- ]
- },
- {
- 'target_name': 'upload_service_test',
- 'type': 'executable',
- 'sources': [
- 'persistent_integer.cc',
- 'uploader/metrics_hashes_unittest.cc',
- 'uploader/metrics_log_base_unittest.cc',
- 'uploader/mock/sender_mock.cc',
- 'uploader/upload_service_test.cc',
- ],
- 'dependencies': [
- 'libupload_service',
- ],
- 'includes':[
- '../common-mk/common_test.gypi',
- ],
- 'include_dirs': ['.']
- },
- ],
- }],
- ['USE_passive_metrics == 1 and USE_test == 1', {
- 'targets': [
- {
- 'target_name': 'metrics_daemon_test',
- 'type': 'executable',
- 'dependencies': [
- 'libmetrics_daemon',
- ],
- 'includes': ['../common-mk/common_test.gypi'],
- 'sources': [
- 'metrics_daemon_test.cc',
- ],
- 'include_dirs': ['.'],
- },
- ],
- }],
- ]
-}
diff --git a/metricsd/metrics_client.cc b/metricsd/metrics_client.cc
deleted file mode 100644
index 57e96c2..0000000
--- a/metricsd/metrics_client.cc
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cstdio>
-#include <cstdlib>
-
-#include "metrics/metrics_library.h"
-
-enum Mode {
- kModeSendSample,
- kModeSendEnumSample,
- kModeSendSparseSample,
- kModeSendUserAction,
- kModeSendCrosEvent,
- kModeHasConsent,
- kModeIsGuestMode,
-};
-
-void ShowUsage() {
- fprintf(stderr,
- "Usage: metrics_client [-t] name sample min max nbuckets\n"
- " metrics_client -e name sample max\n"
- " metrics_client -s name sample\n"
- " metrics_client -v event\n"
- " metrics_client -u action\n"
- " metrics_client [-cg]\n"
- "\n"
- " default: send metric with integer values \n"
- " |min| > 0, |min| <= sample < |max|\n"
- " -c: return exit status 0 if user consents to stats, 1 otherwise,\n"
- " in guest mode always return 1\n"
- " -e: send linear/enumeration histogram data\n"
- " -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
- " -s: send a sparse histogram sample\n"
- " -t: convert sample from double seconds to int milliseconds\n"
- " -u: send a user action to Chrome\n"
- " -v: send a Platform.CrOSEvent enum histogram sample\n");
- exit(1);
-}
-
-static int ParseInt(const char *arg) {
- char *endptr;
- int value = strtol(arg, &endptr, 0);
- if (*endptr != '\0') {
- fprintf(stderr, "metrics client: bad integer \"%s\"\n", arg);
- ShowUsage();
- }
- return value;
-}
-
-static double ParseDouble(const char *arg) {
- char *endptr;
- double value = strtod(arg, &endptr);
- if (*endptr != '\0') {
- fprintf(stderr, "metrics client: bad double \"%s\"\n", arg);
- ShowUsage();
- }
- return value;
-}
-
-static int SendStats(char* argv[],
- int name_index,
- enum Mode mode,
- bool secs_to_msecs) {
- const char* name = argv[name_index];
- int sample;
- if (secs_to_msecs) {
- sample = static_cast<int>(ParseDouble(argv[name_index + 1]) * 1000.0);
- } else {
- sample = ParseInt(argv[name_index + 1]);
- }
-
- MetricsLibrary metrics_lib;
- metrics_lib.Init();
- if (mode == kModeSendSparseSample) {
- metrics_lib.SendSparseToUMA(name, sample);
- } else if (mode == kModeSendEnumSample) {
- int max = ParseInt(argv[name_index + 2]);
- metrics_lib.SendEnumToUMA(name, sample, max);
- } else {
- int min = ParseInt(argv[name_index + 2]);
- int max = ParseInt(argv[name_index + 3]);
- int nbuckets = ParseInt(argv[name_index + 4]);
- metrics_lib.SendToUMA(name, sample, min, max, nbuckets);
- }
- return 0;
-}
-
-static int SendUserAction(char* argv[], int action_index) {
- const char* action = argv[action_index];
- MetricsLibrary metrics_lib;
- metrics_lib.Init();
- metrics_lib.SendUserActionToUMA(action);
- return 0;
-}
-
-static int SendCrosEvent(char* argv[], int action_index) {
- const char* event = argv[action_index];
- bool result;
- MetricsLibrary metrics_lib;
- metrics_lib.Init();
- result = metrics_lib.SendCrosEventToUMA(event);
- if (!result) {
- fprintf(stderr, "metrics_client: could not send event %s\n", event);
- return 1;
- }
- return 0;
-}
-
-static int HasConsent() {
- MetricsLibrary metrics_lib;
- metrics_lib.Init();
- return metrics_lib.AreMetricsEnabled() ? 0 : 1;
-}
-
-static int IsGuestMode() {
- MetricsLibrary metrics_lib;
- metrics_lib.Init();
- return metrics_lib.IsGuestMode() ? 0 : 1;
-}
-
-int main(int argc, char** argv) {
- enum Mode mode = kModeSendSample;
- bool secs_to_msecs = false;
-
- // Parse arguments
- int flag;
- while ((flag = getopt(argc, argv, "abcegstuv")) != -1) {
- switch (flag) {
- case 'c':
- mode = kModeHasConsent;
- break;
- case 'e':
- mode = kModeSendEnumSample;
- break;
- case 'g':
- mode = kModeIsGuestMode;
- break;
- case 's':
- mode = kModeSendSparseSample;
- break;
- case 't':
- secs_to_msecs = true;
- break;
- case 'u':
- mode = kModeSendUserAction;
- break;
- case 'v':
- mode = kModeSendCrosEvent;
- break;
- default:
- ShowUsage();
- break;
- }
- }
- int arg_index = optind;
-
- int expected_args = 0;
- if (mode == kModeSendSample)
- expected_args = 5;
- else if (mode == kModeSendEnumSample)
- expected_args = 3;
- else if (mode == kModeSendSparseSample)
- expected_args = 2;
- else if (mode == kModeSendUserAction)
- expected_args = 1;
- else if (mode == kModeSendCrosEvent)
- expected_args = 1;
-
- if ((arg_index + expected_args) != argc) {
- ShowUsage();
- }
-
- switch (mode) {
- case kModeSendSample:
- case kModeSendEnumSample:
- case kModeSendSparseSample:
- if ((mode != kModeSendSample) && secs_to_msecs) {
- ShowUsage();
- }
- return SendStats(argv,
- arg_index,
- mode,
- secs_to_msecs);
- case kModeSendUserAction:
- return SendUserAction(argv, arg_index);
- case kModeSendCrosEvent:
- return SendCrosEvent(argv, arg_index);
- case kModeHasConsent:
- return HasConsent();
- case kModeIsGuestMode:
- return IsGuestMode();
- default:
- ShowUsage();
- return 0;
- }
-}
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
deleted file mode 100644
index 069f68e..0000000
--- a/metricsd/metrics_daemon.cc
+++ /dev/null
@@ -1,1117 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "metrics_daemon.h"
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <math.h>
-#include <string.h>
-#include <sysexits.h>
-#include <time.h>
-
-#include <base/bind.h>
-#include <base/files/file_path.h>
-#include <base/files/file_util.h>
-#include <base/hash.h>
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <base/sys_info.h>
-#include <dbus/dbus.h>
-#include <dbus/message.h>
-
-#include "constants.h"
-#include "uploader/upload_service.h"
-
-using base::FilePath;
-using base::StringPrintf;
-using base::Time;
-using base::TimeDelta;
-using base::TimeTicks;
-using chromeos_metrics::PersistentInteger;
-using std::map;
-using std::string;
-using std::vector;
-
-namespace {
-
-#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
-
-const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
-const char kCrashReporterUserCrashSignal[] = "UserCrash";
-const char kCrashReporterMatchRule[] =
- "type='signal',interface='%s',path='/',member='%s'";
-
-const int kSecondsPerMinute = 60;
-const int kMinutesPerHour = 60;
-const int kHoursPerDay = 24;
-const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
-const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
-const int kDaysPerWeek = 7;
-const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
-
-// Interval between calls to UpdateStats().
-const uint32_t kUpdateStatsIntervalMs = 300000;
-
-const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected";
-const char kUncleanShutdownDetectedFile[] =
- "/var/run/unclean-shutdown-detected";
-
-} // namespace
-
-// disk stats metrics
-
-// The {Read,Write}Sectors numbers are in sectors/second.
-// A sector is usually 512 bytes.
-
-const char MetricsDaemon::kMetricReadSectorsLongName[] =
- "Platform.ReadSectorsLong";
-const char MetricsDaemon::kMetricWriteSectorsLongName[] =
- "Platform.WriteSectorsLong";
-const char MetricsDaemon::kMetricReadSectorsShortName[] =
- "Platform.ReadSectorsShort";
-const char MetricsDaemon::kMetricWriteSectorsShortName[] =
- "Platform.WriteSectorsShort";
-
-const int MetricsDaemon::kMetricStatsShortInterval = 1; // seconds
-const int MetricsDaemon::kMetricStatsLongInterval = 30; // seconds
-
-const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
-
-// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
-// sectors.
-const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
-const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
-// Page size is 4k, sector size is 0.5k. We're not interested in page fault
-// rates that the disk cannot sustain.
-const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
-const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
-
-// Major page faults, i.e. the ones that require data to be read from disk.
-
-const char MetricsDaemon::kMetricPageFaultsLongName[] =
- "Platform.PageFaultsLong";
-const char MetricsDaemon::kMetricPageFaultsShortName[] =
- "Platform.PageFaultsShort";
-
-// Swap in and Swap out
-
-const char MetricsDaemon::kMetricSwapInLongName[] =
- "Platform.SwapInLong";
-const char MetricsDaemon::kMetricSwapInShortName[] =
- "Platform.SwapInShort";
-
-const char MetricsDaemon::kMetricSwapOutLongName[] =
- "Platform.SwapOutLong";
-const char MetricsDaemon::kMetricSwapOutShortName[] =
- "Platform.SwapOutShort";
-
-const char MetricsDaemon::kMetricsProcStatFileName[] = "/proc/stat";
-const int MetricsDaemon::kMetricsProcStatFirstLineItemsCount = 11;
-
-// Thermal CPU throttling.
-
-const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
- "Platform.CpuFrequencyThermalScaling";
-
-// Zram sysfs entries.
-
-const char MetricsDaemon::kComprDataSizeName[] = "compr_data_size";
-const char MetricsDaemon::kOrigDataSizeName[] = "orig_data_size";
-const char MetricsDaemon::kZeroPagesName[] = "zero_pages";
-
-// Memory use stats collection intervals. We collect some memory use interval
-// at these intervals after boot, and we stop collecting after the last one,
-// with the assumption that in most cases the memory use won't change much
-// after that.
-static const int kMemuseIntervals[] = {
- 1 * kSecondsPerMinute, // 1 minute mark
- 4 * kSecondsPerMinute, // 5 minute mark
- 25 * kSecondsPerMinute, // 0.5 hour mark
- 120 * kSecondsPerMinute, // 2.5 hour mark
- 600 * kSecondsPerMinute, // 12.5 hour mark
-};
-
-MetricsDaemon::MetricsDaemon()
- : memuse_final_time_(0),
- memuse_interval_index_(0),
- read_sectors_(0),
- write_sectors_(0),
- vmstats_(),
- stats_state_(kStatsShort),
- stats_initial_time_(0),
- ticks_per_second_(0),
- latest_cpu_use_ticks_(0) {}
-
-MetricsDaemon::~MetricsDaemon() {
-}
-
-double MetricsDaemon::GetActiveTime() {
- struct timespec ts;
- int r = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (r < 0) {
- PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
- return 0;
- } else {
- return ts.tv_sec + static_cast<double>(ts.tv_nsec) / (1000 * 1000 * 1000);
- }
-}
-
-int MetricsDaemon::Run() {
- if (CheckSystemCrash(kKernelCrashDetectedFile)) {
- ProcessKernelCrash();
- }
-
- if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
- ProcessUncleanShutdown();
- }
-
- // On OS version change, clear version stats (which are reported daily).
- int32_t version = GetOsVersionHash();
- if (version_cycle_->Get() != version) {
- version_cycle_->Set(version);
- kernel_crashes_version_count_->Set(0);
- version_cumulative_active_use_->Set(0);
- version_cumulative_cpu_use_->Set(0);
- }
-
- return chromeos::DBusDaemon::Run();
-}
-
-void MetricsDaemon::RunUploaderTest() {
- upload_service_.reset(new UploadService(new SystemProfileCache(true,
- config_root_),
- metrics_lib_,
- server_));
- upload_service_->Init(upload_interval_, metrics_file_);
- upload_service_->UploadEvent();
-}
-
-uint32_t MetricsDaemon::GetOsVersionHash() {
- static uint32_t cached_version_hash = 0;
- static bool version_hash_is_cached = false;
- if (version_hash_is_cached)
- return cached_version_hash;
- version_hash_is_cached = true;
- std::string version = metrics::kDefaultVersion;
- // The version might not be set for development devices. In this case, use the
- // zero version.
- base::SysInfo::GetLsbReleaseValue("BRILLO_VERSION", &version);
- cached_version_hash = base::Hash(version);
- if (testing_) {
- cached_version_hash = 42; // return any plausible value for the hash
- }
- return cached_version_hash;
-}
-
-void MetricsDaemon::Init(bool testing,
- bool uploader_active,
- bool dbus_enabled,
- MetricsLibraryInterface* metrics_lib,
- const string& vmstats_path,
- const string& scaling_max_freq_path,
- const string& cpuinfo_max_freq_path,
- const base::TimeDelta& upload_interval,
- const string& server,
- const string& metrics_file,
- const string& config_root) {
- testing_ = testing;
- uploader_active_ = uploader_active;
- dbus_enabled_ = dbus_enabled;
- config_root_ = config_root;
- DCHECK(metrics_lib != nullptr);
- metrics_lib_ = metrics_lib;
-
- upload_interval_ = upload_interval;
- server_ = server;
- metrics_file_ = metrics_file;
-
- // Get ticks per second (HZ) on this system.
- // Sysconf cannot fail, so no sanity checks are needed.
- ticks_per_second_ = sysconf(_SC_CLK_TCK);
-
- daily_active_use_.reset(
- new PersistentInteger("Platform.DailyUseTime"));
- version_cumulative_active_use_.reset(
- new PersistentInteger("Platform.CumulativeDailyUseTime"));
- version_cumulative_cpu_use_.reset(
- new PersistentInteger("Platform.CumulativeCpuTime"));
-
- kernel_crash_interval_.reset(
- new PersistentInteger("Platform.KernelCrashInterval"));
- unclean_shutdown_interval_.reset(
- new PersistentInteger("Platform.UncleanShutdownInterval"));
- user_crash_interval_.reset(
- new PersistentInteger("Platform.UserCrashInterval"));
-
- any_crashes_daily_count_.reset(
- new PersistentInteger("Platform.AnyCrashesDaily"));
- any_crashes_weekly_count_.reset(
- new PersistentInteger("Platform.AnyCrashesWeekly"));
- user_crashes_daily_count_.reset(
- new PersistentInteger("Platform.UserCrashesDaily"));
- user_crashes_weekly_count_.reset(
- new PersistentInteger("Platform.UserCrashesWeekly"));
- kernel_crashes_daily_count_.reset(
- new PersistentInteger("Platform.KernelCrashesDaily"));
- kernel_crashes_weekly_count_.reset(
- new PersistentInteger("Platform.KernelCrashesWeekly"));
- kernel_crashes_version_count_.reset(
- new PersistentInteger("Platform.KernelCrashesSinceUpdate"));
- unclean_shutdowns_daily_count_.reset(
- new PersistentInteger("Platform.UncleanShutdownsDaily"));
- unclean_shutdowns_weekly_count_.reset(
- new PersistentInteger("Platform.UncleanShutdownsWeekly"));
-
- daily_cycle_.reset(new PersistentInteger("daily.cycle"));
- weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
- version_cycle_.reset(new PersistentInteger("version.cycle"));
-
- vmstats_path_ = vmstats_path;
- scaling_max_freq_path_ = scaling_max_freq_path;
- cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
-}
-
-int MetricsDaemon::OnInit() {
- int return_code = dbus_enabled_ ? chromeos::DBusDaemon::OnInit() :
- chromeos::Daemon::OnInit();
- if (return_code != EX_OK)
- return return_code;
-
- if (testing_)
- return EX_OK;
-
- if (dbus_enabled_) {
- bus_->AssertOnDBusThread();
- CHECK(bus_->SetUpAsyncOperations());
-
- if (bus_->is_connected()) {
- const std::string match_rule =
- base::StringPrintf(kCrashReporterMatchRule,
- kCrashReporterInterface,
- kCrashReporterUserCrashSignal);
-
- bus_->AddFilterFunction(&MetricsDaemon::MessageFilter, this);
-
- DBusError error;
- dbus_error_init(&error);
- bus_->AddMatch(match_rule, &error);
-
- if (dbus_error_is_set(&error)) {
- LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
- << error.name << ": " << error.message;
- return EX_SOFTWARE;
- }
- } else {
- LOG(ERROR) << "DBus isn't connected.";
- return EX_UNAVAILABLE;
- }
- }
-
- if (uploader_active_) {
- upload_service_.reset(
- new UploadService(new SystemProfileCache(), metrics_lib_, server_));
- upload_service_->Init(upload_interval_, metrics_file_);
- }
-
- return EX_OK;
-}
-
-void MetricsDaemon::OnShutdown(int* return_code) {
- if (!testing_ && dbus_enabled_ && bus_->is_connected()) {
- const std::string match_rule =
- base::StringPrintf(kCrashReporterMatchRule,
- kCrashReporterInterface,
- kCrashReporterUserCrashSignal);
-
- bus_->RemoveFilterFunction(&MetricsDaemon::MessageFilter, this);
-
- DBusError error;
- dbus_error_init(&error);
- bus_->RemoveMatch(match_rule, &error);
-
- if (dbus_error_is_set(&error)) {
- LOG(ERROR) << "Failed to remove match rule \"" << match_rule << "\". Got "
- << error.name << ": " << error.message;
- }
- }
- chromeos::DBusDaemon::OnShutdown(return_code);
-}
-
-// static
-DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
- DBusMessage* message,
- void* user_data) {
- int message_type = dbus_message_get_type(message);
- if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
- DLOG(WARNING) << "unexpected message type " << message_type;
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- // Signal messages always have interfaces.
- const std::string interface(dbus_message_get_interface(message));
- const std::string member(dbus_message_get_member(message));
- DLOG(INFO) << "Got " << interface << "." << member << " D-Bus signal";
-
- MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
-
- DBusMessageIter iter;
- dbus_message_iter_init(message, &iter);
- if (interface == kCrashReporterInterface) {
- CHECK_EQ(member, kCrashReporterUserCrashSignal);
- daemon->ProcessUserCrash();
- } else {
- // Ignore messages from the bus itself.
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- return DBUS_HANDLER_RESULT_HANDLED;
-}
-
-// One might argue that parts of this should go into
-// chromium/src/base/sys_info_chromeos.c instead, but put it here for now.
-
-TimeDelta MetricsDaemon::GetIncrementalCpuUse() {
- FilePath proc_stat_path = FilePath(kMetricsProcStatFileName);
- std::string proc_stat_string;
- if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
- LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
- return TimeDelta();
- }
-
- std::vector<std::string> proc_stat_lines;
- base::SplitString(proc_stat_string, '\n', &proc_stat_lines);
- if (proc_stat_lines.empty()) {
- LOG(WARNING) << "cannot parse " << kMetricsProcStatFileName
- << ": " << proc_stat_string;
- return TimeDelta();
- }
- std::vector<std::string> proc_stat_totals;
- base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
-
- uint64_t user_ticks, user_nice_ticks, system_ticks;
- if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
- proc_stat_totals[0] != "cpu" ||
- !base::StringToUint64(proc_stat_totals[1], &user_ticks) ||
- !base::StringToUint64(proc_stat_totals[2], &user_nice_ticks) ||
- !base::StringToUint64(proc_stat_totals[3], &system_ticks)) {
- LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
- return TimeDelta(base::TimeDelta::FromSeconds(0));
- }
-
- uint64_t total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks;
-
- // Sanity check.
- if (total_cpu_use_ticks < latest_cpu_use_ticks_) {
- LOG(WARNING) << "CPU time decreasing from " << latest_cpu_use_ticks_
- << " to " << total_cpu_use_ticks;
- return TimeDelta();
- }
-
- uint64_t diff = total_cpu_use_ticks - latest_cpu_use_ticks_;
- latest_cpu_use_ticks_ = total_cpu_use_ticks;
- // Use microseconds to avoid significant truncations.
- return base::TimeDelta::FromMicroseconds(
- diff * 1000 * 1000 / ticks_per_second_);
-}
-
-void MetricsDaemon::ProcessUserCrash() {
- // Counts the active time up to now.
- UpdateStats(TimeTicks::Now(), Time::Now());
-
- // Reports the active use time since the last crash and resets it.
- SendCrashIntervalSample(user_crash_interval_);
-
- any_crashes_daily_count_->Add(1);
- any_crashes_weekly_count_->Add(1);
- user_crashes_daily_count_->Add(1);
- user_crashes_weekly_count_->Add(1);
-}
-
-void MetricsDaemon::ProcessKernelCrash() {
- // Counts the active time up to now.
- UpdateStats(TimeTicks::Now(), Time::Now());
-
- // Reports the active use time since the last crash and resets it.
- SendCrashIntervalSample(kernel_crash_interval_);
-
- any_crashes_daily_count_->Add(1);
- any_crashes_weekly_count_->Add(1);
- kernel_crashes_daily_count_->Add(1);
- kernel_crashes_weekly_count_->Add(1);
-
- kernel_crashes_version_count_->Add(1);
-}
-
-void MetricsDaemon::ProcessUncleanShutdown() {
- // Counts the active time up to now.
- UpdateStats(TimeTicks::Now(), Time::Now());
-
- // Reports the active use time since the last crash and resets it.
- SendCrashIntervalSample(unclean_shutdown_interval_);
-
- unclean_shutdowns_daily_count_->Add(1);
- unclean_shutdowns_weekly_count_->Add(1);
- any_crashes_daily_count_->Add(1);
- any_crashes_weekly_count_->Add(1);
-}
-
-bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
- FilePath crash_detected(crash_file);
- if (!base::PathExists(crash_detected))
- return false;
-
- // Deletes the crash-detected file so that the daemon doesn't report
- // another kernel crash in case it's restarted.
- base::DeleteFile(crash_detected, false); // not recursive
- return true;
-}
-
-void MetricsDaemon::StatsReporterInit() {
- DiskStatsReadStats(&read_sectors_, &write_sectors_);
- VmStatsReadStats(&vmstats_);
- // The first time around just run the long stat, so we don't delay boot.
- stats_state_ = kStatsLong;
- stats_initial_time_ = GetActiveTime();
- if (stats_initial_time_ < 0) {
- LOG(WARNING) << "not collecting disk stats";
- } else {
- ScheduleStatsCallback(kMetricStatsLongInterval);
- }
-}
-
-void MetricsDaemon::ScheduleStatsCallback(int wait) {
- if (testing_) {
- return;
- }
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::StatsCallback, base::Unretained(this)),
- base::TimeDelta::FromSeconds(wait));
-}
-
-bool MetricsDaemon::VmStatsParseStats(const char* stats,
- struct VmstatRecord* record) {
- // a mapping of string name to field in VmstatRecord and whether we found it
- struct mapping {
- const string name;
- uint64_t* value_p;
- bool found;
- } map[] =
- { { .name = "pgmajfault",
- .value_p = &record->page_faults_,
- .found = false },
- { .name = "pswpin",
- .value_p = &record->swap_in_,
- .found = false },
- { .name = "pswpout",
- .value_p = &record->swap_out_,
- .found = false }, };
-
- // Each line in the file has the form
- // <ID> <VALUE>
- // for instance:
- // nr_free_pages 213427
- vector<string> lines;
- Tokenize(stats, "\n", &lines);
- for (vector<string>::iterator it = lines.begin();
- it != lines.end(); ++it) {
- vector<string> tokens;
- base::SplitString(*it, ' ', &tokens);
- if (tokens.size() == 2) {
- for (unsigned int i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
- if (!tokens[0].compare(map[i].name)) {
- if (!base::StringToUint64(tokens[1], map[i].value_p))
- return false;
- map[i].found = true;
- }
- }
- } else {
- LOG(WARNING) << "unexpected vmstat format";
- }
- }
- // make sure we got all the stats
- for (unsigned i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
- if (map[i].found == false) {
- LOG(WARNING) << "vmstat missing " << map[i].name;
- return false;
- }
- }
- return true;
-}
-
-bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) {
- string value_string;
- FilePath* path = new FilePath(vmstats_path_);
- if (!base::ReadFileToString(*path, &value_string)) {
- delete path;
- LOG(WARNING) << "cannot read " << vmstats_path_;
- return false;
- }
- delete path;
- return VmStatsParseStats(value_string.c_str(), stats);
-}
-
-bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
- const FilePath sysfs_path(sysfs_file_name);
- string value_string;
- if (!base::ReadFileToString(sysfs_path, &value_string)) {
- LOG(WARNING) << "cannot read " << sysfs_path.value().c_str();
- return false;
- }
- if (!base::RemoveChars(value_string, "\n", &value_string)) {
- LOG(WARNING) << "no newline in " << value_string;
- // Continue even though the lack of newline is suspicious.
- }
- if (!base::StringToInt(value_string, value)) {
- LOG(WARNING) << "cannot convert " << value_string << " to int";
- return false;
- }
- return true;
-}
-
-void MetricsDaemon::SendCpuThrottleMetrics() {
- // |max_freq| is 0 only the first time through.
- static int max_freq = 0;
- if (max_freq == -1)
- // Give up, as sysfs did not report max_freq correctly.
- return;
- if (max_freq == 0 || testing_) {
- // One-time initialization of max_freq. (Every time when testing.)
- if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) {
- max_freq = -1;
- return;
- }
- if (max_freq == 0) {
- LOG(WARNING) << "sysfs reports 0 max CPU frequency\n";
- max_freq = -1;
- return;
- }
- if (max_freq % 10000 == 1000) {
- // Special case: system has turbo mode, and max non-turbo frequency is
- // max_freq - 1000. This relies on "normal" (non-turbo) frequencies
- // being multiples of (at least) 10 MHz. Although there is no guarantee
- // of this, it seems a fairly reasonable assumption. Otherwise we should
- // read scaling_available_frequencies, sort the frequencies, compare the
- // two highest ones, and check if they differ by 1000 (kHz) (and that's a
- // hack too, no telling when it will change).
- max_freq -= 1000;
- }
- }
- int scaled_freq = 0;
- if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq))
- return;
- // Frequencies are in kHz. If scaled_freq > max_freq, turbo is on, but
- // scaled_freq is not the actual turbo frequency. We indicate this situation
- // with a 101% value.
- int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
- SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102);
-}
-
-// Collects disk and vm stats alternating over a short and a long interval.
-
-void MetricsDaemon::StatsCallback() {
- uint64_t read_sectors_now, write_sectors_now;
- struct VmstatRecord vmstats_now;
- double time_now = GetActiveTime();
- double delta_time = time_now - stats_initial_time_;
- if (testing_) {
- // Fake the time when testing.
- delta_time = stats_state_ == kStatsShort ?
- kMetricStatsShortInterval : kMetricStatsLongInterval;
- }
- bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
- &write_sectors_now);
- int delta_read = read_sectors_now - read_sectors_;
- int delta_write = write_sectors_now - write_sectors_;
- int read_sectors_per_second = delta_read / delta_time;
- int write_sectors_per_second = delta_write / delta_time;
- bool vmstats_success = VmStatsReadStats(&vmstats_now);
- uint64_t delta_faults = vmstats_now.page_faults_ - vmstats_.page_faults_;
- uint64_t delta_swap_in = vmstats_now.swap_in_ - vmstats_.swap_in_;
- uint64_t delta_swap_out = vmstats_now.swap_out_ - vmstats_.swap_out_;
- uint64_t page_faults_per_second = delta_faults / delta_time;
- uint64_t swap_in_per_second = delta_swap_in / delta_time;
- uint64_t swap_out_per_second = delta_swap_out / delta_time;
-
- switch (stats_state_) {
- case kStatsShort:
- if (diskstats_success) {
- SendSample(kMetricReadSectorsShortName,
- read_sectors_per_second,
- 1,
- kMetricSectorsIOMax,
- kMetricSectorsBuckets);
- SendSample(kMetricWriteSectorsShortName,
- write_sectors_per_second,
- 1,
- kMetricSectorsIOMax,
- kMetricSectorsBuckets);
- }
- if (vmstats_success) {
- SendSample(kMetricPageFaultsShortName,
- page_faults_per_second,
- 1,
- kMetricPageFaultsMax,
- kMetricPageFaultsBuckets);
- SendSample(kMetricSwapInShortName,
- swap_in_per_second,
- 1,
- kMetricPageFaultsMax,
- kMetricPageFaultsBuckets);
- SendSample(kMetricSwapOutShortName,
- swap_out_per_second,
- 1,
- kMetricPageFaultsMax,
- kMetricPageFaultsBuckets);
- }
- // Schedule long callback.
- stats_state_ = kStatsLong;
- ScheduleStatsCallback(kMetricStatsLongInterval -
- kMetricStatsShortInterval);
- break;
- case kStatsLong:
- if (diskstats_success) {
- SendSample(kMetricReadSectorsLongName,
- read_sectors_per_second,
- 1,
- kMetricSectorsIOMax,
- kMetricSectorsBuckets);
- SendSample(kMetricWriteSectorsLongName,
- write_sectors_per_second,
- 1,
- kMetricSectorsIOMax,
- kMetricSectorsBuckets);
- // Reset sector counters.
- read_sectors_ = read_sectors_now;
- write_sectors_ = write_sectors_now;
- }
- if (vmstats_success) {
- SendSample(kMetricPageFaultsLongName,
- page_faults_per_second,
- 1,
- kMetricPageFaultsMax,
- kMetricPageFaultsBuckets);
- SendSample(kMetricSwapInLongName,
- swap_in_per_second,
- 1,
- kMetricPageFaultsMax,
- kMetricPageFaultsBuckets);
- SendSample(kMetricSwapOutLongName,
- swap_out_per_second,
- 1,
- kMetricPageFaultsMax,
- kMetricPageFaultsBuckets);
-
- vmstats_ = vmstats_now;
- }
- SendCpuThrottleMetrics();
- // Set start time for new cycle.
- stats_initial_time_ = time_now;
- // Schedule short callback.
- stats_state_ = kStatsShort;
- ScheduleStatsCallback(kMetricStatsShortInterval);
- break;
- default:
- LOG(FATAL) << "Invalid stats state";
- }
-}
-
-void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
- if (testing_) {
- return;
- }
- base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait);
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
- waitDelta),
- waitDelta);
-}
-
-void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) {
- string meminfo_raw;
- const FilePath meminfo_path("/proc/meminfo");
- if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
- LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
- return;
- }
- // Make both calls even if the first one fails.
- bool success = ProcessMeminfo(meminfo_raw);
- bool reschedule =
- ReportZram(base::FilePath(FILE_PATH_LITERAL("/sys/block/zram0"))) &&
- success;
- if (reschedule) {
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
- wait),
- wait);
- }
-}
-
-// static
-bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path,
- uint64_t* value) {
- std::string content;
- if (!base::ReadFileToString(path, &content)) {
- PLOG(WARNING) << "cannot read " << path.MaybeAsASCII();
- return false;
- }
- // Remove final newline.
- base::TrimWhitespaceASCII(content, base::TRIM_TRAILING, &content);
- if (!base::StringToUint64(content, value)) {
- LOG(WARNING) << "invalid integer: " << content;
- return false;
- }
- return true;
-}
-
-bool MetricsDaemon::ReportZram(const base::FilePath& zram_dir) {
- // Data sizes are in bytes. |zero_pages| is in number of pages.
- uint64_t compr_data_size, orig_data_size, zero_pages;
- const size_t page_size = 4096;
-
- if (!ReadFileToUint64(zram_dir.Append(kComprDataSizeName),
- &compr_data_size) ||
- !ReadFileToUint64(zram_dir.Append(kOrigDataSizeName), &orig_data_size) ||
- !ReadFileToUint64(zram_dir.Append(kZeroPagesName), &zero_pages)) {
- return false;
- }
-
- // |orig_data_size| does not include zero-filled pages.
- orig_data_size += zero_pages * page_size;
-
- const int compr_data_size_mb = compr_data_size >> 20;
- const int savings_mb = (orig_data_size - compr_data_size) >> 20;
- const int zero_ratio_percent = zero_pages * page_size * 100 / orig_data_size;
-
- // Report compressed size in megabytes. 100 MB or less has little impact.
- SendSample("Platform.ZramCompressedSize", compr_data_size_mb, 100, 4000, 50);
- SendSample("Platform.ZramSavings", savings_mb, 100, 4000, 50);
- // The compression ratio is multiplied by 100 for better resolution. The
- // ratios of interest are between 1 and 6 (100% and 600% as reported). We
- // don't want samples when very little memory is being compressed.
- if (compr_data_size_mb >= 1) {
- SendSample("Platform.ZramCompressionRatioPercent",
- orig_data_size * 100 / compr_data_size, 100, 600, 50);
- }
- // The values of interest for zero_pages are between 1MB and 1GB. The units
- // are number of pages.
- SendSample("Platform.ZramZeroPages", zero_pages, 256, 256 * 1024, 50);
- SendSample("Platform.ZramZeroRatioPercent", zero_ratio_percent, 1, 50, 50);
-
- return true;
-}
-
-bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
- static const MeminfoRecord fields_array[] = {
- { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
- { "MemFree", "MemFree" },
- { "Buffers", "Buffers" },
- { "Cached", "Cached" },
- // { "SwapCached", "SwapCached" },
- { "Active", "Active" },
- { "Inactive", "Inactive" },
- { "ActiveAnon", "Active(anon)" },
- { "InactiveAnon", "Inactive(anon)" },
- { "ActiveFile" , "Active(file)" },
- { "InactiveFile", "Inactive(file)" },
- { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
- // { "Mlocked", "Mlocked" },
- { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
- { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
- // { "Dirty", "Dirty" },
- // { "Writeback", "Writeback" },
- { "AnonPages", "AnonPages" },
- { "Mapped", "Mapped" },
- { "Shmem", "Shmem", kMeminfoOp_HistLog },
- { "Slab", "Slab", kMeminfoOp_HistLog },
- // { "SReclaimable", "SReclaimable" },
- // { "SUnreclaim", "SUnreclaim" },
- };
- vector<MeminfoRecord> fields(fields_array,
- fields_array + arraysize(fields_array));
- if (!FillMeminfo(meminfo_raw, &fields)) {
- return false;
- }
- int total_memory = fields[0].value;
- if (total_memory == 0) {
- // this "cannot happen"
- LOG(WARNING) << "borked meminfo parser";
- return false;
- }
- int swap_total = 0;
- int swap_free = 0;
- // Send all fields retrieved, except total memory.
- for (unsigned int i = 1; i < fields.size(); i++) {
- string metrics_name = base::StringPrintf("Platform.Meminfo%s",
- fields[i].name);
- int percent;
- switch (fields[i].op) {
- case kMeminfoOp_HistPercent:
- // report value as percent of total memory
- percent = fields[i].value * 100 / total_memory;
- SendLinearSample(metrics_name, percent, 100, 101);
- break;
- case kMeminfoOp_HistLog:
- // report value in kbytes, log scale, 4Gb max
- SendSample(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
- break;
- case kMeminfoOp_SwapTotal:
- swap_total = fields[i].value;
- case kMeminfoOp_SwapFree:
- swap_free = fields[i].value;
- break;
- }
- }
- if (swap_total > 0) {
- int swap_used = swap_total - swap_free;
- int swap_used_percent = swap_used * 100 / swap_total;
- SendSample("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
- SendLinearSample("Platform.MeminfoSwapUsedPercent", swap_used_percent,
- 100, 101);
- }
- return true;
-}
-
-bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
- vector<MeminfoRecord>* fields) {
- vector<string> lines;
- unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
-
- // Scan meminfo output and collect field values. Each field name has to
- // match a meminfo entry (case insensitive) after removing non-alpha
- // characters from the entry.
- unsigned int ifield = 0;
- for (unsigned int iline = 0;
- iline < nlines && ifield < fields->size();
- iline++) {
- vector<string> tokens;
- Tokenize(lines[iline], ": ", &tokens);
- if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
- // Name matches. Parse value and save.
- char* rest;
- (*fields)[ifield].value =
- static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
- if (*rest != '\0') {
- LOG(WARNING) << "missing meminfo value";
- return false;
- }
- ifield++;
- }
- }
- if (ifield < fields->size()) {
- // End of input reached while scanning.
- LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
- << " and following";
- return false;
- }
- return true;
-}
-
-void MetricsDaemon::ScheduleMemuseCallback(double interval) {
- if (testing_) {
- return;
- }
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::MemuseCallback, base::Unretained(this)),
- base::TimeDelta::FromSeconds(interval));
-}
-
-void MetricsDaemon::MemuseCallback() {
- // Since we only care about active time (i.e. uptime minus sleep time) but
- // the callbacks are driven by real time (uptime), we check if we should
- // reschedule this callback due to intervening sleep periods.
- double now = GetActiveTime();
- // Avoid intervals of less than one second.
- double remaining_time = ceil(memuse_final_time_ - now);
- if (remaining_time > 0) {
- ScheduleMemuseCallback(remaining_time);
- } else {
- // Report stats and advance the measurement interval unless there are
- // errors or we've completed the last interval.
- if (MemuseCallbackWork() &&
- memuse_interval_index_ < arraysize(kMemuseIntervals)) {
- double interval = kMemuseIntervals[memuse_interval_index_++];
- memuse_final_time_ = now + interval;
- ScheduleMemuseCallback(interval);
- }
- }
-}
-
-bool MetricsDaemon::MemuseCallbackWork() {
- string meminfo_raw;
- const FilePath meminfo_path("/proc/meminfo");
- if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
- LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
- return false;
- }
- return ProcessMemuse(meminfo_raw);
-}
-
-bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
- static const MeminfoRecord fields_array[] = {
- { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
- { "ActiveAnon", "Active(anon)" },
- { "InactiveAnon", "Inactive(anon)" },
- };
- vector<MeminfoRecord> fields(fields_array,
- fields_array + arraysize(fields_array));
- if (!FillMeminfo(meminfo_raw, &fields)) {
- return false;
- }
- int total = fields[0].value;
- int active_anon = fields[1].value;
- int inactive_anon = fields[2].value;
- if (total == 0) {
- // this "cannot happen"
- LOG(WARNING) << "borked meminfo parser";
- return false;
- }
- string metrics_name = base::StringPrintf("Platform.MemuseAnon%d",
- memuse_interval_index_);
- SendLinearSample(metrics_name, (active_anon + inactive_anon) * 100 / total,
- 100, 101);
- return true;
-}
-
-void MetricsDaemon::SendSample(const string& name, int sample,
- int min, int max, int nbuckets) {
- metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
-}
-
-void MetricsDaemon::SendKernelCrashesCumulativeCountStats() {
- // Report the number of crashes for this OS version, but don't clear the
- // counter. It is cleared elsewhere on version change.
- int64_t crashes_count = kernel_crashes_version_count_->Get();
- SendSample(kernel_crashes_version_count_->Name(),
- crashes_count,
- 1, // value of first bucket
- 500, // value of last bucket
- 100); // number of buckets
-
-
- int64_t cpu_use_ms = version_cumulative_cpu_use_->Get();
- SendSample(version_cumulative_cpu_use_->Name(),
- cpu_use_ms / 1000, // stat is in seconds
- 1, // device may be used very little...
- 8 * 1000 * 1000, // ... or a lot (a little over 90 days)
- 100);
-
- // On the first run after an autoupdate, cpu_use_ms and active_use_seconds
- // can be zero. Avoid division by zero.
- if (cpu_use_ms > 0) {
- // Send the crash frequency since update in number of crashes per CPU year.
- SendSample("Logging.KernelCrashesPerCpuYear",
- crashes_count * kSecondsPerDay * 365 * 1000 / cpu_use_ms,
- 1,
- 1000 * 1000, // about one crash every 30s of CPU time
- 100);
- }
-
- int64_t active_use_seconds = version_cumulative_active_use_->Get();
- if (active_use_seconds > 0) {
- SendSample(version_cumulative_active_use_->Name(),
- active_use_seconds / 1000, // stat is in seconds
- 1, // device may be used very little...
- 8 * 1000 * 1000, // ... or a lot (about 90 days)
- 100);
- // Same as above, but per year of active time.
- SendSample("Logging.KernelCrashesPerActiveYear",
- crashes_count * kSecondsPerDay * 365 / active_use_seconds,
- 1,
- 1000 * 1000, // about one crash every 30s of active time
- 100);
- }
-}
-
-void MetricsDaemon::SendDailyUseSample(
- const scoped_ptr<PersistentInteger>& use) {
- SendSample(use->Name(),
- use->GetAndClear(),
- 1, // value of first bucket
- kSecondsPerDay, // value of last bucket
- 50); // number of buckets
-}
-
-void MetricsDaemon::SendCrashIntervalSample(
- const scoped_ptr<PersistentInteger>& interval) {
- SendSample(interval->Name(),
- interval->GetAndClear(),
- 1, // value of first bucket
- 4 * kSecondsPerWeek, // value of last bucket
- 50); // number of buckets
-}
-
-void MetricsDaemon::SendCrashFrequencySample(
- const scoped_ptr<PersistentInteger>& frequency) {
- SendSample(frequency->Name(),
- frequency->GetAndClear(),
- 1, // value of first bucket
- 100, // value of last bucket
- 50); // number of buckets
-}
-
-void MetricsDaemon::SendLinearSample(const string& name, int sample,
- int max, int nbuckets) {
- // TODO(semenzato): add a proper linear histogram to the Chrome external
- // metrics API.
- LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
- metrics_lib_->SendEnumToUMA(name, sample, max);
-}
-
-void MetricsDaemon::UpdateStats(TimeTicks now_ticks,
- Time now_wall_time) {
- const int elapsed_seconds = (now_ticks - last_update_stats_time_).InSeconds();
- daily_active_use_->Add(elapsed_seconds);
- version_cumulative_active_use_->Add(elapsed_seconds);
- user_crash_interval_->Add(elapsed_seconds);
- kernel_crash_interval_->Add(elapsed_seconds);
- version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds());
- last_update_stats_time_ = now_ticks;
-
- const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch();
- const int day = since_epoch.InDays();
- const int week = day / 7;
-
- if (daily_cycle_->Get() != day) {
- daily_cycle_->Set(day);
- SendDailyUseSample(daily_active_use_);
- SendDailyUseSample(version_cumulative_active_use_);
- SendCrashFrequencySample(any_crashes_daily_count_);
- SendCrashFrequencySample(user_crashes_daily_count_);
- SendCrashFrequencySample(kernel_crashes_daily_count_);
- SendCrashFrequencySample(unclean_shutdowns_daily_count_);
- SendKernelCrashesCumulativeCountStats();
- }
-
- if (weekly_cycle_->Get() != week) {
- weekly_cycle_->Set(week);
- SendCrashFrequencySample(any_crashes_weekly_count_);
- SendCrashFrequencySample(user_crashes_weekly_count_);
- SendCrashFrequencySample(kernel_crashes_weekly_count_);
- SendCrashFrequencySample(unclean_shutdowns_weekly_count_);
- }
-}
-
-void MetricsDaemon::HandleUpdateStatsTimeout() {
- UpdateStats(TimeTicks::Now(), Time::Now());
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
- base::Unretained(this)),
- base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
-}
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
deleted file mode 100644
index 6f5a3bf..0000000
--- a/metricsd/metrics_daemon.h
+++ /dev/null
@@ -1,383 +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 METRICS_METRICS_DAEMON_H_
-#define METRICS_METRICS_DAEMON_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <base/files/file_path.h>
-#include <base/memory/scoped_ptr.h>
-#include <base/time/time.h>
-#include <chromeos/daemons/dbus_daemon.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-#include "metrics/metrics_library.h"
-#include "persistent_integer.h"
-#include "uploader/upload_service.h"
-
-using chromeos_metrics::PersistentInteger;
-
-class MetricsDaemon : public chromeos::DBusDaemon {
- public:
- MetricsDaemon();
- ~MetricsDaemon();
-
- // Initializes metrics class variables.
- void Init(bool testing,
- bool uploader_active,
- bool dbus_enabled,
- MetricsLibraryInterface* metrics_lib,
- const std::string& vmstats_path,
- const std::string& cpuinfo_max_freq_path,
- const std::string& scaling_max_freq_path,
- const base::TimeDelta& upload_interval,
- const std::string& server,
- const std::string& metrics_file,
- const std::string& config_root);
-
- // Initializes DBus and MessageLoop variables before running the MessageLoop.
- int OnInit() override;
-
- // Clean up data set up in OnInit before shutting down message loop.
- void OnShutdown(int* return_code) override;
-
- // Does all the work.
- int Run() override;
-
- // Triggers an upload event and exit. (Used to test UploadService)
- void RunUploaderTest();
-
- protected:
- // Used also by the unit tests.
- static const char kComprDataSizeName[];
- static const char kOrigDataSizeName[];
- static const char kZeroPagesName[];
-
- private:
- friend class MetricsDaemonTest;
- FRIEND_TEST(MetricsDaemonTest, CheckSystemCrash);
- FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoCurrent);
- FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoLast);
- FRIEND_TEST(MetricsDaemonTest, GetHistogramPath);
- FRIEND_TEST(MetricsDaemonTest, IsNewEpoch);
- FRIEND_TEST(MetricsDaemonTest, MessageFilter);
- FRIEND_TEST(MetricsDaemonTest, ParseVmStats);
- FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash);
- FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo);
- FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo2);
- FRIEND_TEST(MetricsDaemonTest, ProcessUncleanShutdown);
- FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash);
- FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency);
- FRIEND_TEST(MetricsDaemonTest, ReadFreqToInt);
- FRIEND_TEST(MetricsDaemonTest, ReportDiskStats);
- FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval);
- FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval);
- FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval);
- FRIEND_TEST(MetricsDaemonTest, SendSample);
- FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics);
- FRIEND_TEST(MetricsDaemonTest, SendZramMetrics);
-
- // State for disk stats collector callback.
- enum StatsState {
- kStatsShort, // short wait before short interval collection
- kStatsLong, // final wait before new collection
- };
-
- // Data record for aggregating daily usage.
- class UseRecord {
- public:
- UseRecord() : day_(0), seconds_(0) {}
- int day_;
- int seconds_;
- };
-
- // Type of scale to use for meminfo histograms. For most of them we use
- // percent of total RAM, but for some we use absolute numbers, usually in
- // megabytes, on a log scale from 0 to 4000, and 0 to 8000 for compressed
- // swap (since it can be larger than total RAM).
- enum MeminfoOp {
- kMeminfoOp_HistPercent = 0,
- kMeminfoOp_HistLog,
- kMeminfoOp_SwapTotal,
- kMeminfoOp_SwapFree,
- };
-
- // Record for retrieving and reporting values from /proc/meminfo.
- struct MeminfoRecord {
- const char* name; // print name
- const char* match; // string to match in output of /proc/meminfo
- MeminfoOp op; // histogram scale selector, or other operator
- int value; // value from /proc/meminfo
- };
-
- // Record for retrieving and reporting values from /proc/vmstat
- struct VmstatRecord {
- uint64_t page_faults_; // major faults
- uint64_t swap_in_; // pages swapped in
- uint64_t swap_out_; // pages swapped out
- };
-
- // Metric parameters.
- static const char kMetricReadSectorsLongName[];
- static const char kMetricReadSectorsShortName[];
- static const char kMetricWriteSectorsLongName[];
- static const char kMetricWriteSectorsShortName[];
- static const char kMetricPageFaultsShortName[];
- static const char kMetricPageFaultsLongName[];
- static const char kMetricSwapInLongName[];
- static const char kMetricSwapInShortName[];
- static const char kMetricSwapOutLongName[];
- static const char kMetricSwapOutShortName[];
- static const char kMetricScaledCpuFrequencyName[];
- static const int kMetricStatsShortInterval;
- static const int kMetricStatsLongInterval;
- static const int kMetricMeminfoInterval;
- static const int kMetricSectorsIOMax;
- static const int kMetricSectorsBuckets;
- static const int kMetricPageFaultsMax;
- static const int kMetricPageFaultsBuckets;
- static const char kMetricsDiskStatsPath[];
- static const char kMetricsVmStatsPath[];
- static const char kMetricsProcStatFileName[];
- static const int kMetricsProcStatFirstLineItemsCount;
-
- // Returns the active time since boot (uptime minus sleep time) in seconds.
- double GetActiveTime();
-
- // D-Bus filter callback.
- static DBusHandlerResult MessageFilter(DBusConnection* connection,
- DBusMessage* message,
- void* user_data);
-
- // Updates the daily usage file, if necessary, by adding |seconds|
- // of active use to the |day| since Epoch. If there's usage data for
- // day in the past in the usage file, that data is sent to UMA and
- // removed from the file. If there's already usage data for |day| in
- // the usage file, the |seconds| are accumulated.
- void LogDailyUseRecord(int day, int seconds);
-
- // Updates the active use time and logs time between user-space
- // process crashes.
- void ProcessUserCrash();
-
- // Updates the active use time and logs time between kernel crashes.
- void ProcessKernelCrash();
-
- // Updates the active use time and logs time between unclean shutdowns.
- void ProcessUncleanShutdown();
-
- // Checks if a kernel crash has been detected and returns true if
- // so. The method assumes that a kernel crash has happened if
- // |crash_file| exists. It removes the file immediately if it
- // exists, so it must not be called more than once.
- bool CheckSystemCrash(const std::string& crash_file);
-
- // Sends a regular (exponential) histogram sample to Chrome for
- // transport to UMA. See MetricsLibrary::SendToUMA in
- // metrics_library.h for a description of the arguments.
- void SendSample(const std::string& name, int sample,
- int min, int max, int nbuckets);
-
- // Sends a linear histogram sample to Chrome for transport to UMA. See
- // MetricsLibrary::SendToUMA in metrics_library.h for a description of the
- // arguments.
- void SendLinearSample(const std::string& name, int sample,
- int max, int nbuckets);
-
- // Sends various cumulative kernel crash-related stats, for instance the
- // total number of kernel crashes since the last version update.
- void SendKernelCrashesCumulativeCountStats();
-
- // Returns the total (system-wide) CPU usage between the time of the most
- // recent call to this function and now.
- base::TimeDelta GetIncrementalCpuUse();
-
- // Sends a sample representing the number of seconds of active use
- // for a 24-hour period.
- void SendDailyUseSample(const scoped_ptr<PersistentInteger>& use);
-
- // Sends a sample representing a time interval between two crashes of the
- // same type.
- void SendCrashIntervalSample(const scoped_ptr<PersistentInteger>& interval);
-
- // Sends a sample representing a frequency of crashes of some type.
- void SendCrashFrequencySample(const scoped_ptr<PersistentInteger>& frequency);
-
- // Initializes vm and disk stats reporting.
- void StatsReporterInit();
-
- // Schedules a callback for the next vm and disk stats collection.
- void ScheduleStatsCallback(int wait);
-
- // Reads cumulative disk statistics from sysfs. Returns true for success.
- bool DiskStatsReadStats(uint64_t* read_sectors, uint64_t* write_sectors);
-
- // Reads cumulative vm statistics from procfs. Returns true for success.
- bool VmStatsReadStats(struct VmstatRecord* stats);
-
- // Parse cumulative vm statistics from a C string. Returns true for success.
- bool VmStatsParseStats(const char* stats, struct VmstatRecord* record);
-
- // Reports disk and vm statistics.
- void StatsCallback();
-
- // Schedules meminfo collection callback.
- void ScheduleMeminfoCallback(int wait);
-
- // Reports memory statistics. Reschedules callback on success.
- void MeminfoCallback(base::TimeDelta wait);
-
- // Parses content of /proc/meminfo and sends fields of interest to UMA.
- // Returns false on errors. |meminfo_raw| contains the content of
- // /proc/meminfo.
- bool ProcessMeminfo(const std::string& meminfo_raw);
-
- // Parses meminfo data from |meminfo_raw|. |fields| is a vector containing
- // the fields of interest. The order of the fields must be the same in which
- // /proc/meminfo prints them. The result of parsing fields[i] is placed in
- // fields[i].value.
- bool FillMeminfo(const std::string& meminfo_raw,
- std::vector<MeminfoRecord>* fields);
-
- // Schedule a memory use callback in |interval| seconds.
- void ScheduleMemuseCallback(double interval);
-
- // Calls MemuseCallbackWork, and possibly schedules next callback, if enough
- // active time has passed. Otherwise reschedules itself to simulate active
- // time callbacks (i.e. wall clock time minus sleep time).
- void MemuseCallback();
-
- // Reads /proc/meminfo and sends total anonymous memory usage to UMA.
- bool MemuseCallbackWork();
-
- // Parses meminfo data and sends it to UMA.
- bool ProcessMemuse(const std::string& meminfo_raw);
-
- // Sends stats for thermal CPU throttling.
- void SendCpuThrottleMetrics();
-
- // Reads an integer CPU frequency value from sysfs.
- bool ReadFreqToInt(const std::string& sysfs_file_name, int* value);
-
- // Reads the current OS version from /etc/lsb-release and hashes it
- // to a unsigned 32-bit int.
- uint32_t GetOsVersionHash();
-
- // Updates stats, additionally sending them to UMA if enough time has elapsed
- // since the last report.
- void UpdateStats(base::TimeTicks now_ticks, base::Time now_wall_time);
-
- // Invoked periodically by |update_stats_timeout_id_| to call UpdateStats().
- void HandleUpdateStatsTimeout();
-
- // Reports zram statistics.
- bool ReportZram(const base::FilePath& zram_dir);
-
- // Reads a string from a file and converts it to uint64_t.
- static bool ReadFileToUint64(const base::FilePath& path, uint64_t* value);
-
- // VARIABLES
-
- // Test mode.
- bool testing_;
-
- // Whether the uploader is enabled or disabled.
- bool uploader_active_;
-
- // Whether or not dbus should be used.
- // If disabled, we will not collect the frequency of crashes.
- bool dbus_enabled_;
-
- // Root of the configuration files to use.
- std::string config_root_;
-
- // The metrics library handle.
- MetricsLibraryInterface* metrics_lib_;
-
- // Timestamps last network state update. This timestamp is used to
- // sample the time from the network going online to going offline so
- // TimeTicks ensures a monotonically increasing TimeDelta.
- base::TimeTicks network_state_last_;
-
- // The last time that UpdateStats() was called.
- base::TimeTicks last_update_stats_time_;
-
- // End time of current memuse stat collection interval.
- double memuse_final_time_;
-
- // Selects the wait time for the next memory use callback.
- unsigned int memuse_interval_index_;
-
- // Contain the most recent disk and vm cumulative stats.
- uint64_t read_sectors_;
- uint64_t write_sectors_;
- struct VmstatRecord vmstats_;
-
- StatsState stats_state_;
- double stats_initial_time_;
-
- // The system "HZ", or frequency of ticks. Some system data uses ticks as a
- // unit, and this is used to convert to standard time units.
- uint32_t ticks_per_second_;
- // Used internally by GetIncrementalCpuUse() to return the CPU utilization
- // between calls.
- uint64_t latest_cpu_use_ticks_;
-
- // Persistent values and accumulators for crash statistics.
- scoped_ptr<PersistentInteger> daily_cycle_;
- scoped_ptr<PersistentInteger> weekly_cycle_;
- scoped_ptr<PersistentInteger> version_cycle_;
-
- // Active use accumulated in a day.
- scoped_ptr<PersistentInteger> daily_active_use_;
- // Active use accumulated since the latest version update.
- scoped_ptr<PersistentInteger> version_cumulative_active_use_;
-
- // The CPU time accumulator. This contains the CPU time, in milliseconds,
- // used by the system since the most recent OS version update.
- scoped_ptr<PersistentInteger> version_cumulative_cpu_use_;
-
- scoped_ptr<PersistentInteger> user_crash_interval_;
- scoped_ptr<PersistentInteger> kernel_crash_interval_;
- scoped_ptr<PersistentInteger> unclean_shutdown_interval_;
-
- scoped_ptr<PersistentInteger> any_crashes_daily_count_;
- scoped_ptr<PersistentInteger> any_crashes_weekly_count_;
- scoped_ptr<PersistentInteger> user_crashes_daily_count_;
- scoped_ptr<PersistentInteger> user_crashes_weekly_count_;
- scoped_ptr<PersistentInteger> kernel_crashes_daily_count_;
- scoped_ptr<PersistentInteger> kernel_crashes_weekly_count_;
- scoped_ptr<PersistentInteger> kernel_crashes_version_count_;
- scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
- scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
-
- std::string vmstats_path_;
- std::string scaling_max_freq_path_;
- std::string cpuinfo_max_freq_path_;
-
- base::TimeDelta upload_interval_;
- std::string server_;
- std::string metrics_file_;
-
- scoped_ptr<UploadService> upload_service_;
-};
-
-#endif // METRICS_METRICS_DAEMON_H_
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
deleted file mode 100644
index c3d5cab..0000000
--- a/metricsd/metrics_daemon_main.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <base/at_exit.h>
-#include <base/command_line.h>
-#include <base/logging.h>
-#include <base/strings/string_util.h>
-#include <chromeos/flag_helper.h>
-#include <chromeos/syslog_logging.h>
-
-#include "constants.h"
-#include "metrics_daemon.h"
-
-const char kScalingMaxFreqPath[] =
- "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq";
-const char kCpuinfoMaxFreqPath[] =
- "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
-
-int main(int argc, char** argv) {
- DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)");
-
- // The uploader is disabled by default on ChromeOS as Chrome is responsible
- // for sending the metrics.
- DEFINE_bool(uploader, false, "activate the uploader");
-
- // Upload the metrics once and exit. (used for testing)
- DEFINE_bool(uploader_test,
- false,
- "run the uploader once and exit");
-
- // Enable dbus.
- DEFINE_bool(withdbus, true, "Enable dbus");
-
- // Upload Service flags.
- DEFINE_int32(upload_interval_secs,
- 1800,
- "Interval at which metrics_daemon sends the metrics. (needs "
- "-uploader)");
- DEFINE_string(server,
- metrics::kMetricsServer,
- "Server to upload the metrics to. (needs -uploader)");
- DEFINE_string(metrics_file,
- metrics::kMetricsEventsFilePath,
- "File to use as a proxy for uploading the metrics");
- DEFINE_string(config_root,
- "/", "Root of the configuration files (testing only)");
-
- chromeos::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
-
- // Also log to stderr when not running as daemon.
- chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader |
- (FLAGS_daemon ? 0 : chromeos::kLogToStderr));
-
- if (FLAGS_daemon && daemon(0, 0) != 0) {
- return errno;
- }
-
- MetricsLibrary metrics_lib;
- metrics_lib.Init();
- MetricsDaemon daemon;
- daemon.Init(FLAGS_uploader_test,
- FLAGS_uploader | FLAGS_uploader_test,
- FLAGS_withdbus,
- &metrics_lib,
- "/proc/vmstat",
- kScalingMaxFreqPath,
- kCpuinfoMaxFreqPath,
- base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
- FLAGS_server,
- FLAGS_metrics_file,
- FLAGS_config_root);
-
- if (FLAGS_uploader_test) {
- daemon.RunUploaderTest();
- return 0;
- }
-
- daemon.Run();
-}
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc
deleted file mode 100644
index 9b5b58e..0000000
--- a/metricsd/metrics_daemon_test.cc
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-#include <utime.h>
-
-#include <string>
-#include <vector>
-
-#include <base/at_exit.h>
-#include <base/files/file_util.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/stringprintf.h>
-#include <chromeos/dbus/service_constants.h>
-#include <gtest/gtest.h>
-
-#include "metrics_daemon.h"
-#include "metrics_library_mock.h"
-#include "persistent_integer_mock.h"
-
-using base::FilePath;
-using base::StringPrintf;
-using base::Time;
-using base::TimeDelta;
-using base::TimeTicks;
-using std::string;
-using std::vector;
-using ::testing::_;
-using ::testing::AnyNumber;
-using ::testing::AtLeast;
-using ::testing::Return;
-using ::testing::StrictMock;
-using chromeos_metrics::PersistentIntegerMock;
-
-static const char kFakeDiskStatsName[] = "fake-disk-stats";
-static const char kFakeDiskStatsFormat[] =
- " 1793 1788 %" PRIu64 " 105580 "
- " 196 175 %" PRIu64 " 30290 "
- " 0 44060 135850\n";
-static const uint64_t kFakeReadSectors[] = {80000, 100000};
-static const uint64_t kFakeWriteSectors[] = {3000, 4000};
-
-static const char kFakeVmStatsName[] = "fake-vm-stats";
-static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq";
-static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq";
-
-class MetricsDaemonTest : public testing::Test {
- protected:
- std::string kFakeDiskStats0;
- std::string kFakeDiskStats1;
-
- virtual void SetUp() {
- kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat,
- kFakeReadSectors[0],
- kFakeWriteSectors[0]);
- kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat,
- kFakeReadSectors[1],
- kFakeWriteSectors[1]);
- CreateFakeDiskStatsFile(kFakeDiskStats0.c_str());
- CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 10000000);
- CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 10000000);
-
- chromeos_metrics::PersistentInteger::SetTestingMode(true);
- daemon_.Init(true,
- false,
- &metrics_lib_,
- kFakeDiskStatsName,
- kFakeVmStatsName,
- kFakeScalingMaxFreqPath,
- kFakeCpuinfoMaxFreqPath,
- base::TimeDelta::FromMinutes(30),
- kMetricsServer,
- kMetricsFilePath,
- "/");
-
- // Replace original persistent values with mock ones.
- daily_active_use_mock_ =
- new StrictMock<PersistentIntegerMock>("1.mock");
- daemon_.daily_active_use_.reset(daily_active_use_mock_);
-
- kernel_crash_interval_mock_ =
- new StrictMock<PersistentIntegerMock>("2.mock");
- daemon_.kernel_crash_interval_.reset(kernel_crash_interval_mock_);
-
- user_crash_interval_mock_ =
- new StrictMock<PersistentIntegerMock>("3.mock");
- daemon_.user_crash_interval_.reset(user_crash_interval_mock_);
-
- unclean_shutdown_interval_mock_ =
- new StrictMock<PersistentIntegerMock>("4.mock");
- daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_mock_);
- }
-
- virtual void TearDown() {
- EXPECT_EQ(0, unlink(kFakeDiskStatsName));
- EXPECT_EQ(0, unlink(kFakeScalingMaxFreqPath));
- EXPECT_EQ(0, unlink(kFakeCpuinfoMaxFreqPath));
- }
-
- // Adds active use aggregation counters update expectations that the
- // specified count will be added.
- void ExpectActiveUseUpdate(int count) {
- EXPECT_CALL(*daily_active_use_mock_, Add(count))
- .Times(1)
- .RetiresOnSaturation();
- EXPECT_CALL(*kernel_crash_interval_mock_, Add(count))
- .Times(1)
- .RetiresOnSaturation();
- EXPECT_CALL(*user_crash_interval_mock_, Add(count))
- .Times(1)
- .RetiresOnSaturation();
- }
-
- // As above, but ignore values of counter updates.
- void IgnoreActiveUseUpdate() {
- EXPECT_CALL(*daily_active_use_mock_, Add(_))
- .Times(1)
- .RetiresOnSaturation();
- EXPECT_CALL(*kernel_crash_interval_mock_, Add(_))
- .Times(1)
- .RetiresOnSaturation();
- EXPECT_CALL(*user_crash_interval_mock_, Add(_))
- .Times(1)
- .RetiresOnSaturation();
- }
-
- // Adds a metrics library mock expectation that the specified metric
- // will be generated.
- void ExpectSample(const std::string& name, int sample) {
- EXPECT_CALL(metrics_lib_, SendToUMA(name, sample, _, _, _))
- .Times(1)
- .WillOnce(Return(true))
- .RetiresOnSaturation();
- }
-
- // Creates a new DBus signal message with zero or more string arguments.
- // The message can be deallocated through DeleteDBusMessage.
- //
- // |path| is the object emitting the signal.
- // |interface| is the interface the signal is emitted from.
- // |name| is the name of the signal.
- // |arg_values| contains the values of the string arguments.
- DBusMessage* NewDBusSignalString(const string& path,
- const string& interface,
- const string& name,
- const vector<string>& arg_values) {
- DBusMessage* msg = dbus_message_new_signal(path.c_str(),
- interface.c_str(),
- name.c_str());
- DBusMessageIter iter;
- dbus_message_iter_init_append(msg, &iter);
- for (vector<string>::const_iterator it = arg_values.begin();
- it != arg_values.end(); ++it) {
- const char* str_value = it->c_str();
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &str_value);
- }
- return msg;
- }
-
- // Deallocates the DBus message |msg| previously allocated through
- // dbus_message_new*.
- void DeleteDBusMessage(DBusMessage* msg) {
- dbus_message_unref(msg);
- }
-
- // Creates or overwrites an input file containing fake disk stats.
- void CreateFakeDiskStatsFile(const char* fake_stats) {
- if (unlink(kFakeDiskStatsName) < 0) {
- EXPECT_EQ(errno, ENOENT);
- }
- FILE* f = fopen(kFakeDiskStatsName, "w");
- EXPECT_EQ(1, fwrite(fake_stats, strlen(fake_stats), 1, f));
- EXPECT_EQ(0, fclose(f));
- }
-
- // Creates or overwrites the file in |path| so that it contains the printable
- // representation of |value|.
- void CreateUint64ValueFile(const base::FilePath& path, uint64_t value) {
- base::DeleteFile(path, false);
- std::string value_string = base::Uint64ToString(value);
- ASSERT_EQ(value_string.length(),
- base::WriteFile(path, value_string.c_str(),
- value_string.length()));
- }
-
- // The MetricsDaemon under test.
- MetricsDaemon daemon_;
-
- // Mocks. They are strict mock so that all unexpected
- // calls are marked as failures.
- StrictMock<MetricsLibraryMock> metrics_lib_;
- StrictMock<PersistentIntegerMock>* daily_active_use_mock_;
- StrictMock<PersistentIntegerMock>* kernel_crash_interval_mock_;
- StrictMock<PersistentIntegerMock>* user_crash_interval_mock_;
- StrictMock<PersistentIntegerMock>* unclean_shutdown_interval_mock_;
-};
-
-TEST_F(MetricsDaemonTest, CheckSystemCrash) {
- static const char kKernelCrashDetected[] = "test-kernel-crash-detected";
- EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected));
-
- base::FilePath crash_detected(kKernelCrashDetected);
- base::WriteFile(crash_detected, "", 0);
- EXPECT_TRUE(base::PathExists(crash_detected));
- EXPECT_TRUE(daemon_.CheckSystemCrash(kKernelCrashDetected));
- EXPECT_FALSE(base::PathExists(crash_detected));
- EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected));
- EXPECT_FALSE(base::PathExists(crash_detected));
- base::DeleteFile(crash_detected, false);
-}
-
-TEST_F(MetricsDaemonTest, MessageFilter) {
- // Ignore calls to SendToUMA.
- EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AnyNumber());
-
- DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
- DBusHandlerResult res =
- MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
- EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
- DeleteDBusMessage(msg);
-
- IgnoreActiveUseUpdate();
- vector<string> signal_args;
- msg = NewDBusSignalString("/",
- "org.chromium.CrashReporter",
- "UserCrash",
- signal_args);
- res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
- EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res);
- DeleteDBusMessage(msg);
-
- signal_args.clear();
- signal_args.push_back("randomstate");
- signal_args.push_back("bob"); // arbitrary username
- msg = NewDBusSignalString("/",
- "org.chromium.UnknownService.Manager",
- "StateChanged",
- signal_args);
- res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
- EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
- DeleteDBusMessage(msg);
-}
-
-TEST_F(MetricsDaemonTest, SendSample) {
- ExpectSample("Dummy.Metric", 3);
- daemon_.SendSample("Dummy.Metric", /* sample */ 3,
- /* min */ 1, /* max */ 100, /* buckets */ 50);
-}
-
-TEST_F(MetricsDaemonTest, ReportDiskStats) {
- uint64_t read_sectors_now, write_sectors_now;
- CreateFakeDiskStatsFile(kFakeDiskStats1.c_str());
- daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
- EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
- EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]);
-
- MetricsDaemon::StatsState s_state = daemon_.stats_state_;
- EXPECT_CALL(metrics_lib_,
- SendToUMA(_, (kFakeReadSectors[1] - kFakeReadSectors[0]) / 30,
- _, _, _));
- EXPECT_CALL(metrics_lib_,
- SendToUMA(_, (kFakeWriteSectors[1] - kFakeWriteSectors[0]) / 30,
- _, _, _));
- EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, _)); // SendCpuThrottleMetrics
- daemon_.StatsCallback();
- EXPECT_TRUE(s_state != daemon_.stats_state_);
-}
-
-TEST_F(MetricsDaemonTest, ProcessMeminfo) {
- string meminfo =
- "MemTotal: 2000000 kB\nMemFree: 500000 kB\n"
- "Buffers: 1000000 kB\nCached: 213652 kB\n"
- "SwapCached: 0 kB\nActive: 133400 kB\n"
- "Inactive: 183396 kB\nActive(anon): 92984 kB\n"
- "Inactive(anon): 58860 kB\nActive(file): 40416 kB\n"
- "Inactive(file): 124536 kB\nUnevictable: 0 kB\n"
- "Mlocked: 0 kB\nSwapTotal: 0 kB\n"
- "SwapFree: 0 kB\nDirty: 40 kB\n"
- "Writeback: 0 kB\nAnonPages: 92652 kB\n"
- "Mapped: 59716 kB\nShmem: 59196 kB\n"
- "Slab: 16656 kB\nSReclaimable: 6132 kB\n"
- "SUnreclaim: 10524 kB\nKernelStack: 1648 kB\n"
- "PageTables: 2780 kB\nNFS_Unstable: 0 kB\n"
- "Bounce: 0 kB\nWritebackTmp: 0 kB\n"
- "CommitLimit: 970656 kB\nCommitted_AS: 1260528 kB\n"
- "VmallocTotal: 122880 kB\nVmallocUsed: 12144 kB\n"
- "VmallocChunk: 103824 kB\nDirectMap4k: 9636 kB\n"
- "DirectMap2M: 1955840 kB\n";
-
- // All enum calls must report percents.
- EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, 100)).Times(AtLeast(1));
- // Check that MemFree is correctly computed at 25%.
- EXPECT_CALL(metrics_lib_, SendEnumToUMA("Platform.MeminfoMemFree", 25, 100))
- .Times(AtLeast(1));
- // Check that we call SendToUma at least once (log histogram).
- EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _))
- .Times(AtLeast(1));
- // Make sure we don't report fields not in the list.
- EXPECT_CALL(metrics_lib_, SendToUMA("Platform.MeminfoMlocked", _, _, _, _))
- .Times(0);
- EXPECT_CALL(metrics_lib_, SendEnumToUMA("Platform.MeminfoMlocked", _, _))
- .Times(0);
- EXPECT_TRUE(daemon_.ProcessMeminfo(meminfo));
-}
-
-TEST_F(MetricsDaemonTest, ProcessMeminfo2) {
- string meminfo = "MemTotal: 2000000 kB\nMemFree: 1000000 kB\n";
- // Not enough fields.
- EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo));
-}
-
-TEST_F(MetricsDaemonTest, ParseVmStats) {
- static char kVmStats[] = "pswpin 1345\npswpout 8896\n"
- "foo 100\nbar 200\npgmajfault 42\netcetc 300\n";
- struct MetricsDaemon::VmstatRecord stats;
- EXPECT_TRUE(daemon_.VmStatsParseStats(kVmStats, &stats));
- EXPECT_EQ(stats.page_faults_, 42);
- EXPECT_EQ(stats.swap_in_, 1345);
- EXPECT_EQ(stats.swap_out_, 8896);
-}
-
-TEST_F(MetricsDaemonTest, ReadFreqToInt) {
- const int fake_scaled_freq = 1666999;
- const int fake_max_freq = 2000000;
- int scaled_freq = 0;
- int max_freq = 0;
- CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath),
- fake_scaled_freq);
- CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), fake_max_freq);
- EXPECT_TRUE(daemon_.testing_);
- EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeScalingMaxFreqPath, &scaled_freq));
- EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeCpuinfoMaxFreqPath, &max_freq));
- EXPECT_EQ(fake_scaled_freq, scaled_freq);
- EXPECT_EQ(fake_max_freq, max_freq);
-}
-
-TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) {
- CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 2001000);
- // Test the 101% and 100% cases.
- CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 2001000);
- EXPECT_TRUE(daemon_.testing_);
- EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 101, 101));
- daemon_.SendCpuThrottleMetrics();
- CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 2000000);
- EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 100, 101));
- daemon_.SendCpuThrottleMetrics();
-}
-
-TEST_F(MetricsDaemonTest, SendZramMetrics) {
- EXPECT_TRUE(daemon_.testing_);
-
- // |compr_data_size| is the size in bytes of compressed data.
- const uint64_t compr_data_size = 50 * 1000 * 1000;
- // The constant '3' is a realistic but random choice.
- // |orig_data_size| does not include zero pages.
- const uint64_t orig_data_size = compr_data_size * 3;
- const uint64_t page_size = 4096;
- const uint64_t zero_pages = 10 * 1000 * 1000 / page_size;
-
- CreateUint64ValueFile(base::FilePath(MetricsDaemon::kComprDataSizeName),
- compr_data_size);
- CreateUint64ValueFile(base::FilePath(MetricsDaemon::kOrigDataSizeName),
- orig_data_size);
- CreateUint64ValueFile(base::FilePath(MetricsDaemon::kZeroPagesName),
- zero_pages);
-
- const uint64_t real_orig_size = orig_data_size + zero_pages * page_size;
- const uint64_t zero_ratio_percent =
- zero_pages * page_size * 100 / real_orig_size;
- // Ratio samples are in percents.
- const uint64_t actual_ratio_sample = real_orig_size * 100 / compr_data_size;
-
- EXPECT_CALL(metrics_lib_, SendToUMA(_, compr_data_size >> 20, _, _, _));
- EXPECT_CALL(metrics_lib_,
- SendToUMA(_, (real_orig_size - compr_data_size) >> 20, _, _, _));
- EXPECT_CALL(metrics_lib_, SendToUMA(_, actual_ratio_sample, _, _, _));
- EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_pages, _, _, _));
- EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_ratio_percent, _, _, _));
-
- EXPECT_TRUE(daemon_.ReportZram(base::FilePath(".")));
-}
-
-int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
-
- return RUN_ALL_TESTS();
-}
diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc
deleted file mode 100644
index c1998a6..0000000
--- a/metricsd/metrics_library.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "metrics/metrics_library.h"
-
-#include <base/logging.h>
-#include <base/strings/stringprintf.h>
-#include <errno.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-
-#include <cstdio>
-#include <cstring>
-
-#include "constants.h"
-#include "serialization/metric_sample.h"
-#include "serialization/serialization_utils.h"
-
-static const char kCrosEventHistogramName[] = "Platform.CrOSEvent";
-static const int kCrosEventHistogramMax = 100;
-
-/* Add new cros events here.
- *
- * The index of the event is sent in the message, so please do not
- * reorder the names.
- */
-static const char *kCrosEventNames[] = {
- "ModemManagerCommandSendFailure", // 0
- "HwWatchdogReboot", // 1
- "Cras.NoCodecsFoundAtBoot", // 2
- "Chaps.DatabaseCorrupted", // 3
- "Chaps.DatabaseRepairFailure", // 4
- "Chaps.DatabaseCreateFailure", // 5
- "Attestation.OriginSpecificExhausted", // 6
- "SpringPowerSupply.Original.High", // 7
- "SpringPowerSupply.Other.High", // 8
- "SpringPowerSupply.Original.Low", // 9
- "SpringPowerSupply.ChargerIdle", // 10
- "TPM.NonZeroDictionaryAttackCounter", // 11
- "TPM.EarlyResetDuringCommand", // 12
-};
-
-time_t MetricsLibrary::cached_enabled_time_ = 0;
-bool MetricsLibrary::cached_enabled_ = false;
-
-MetricsLibrary::MetricsLibrary() : consent_file_(metrics::kConsentFilePath) {}
-MetricsLibrary::~MetricsLibrary() {}
-
-// We take buffer and buffer_size as parameters in order to simplify testing
-// of various alignments of the |device_name| with |buffer_size|.
-bool MetricsLibrary::IsDeviceMounted(const char* device_name,
- const char* mounts_file,
- char* buffer,
- int buffer_size,
- bool* result) {
- if (buffer == nullptr || buffer_size < 1)
- return false;
- int mounts_fd = open(mounts_file, O_RDONLY);
- if (mounts_fd < 0)
- return false;
- // match_offset describes:
- // -1 -- not beginning of line
- // 0..strlen(device_name)-1 -- this offset in device_name is next to match
- // strlen(device_name) -- matched full name, just need a space.
- int match_offset = 0;
- bool match = false;
- while (!match) {
- int read_size = read(mounts_fd, buffer, buffer_size);
- if (read_size <= 0) {
- if (errno == -EINTR)
- continue;
- break;
- }
- for (int i = 0; i < read_size; ++i) {
- if (buffer[i] == '\n') {
- match_offset = 0;
- continue;
- }
- if (match_offset < 0) {
- continue;
- }
- if (device_name[match_offset] == '\0') {
- if (buffer[i] == ' ') {
- match = true;
- break;
- }
- match_offset = -1;
- continue;
- }
-
- if (buffer[i] == device_name[match_offset]) {
- ++match_offset;
- } else {
- match_offset = -1;
- }
- }
- }
- close(mounts_fd);
- *result = match;
- return true;
-}
-
-bool MetricsLibrary::IsGuestMode() {
- char buffer[256];
- bool result = false;
- if (!IsDeviceMounted("guestfs",
- "/proc/mounts",
- buffer,
- sizeof(buffer),
- &result)) {
- return false;
- }
- return result && (access("/var/run/state/logged-in", F_OK) == 0);
-}
-
-bool MetricsLibrary::AreMetricsEnabled() {
- static struct stat stat_buffer;
- time_t this_check_time = time(nullptr);
- if (this_check_time != cached_enabled_time_) {
- cached_enabled_time_ = this_check_time;
- cached_enabled_ = stat(consent_file_.c_str(), &stat_buffer) >= 0;
- }
- return cached_enabled_;
-}
-
-void MetricsLibrary::Init() {
- uma_events_file_ = metrics::kMetricsEventsFilePath;
-}
-
-bool MetricsLibrary::SendToUMA(const std::string& name,
- int sample,
- int min,
- int max,
- int nbuckets) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::HistogramSample(name, sample, min, max, nbuckets)
- .get(),
- metrics::kMetricsEventsFilePath);
-}
-
-bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
- int max) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::LinearHistogramSample(name, sample, max).get(),
- metrics::kMetricsEventsFilePath);
-}
-
-bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::SparseHistogramSample(name, sample).get(),
- metrics::kMetricsEventsFilePath);
-}
-
-bool MetricsLibrary::SendUserActionToUMA(const std::string& action) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::UserActionSample(action).get(), metrics::kMetricsEventsFilePath);
-}
-
-bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::CrashSample(crash_kind).get(), metrics::kMetricsEventsFilePath);
-}
-
-bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) {
- for (size_t i = 0; i < arraysize(kCrosEventNames); i++) {
- if (strcmp(event.c_str(), kCrosEventNames[i]) == 0) {
- return SendEnumToUMA(kCrosEventHistogramName, i, kCrosEventHistogramMax);
- }
- }
- return false;
-}
diff --git a/metricsd/metrics_library_mock.h b/metricsd/metrics_library_mock.h
deleted file mode 100644
index 3de87a9..0000000
--- a/metricsd/metrics_library_mock.h
+++ /dev/null
@@ -1,41 +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 METRICS_METRICS_LIBRARY_MOCK_H_
-#define METRICS_METRICS_LIBRARY_MOCK_H_
-
-#include <string>
-
-#include "metrics/metrics_library.h"
-
-#include <gmock/gmock.h>
-
-class MetricsLibraryMock : public MetricsLibraryInterface {
- public:
- bool metrics_enabled_ = true;
-
- MOCK_METHOD0(Init, void());
- MOCK_METHOD5(SendToUMA, bool(const std::string& name, int sample,
- int min, int max, int nbuckets));
- MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample,
- int max));
- MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample));
- MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action));
-
- bool AreMetricsEnabled() override {return metrics_enabled_;};
-};
-
-#endif // METRICS_METRICS_LIBRARY_MOCK_H_
diff --git a/metricsd/metrics_library_test.cc b/metricsd/metrics_library_test.cc
deleted file mode 100644
index c58e3fb..0000000
--- a/metricsd/metrics_library_test.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cstring>
-
-#include <base/files/file_util.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <policy/mock_device_policy.h>
-#include <policy/libpolicy.h>
-
-#include "metrics/c_metrics_library.h"
-#include "metrics/metrics_library.h"
-
-using base::FilePath;
-using ::testing::_;
-using ::testing::Return;
-using ::testing::AnyNumber;
-
-static const FilePath kTestUMAEventsFile("test-uma-events");
-static const char kTestMounts[] = "test-mounts";
-
-ACTION_P(SetMetricsPolicy, enabled) {
- *arg0 = enabled;
- return true;
-}
-
-class MetricsLibraryTest : public testing::Test {
- protected:
- virtual void SetUp() {
- EXPECT_TRUE(lib_.uma_events_file_.empty());
- lib_.Init();
- EXPECT_FALSE(lib_.uma_events_file_.empty());
- lib_.uma_events_file_ = kTestUMAEventsFile.value();
- EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0));
- device_policy_ = new policy::MockDevicePolicy();
- EXPECT_CALL(*device_policy_, LoadPolicy())
- .Times(AnyNumber())
- .WillRepeatedly(Return(true));
- EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
- .Times(AnyNumber())
- .WillRepeatedly(SetMetricsPolicy(true));
- provider_ = new policy::PolicyProvider(device_policy_);
- lib_.SetPolicyProvider(provider_);
- // Defeat metrics enabled caching between tests.
- lib_.cached_enabled_time_ = 0;
- }
-
- virtual void TearDown() {
- base::DeleteFile(FilePath(kTestMounts), false);
- base::DeleteFile(kTestUMAEventsFile, false);
- }
-
- void VerifyEnabledCacheHit(bool to_value);
- void VerifyEnabledCacheEviction(bool to_value);
-
- MetricsLibrary lib_;
- policy::MockDevicePolicy* device_policy_;
- policy::PolicyProvider* provider_;
-};
-
-TEST_F(MetricsLibraryTest, IsDeviceMounted) {
- static const char kTestContents[] =
- "0123456789abcde 0123456789abcde\nguestfs foo bar\n";
- char buffer[1024];
- int block_sizes[] = { 1, 2, 3, 4, 5, 6, 8, 12, 14, 16, 32, 1024 };
- bool result;
- EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
- "nonexistent",
- buffer,
- 1,
- &result));
- ASSERT_TRUE(base::WriteFile(base::FilePath(kTestMounts),
- kTestContents,
- strlen(kTestContents)));
- EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
- kTestMounts,
- buffer,
- 0,
- &result));
- for (size_t i = 0; i < arraysize(block_sizes); ++i) {
- EXPECT_TRUE(lib_.IsDeviceMounted("0123456789abcde",
- kTestMounts,
- buffer,
- block_sizes[i],
- &result));
- EXPECT_TRUE(result);
- EXPECT_TRUE(lib_.IsDeviceMounted("guestfs",
- kTestMounts,
- buffer,
- block_sizes[i],
- &result));
- EXPECT_TRUE(result);
- EXPECT_TRUE(lib_.IsDeviceMounted("0123456",
- kTestMounts,
- buffer,
- block_sizes[i],
- &result));
- EXPECT_FALSE(result);
- EXPECT_TRUE(lib_.IsDeviceMounted("9abcde",
- kTestMounts,
- buffer,
- block_sizes[i],
- &result));
- EXPECT_FALSE(result);
- EXPECT_TRUE(lib_.IsDeviceMounted("foo",
- kTestMounts,
- buffer,
- block_sizes[i],
- &result));
- EXPECT_FALSE(result);
- EXPECT_TRUE(lib_.IsDeviceMounted("bar",
- kTestMounts,
- buffer,
- block_sizes[i],
- &result));
- EXPECT_FALSE(result);
- }
-}
-
-TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
- EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
- .WillOnce(SetMetricsPolicy(false));
- EXPECT_FALSE(lib_.AreMetricsEnabled());
-}
-
-TEST_F(MetricsLibraryTest, AreMetricsEnabledTrue) {
- EXPECT_TRUE(lib_.AreMetricsEnabled());
-}
-
-void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) {
- // We might step from one second to the next one time, but not 100
- // times in a row.
- for (int i = 0; i < 100; ++i) {
- lib_.cached_enabled_time_ = 0;
- EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
- .WillOnce(SetMetricsPolicy(!to_value));
- ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
- ON_CALL(*device_policy_, GetMetricsEnabled(_))
- .WillByDefault(SetMetricsPolicy(to_value));
- if (lib_.AreMetricsEnabled() == !to_value)
- return;
- }
- ADD_FAILURE() << "Did not see evidence of caching";
-}
-
-void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) {
- lib_.cached_enabled_time_ = 0;
- EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
- .WillOnce(SetMetricsPolicy(!to_value));
- ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
- EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
- .WillOnce(SetMetricsPolicy(to_value));
- ASSERT_LT(abs(static_cast<int>(time(nullptr) - lib_.cached_enabled_time_)),
- 5);
- // Sleep one second (or cheat to be faster).
- --lib_.cached_enabled_time_;
- ASSERT_EQ(to_value, lib_.AreMetricsEnabled());
-}
-
-TEST_F(MetricsLibraryTest, AreMetricsEnabledCaching) {
- VerifyEnabledCacheHit(false);
- VerifyEnabledCacheHit(true);
- VerifyEnabledCacheEviction(false);
- VerifyEnabledCacheEviction(true);
-}
-
-class CMetricsLibraryTest : public testing::Test {
- protected:
- virtual void SetUp() {
- lib_ = CMetricsLibraryNew();
- MetricsLibrary& ml = *reinterpret_cast<MetricsLibrary*>(lib_);
- EXPECT_TRUE(ml.uma_events_file_.empty());
- CMetricsLibraryInit(lib_);
- EXPECT_FALSE(ml.uma_events_file_.empty());
- ml.uma_events_file_ = kTestUMAEventsFile.value();
- EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0));
- device_policy_ = new policy::MockDevicePolicy();
- EXPECT_CALL(*device_policy_, LoadPolicy())
- .Times(AnyNumber())
- .WillRepeatedly(Return(true));
- EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
- .Times(AnyNumber())
- .WillRepeatedly(SetMetricsPolicy(true));
- provider_ = new policy::PolicyProvider(device_policy_);
- ml.SetPolicyProvider(provider_);
- reinterpret_cast<MetricsLibrary*>(lib_)->cached_enabled_time_ = 0;
- }
-
- virtual void TearDown() {
- CMetricsLibraryDelete(lib_);
- base::DeleteFile(kTestUMAEventsFile, false);
- }
-
- CMetricsLibrary lib_;
- policy::MockDevicePolicy* device_policy_;
- policy::PolicyProvider* provider_;
-};
-
-TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) {
- EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
- .WillOnce(SetMetricsPolicy(false));
- EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_));
-}
-
-TEST_F(CMetricsLibraryTest, AreMetricsEnabledTrue) {
- EXPECT_TRUE(CMetricsLibraryAreMetricsEnabled(lib_));
-}
-
-int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/metricsd/persistent_integer.cc b/metricsd/persistent_integer.cc
deleted file mode 100644
index 9fa5c1e..0000000
--- a/metricsd/persistent_integer.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "persistent_integer.h"
-
-#include <fcntl.h>
-
-#include <base/logging.h>
-#include <base/posix/eintr_wrapper.h>
-
-#include "constants.h"
-#include "metrics/metrics_library.h"
-
-
-namespace chromeos_metrics {
-
-// Static class member instantiation.
-bool PersistentInteger::testing_ = false;
-
-PersistentInteger::PersistentInteger(const std::string& name) :
- value_(0),
- version_(kVersion),
- name_(name),
- synced_(false) {
- if (testing_) {
- backing_file_name_ = name_;
- } else {
- backing_file_name_ = metrics::kMetricsDirectory + name_;
- }
-}
-
-PersistentInteger::~PersistentInteger() {}
-
-void PersistentInteger::Set(int64_t value) {
- value_ = value;
- Write();
-}
-
-int64_t PersistentInteger::Get() {
- // If not synced, then read. If the read fails, it's a good idea to write.
- if (!synced_ && !Read())
- Write();
- return value_;
-}
-
-int64_t PersistentInteger::GetAndClear() {
- int64_t v = Get();
- Set(0);
- return v;
-}
-
-void PersistentInteger::Add(int64_t x) {
- Set(Get() + x);
-}
-
-void PersistentInteger::Write() {
- int fd = HANDLE_EINTR(open(backing_file_name_.c_str(),
- O_WRONLY | O_CREAT | O_TRUNC,
- S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH));
- PCHECK(fd >= 0) << "cannot open " << backing_file_name_ << " for writing";
- PCHECK((HANDLE_EINTR(write(fd, &version_, sizeof(version_))) ==
- sizeof(version_)) &&
- (HANDLE_EINTR(write(fd, &value_, sizeof(value_))) ==
- sizeof(value_)))
- << "cannot write to " << backing_file_name_;
- close(fd);
- synced_ = true;
-}
-
-bool PersistentInteger::Read() {
- int fd = HANDLE_EINTR(open(backing_file_name_.c_str(), O_RDONLY));
- if (fd < 0) {
- PLOG(WARNING) << "cannot open " << backing_file_name_ << " for reading";
- return false;
- }
- int32_t version;
- int64_t value;
- bool read_succeeded = false;
- if (HANDLE_EINTR(read(fd, &version, sizeof(version))) == sizeof(version) &&
- version == version_ &&
- HANDLE_EINTR(read(fd, &value, sizeof(value))) == sizeof(value)) {
- value_ = value;
- read_succeeded = true;
- synced_ = true;
- }
- close(fd);
- return read_succeeded;
-}
-
-void PersistentInteger::SetTestingMode(bool testing) {
- testing_ = testing;
-}
-
-
-} // namespace chromeos_metrics
diff --git a/metricsd/persistent_integer.h b/metricsd/persistent_integer.h
deleted file mode 100644
index fec001f..0000000
--- a/metricsd/persistent_integer.h
+++ /dev/null
@@ -1,79 +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 METRICS_PERSISTENT_INTEGER_H_
-#define METRICS_PERSISTENT_INTEGER_H_
-
-#include <stdint.h>
-
-#include <string>
-
-namespace chromeos_metrics {
-
-// PersistentIntegers is a named 64-bit integer value backed by a file.
-// The in-memory value acts as a write-through cache of the file value.
-// If the backing file doesn't exist or has bad content, the value is 0.
-
-class PersistentInteger {
- public:
- explicit PersistentInteger(const std::string& name);
-
- // Virtual only because of mock.
- virtual ~PersistentInteger();
-
- // Sets the value. This writes through to the backing file.
- void Set(int64_t v);
-
- // Gets the value. May sync from backing file first.
- int64_t Get();
-
- // Returns the name of the object.
- std::string Name() { return name_; }
-
- // Convenience function for Get() followed by Set(0).
- int64_t GetAndClear();
-
- // Convenience function for v = Get, Set(v + x).
- // Virtual only because of mock.
- virtual void Add(int64_t x);
-
- // After calling with |testing| = true, changes some behavior for the purpose
- // of testing. For instance: instances created while testing use the current
- // directory for the backing files.
- static void SetTestingMode(bool testing);
-
- private:
- static const int kVersion = 1001;
-
- // Writes |value_| to the backing file, creating it if necessary.
- void Write();
-
- // Reads the value from the backing file, stores it in |value_|, and returns
- // true if the backing file is valid. Returns false otherwise, and creates
- // a valid backing file as a side effect.
- bool Read();
-
- int64_t value_;
- int32_t version_;
- std::string name_;
- std::string backing_file_name_;
- bool synced_;
- static bool testing_;
-};
-
-} // namespace chromeos_metrics
-
-#endif // METRICS_PERSISTENT_INTEGER_H_
diff --git a/metricsd/persistent_integer_mock.h b/metricsd/persistent_integer_mock.h
deleted file mode 100644
index acc5389..0000000
--- a/metricsd/persistent_integer_mock.h
+++ /dev/null
@@ -1,37 +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 METRICS_PERSISTENT_INTEGER_MOCK_H_
-#define METRICS_PERSISTENT_INTEGER_MOCK_H_
-
-#include <string>
-
-#include <gmock/gmock.h>
-
-#include "persistent_integer.h"
-
-namespace chromeos_metrics {
-
-class PersistentIntegerMock : public PersistentInteger {
- public:
- explicit PersistentIntegerMock(const std::string& name)
- : PersistentInteger(name) {}
- MOCK_METHOD1(Add, void(int64_t count));
-};
-
-} // namespace chromeos_metrics
-
-#endif // METRICS_PERSISTENT_INTEGER_MOCK_H_
diff --git a/metricsd/persistent_integer_test.cc b/metricsd/persistent_integer_test.cc
deleted file mode 100644
index 19801f9..0000000
--- a/metricsd/persistent_integer_test.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <base/compiler_specific.h>
-#include <base/files/file_enumerator.h>
-#include <base/files/file_util.h>
-
-#include "persistent_integer.h"
-
-const char kBackingFileName[] = "1.pibakf";
-const char kBackingFilePattern[] = "*.pibakf";
-
-using chromeos_metrics::PersistentInteger;
-
-class PersistentIntegerTest : public testing::Test {
- void SetUp() override {
- // Set testing mode.
- chromeos_metrics::PersistentInteger::SetTestingMode(true);
- }
-
- void TearDown() override {
- // Remove backing files. The convention is that they all end in ".pibakf".
- base::FileEnumerator f_enum(base::FilePath("."),
- false,
- base::FileEnumerator::FILES,
- FILE_PATH_LITERAL(kBackingFilePattern));
- for (base::FilePath name = f_enum.Next();
- !name.empty();
- name = f_enum.Next()) {
- base::DeleteFile(name, false);
- }
- }
-};
-
-TEST_F(PersistentIntegerTest, BasicChecks) {
- scoped_ptr<PersistentInteger> pi(new PersistentInteger(kBackingFileName));
-
- // Test initialization.
- EXPECT_EQ(0, pi->Get());
- EXPECT_EQ(kBackingFileName, pi->Name()); // boring
-
- // Test set and add.
- pi->Set(2);
- pi->Add(3);
- EXPECT_EQ(5, pi->Get());
-
- // Test persistence.
- pi.reset(new PersistentInteger(kBackingFileName));
- EXPECT_EQ(5, pi->Get());
-
- // Test GetAndClear.
- EXPECT_EQ(5, pi->GetAndClear());
- EXPECT_EQ(pi->Get(), 0);
-
- // Another persistence test.
- pi.reset(new PersistentInteger(kBackingFileName));
- EXPECT_EQ(0, pi->Get());
-}
-
-int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/metricsd/serialization/metric_sample.cc b/metricsd/serialization/metric_sample.cc
deleted file mode 100644
index 76a47c0..0000000
--- a/metricsd/serialization/metric_sample.cc
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "serialization/metric_sample.h"
-
-#include <string>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/stringprintf.h"
-
-namespace metrics {
-
-MetricSample::MetricSample(MetricSample::SampleType sample_type,
- const std::string& metric_name,
- int sample,
- int min,
- int max,
- int bucket_count)
- : type_(sample_type),
- name_(metric_name),
- sample_(sample),
- min_(min),
- max_(max),
- bucket_count_(bucket_count) {
-}
-
-MetricSample::~MetricSample() {
-}
-
-bool MetricSample::IsValid() const {
- return name().find(' ') == std::string::npos &&
- name().find('\0') == std::string::npos && !name().empty();
-}
-
-std::string MetricSample::ToString() const {
- if (type_ == CRASH) {
- return base::StringPrintf("crash%c%s%c",
- '\0',
- name().c_str(),
- '\0');
- } else if (type_ == SPARSE_HISTOGRAM) {
- return base::StringPrintf("sparsehistogram%c%s %d%c",
- '\0',
- name().c_str(),
- sample_,
- '\0');
- } else if (type_ == LINEAR_HISTOGRAM) {
- return base::StringPrintf("linearhistogram%c%s %d %d%c",
- '\0',
- name().c_str(),
- sample_,
- max_,
- '\0');
- } else if (type_ == HISTOGRAM) {
- return base::StringPrintf("histogram%c%s %d %d %d %d%c",
- '\0',
- name().c_str(),
- sample_,
- min_,
- max_,
- bucket_count_,
- '\0');
- } else {
- // The type can only be USER_ACTION.
- CHECK_EQ(type_, USER_ACTION);
- return base::StringPrintf("useraction%c%s%c",
- '\0',
- name().c_str(),
- '\0');
- }
-}
-
-int MetricSample::sample() const {
- CHECK_NE(type_, USER_ACTION);
- CHECK_NE(type_, CRASH);
- return sample_;
-}
-
-int MetricSample::min() const {
- CHECK_EQ(type_, HISTOGRAM);
- return min_;
-}
-
-int MetricSample::max() const {
- CHECK_NE(type_, CRASH);
- CHECK_NE(type_, USER_ACTION);
- CHECK_NE(type_, SPARSE_HISTOGRAM);
- return max_;
-}
-
-int MetricSample::bucket_count() const {
- CHECK_EQ(type_, HISTOGRAM);
- return bucket_count_;
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::CrashSample(
- const std::string& crash_name) {
- return scoped_ptr<MetricSample>(
- new MetricSample(CRASH, crash_name, 0, 0, 0, 0));
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::HistogramSample(
- const std::string& histogram_name,
- int sample,
- int min,
- int max,
- int bucket_count) {
- return scoped_ptr<MetricSample>(new MetricSample(
- HISTOGRAM, histogram_name, sample, min, max, bucket_count));
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::ParseHistogram(
- const std::string& serialized_histogram) {
- std::vector<std::string> parts;
- base::SplitString(serialized_histogram, ' ', &parts);
-
- if (parts.size() != 5)
- return scoped_ptr<MetricSample>();
- int sample, min, max, bucket_count;
- if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
- !base::StringToInt(parts[2], &min) ||
- !base::StringToInt(parts[3], &max) ||
- !base::StringToInt(parts[4], &bucket_count)) {
- return scoped_ptr<MetricSample>();
- }
-
- return HistogramSample(parts[0], sample, min, max, bucket_count);
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::SparseHistogramSample(
- const std::string& histogram_name,
- int sample) {
- return scoped_ptr<MetricSample>(
- new MetricSample(SPARSE_HISTOGRAM, histogram_name, sample, 0, 0, 0));
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::ParseSparseHistogram(
- const std::string& serialized_histogram) {
- std::vector<std::string> parts;
- base::SplitString(serialized_histogram, ' ', &parts);
- if (parts.size() != 2)
- return scoped_ptr<MetricSample>();
- int sample;
- if (parts[0].empty() || !base::StringToInt(parts[1], &sample))
- return scoped_ptr<MetricSample>();
-
- return SparseHistogramSample(parts[0], sample);
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::LinearHistogramSample(
- const std::string& histogram_name,
- int sample,
- int max) {
- return scoped_ptr<MetricSample>(
- new MetricSample(LINEAR_HISTOGRAM, histogram_name, sample, 0, max, 0));
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::ParseLinearHistogram(
- const std::string& serialized_histogram) {
- std::vector<std::string> parts;
- int sample, max;
- base::SplitString(serialized_histogram, ' ', &parts);
- if (parts.size() != 3)
- return scoped_ptr<MetricSample>();
- if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
- !base::StringToInt(parts[2], &max)) {
- return scoped_ptr<MetricSample>();
- }
-
- return LinearHistogramSample(parts[0], sample, max);
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::UserActionSample(
- const std::string& action_name) {
- return scoped_ptr<MetricSample>(
- new MetricSample(USER_ACTION, action_name, 0, 0, 0, 0));
-}
-
-bool MetricSample::IsEqual(const MetricSample& metric) {
- return type_ == metric.type_ && name_ == metric.name_ &&
- sample_ == metric.sample_ && min_ == metric.min_ &&
- max_ == metric.max_ && bucket_count_ == metric.bucket_count_;
-}
-
-} // namespace metrics
diff --git a/metricsd/serialization/metric_sample.h b/metricsd/serialization/metric_sample.h
deleted file mode 100644
index 5a4e4ae..0000000
--- a/metricsd/serialization/metric_sample.h
+++ /dev/null
@@ -1,131 +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 METRICS_SERIALIZATION_METRIC_SAMPLE_H_
-#define METRICS_SERIALIZATION_METRIC_SAMPLE_H_
-
-#include <string>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-
-namespace metrics {
-
-// This class is used by libmetrics (ChromeOS) to serialize
-// and deserialize measurements to send them to a metrics sending service.
-// It is meant to be a simple container with serialization functions.
-class MetricSample {
- public:
- // Types of metric sample used.
- enum SampleType {
- CRASH,
- HISTOGRAM,
- LINEAR_HISTOGRAM,
- SPARSE_HISTOGRAM,
- USER_ACTION
- };
-
- ~MetricSample();
-
- // Returns true if the sample is valid (can be serialized without ambiguity).
- //
- // This function should be used to filter bad samples before serializing them.
- bool IsValid() const;
-
- // Getters for type and name. All types of metrics have these so we do not
- // need to check the type.
- SampleType type() const { return type_; }
- const std::string& name() const { return name_; }
-
- // Getters for sample, min, max, bucket_count.
- // Check the metric type to make sure the request make sense. (ex: a crash
- // sample does not have a bucket_count so we crash if we call bucket_count()
- // on it.)
- int sample() const;
- int min() const;
- int max() const;
- int bucket_count() const;
-
- // Returns a serialized version of the sample.
- //
- // The serialized message for each type is:
- // crash: crash\0|name_|\0
- // user action: useraction\0|name_|\0
- // histogram: histogram\0|name_| |sample_| |min_| |max_| |bucket_count_|\0
- // sparsehistogram: sparsehistogram\0|name_| |sample_|\0
- // linearhistogram: linearhistogram\0|name_| |sample_| |max_|\0
- std::string ToString() const;
-
- // Builds a crash sample.
- static scoped_ptr<MetricSample> CrashSample(const std::string& crash_name);
-
- // Builds a histogram sample.
- static scoped_ptr<MetricSample> HistogramSample(
- const std::string& histogram_name,
- int sample,
- int min,
- int max,
- int bucket_count);
- // Deserializes a histogram sample.
- static scoped_ptr<MetricSample> ParseHistogram(const std::string& serialized);
-
- // Builds a sparse histogram sample.
- static scoped_ptr<MetricSample> SparseHistogramSample(
- const std::string& histogram_name,
- int sample);
- // Deserializes a sparse histogram sample.
- static scoped_ptr<MetricSample> ParseSparseHistogram(
- const std::string& serialized);
-
- // Builds a linear histogram sample.
- static scoped_ptr<MetricSample> LinearHistogramSample(
- const std::string& histogram_name,
- int sample,
- int max);
- // Deserializes a linear histogram sample.
- static scoped_ptr<MetricSample> ParseLinearHistogram(
- const std::string& serialized);
-
- // Builds a user action sample.
- static scoped_ptr<MetricSample> UserActionSample(
- const std::string& action_name);
-
- // Returns true if sample and this object represent the same sample (type,
- // name, sample, min, max, bucket_count match).
- bool IsEqual(const MetricSample& sample);
-
- private:
- MetricSample(SampleType sample_type,
- const std::string& metric_name,
- const int sample,
- const int min,
- const int max,
- const int bucket_count);
-
- const SampleType type_;
- const std::string name_;
- const int sample_;
- const int min_;
- const int max_;
- const int bucket_count_;
-
- DISALLOW_COPY_AND_ASSIGN(MetricSample);
-};
-
-} // namespace metrics
-
-#endif // METRICS_SERIALIZATION_METRIC_SAMPLE_H_
diff --git a/metricsd/serialization/serialization_utils.cc b/metricsd/serialization/serialization_utils.cc
deleted file mode 100644
index 6dd8258..0000000
--- a/metricsd/serialization/serialization_utils.cc
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "serialization/serialization_utils.h"
-
-#include <sys/file.h>
-
-#include <string>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "serialization/metric_sample.h"
-
-#define READ_WRITE_ALL_FILE_FLAGS \
- (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
-
-namespace metrics {
-namespace {
-
-// Reads the next message from |file_descriptor| into |message|.
-//
-// |message| will be set to the empty string if no message could be read (EOF)
-// or the message was badly constructed.
-//
-// Returns false if no message can be read from this file anymore (EOF or
-// unrecoverable error).
-bool ReadMessage(int fd, std::string* message) {
- CHECK(message);
-
- int result;
- int32_t message_size;
- const int32_t message_hdr_size = sizeof(message_size);
- // The file containing the metrics do not leave the device so the writer and
- // the reader will always have the same endianness.
- result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size)));
- if (result < 0) {
- DPLOG(ERROR) << "reading metrics message header";
- return false;
- }
- if (result == 0) {
- // This indicates a normal EOF.
- return false;
- }
- if (result < message_hdr_size) {
- DLOG(ERROR) << "bad read size " << result << ", expecting "
- << sizeof(message_size);
- return false;
- }
-
- // kMessageMaxLength applies to the entire message: the 4-byte
- // length field and the content.
- if (message_size > SerializationUtils::kMessageMaxLength) {
- DLOG(ERROR) << "message too long : " << message_size;
- if (HANDLE_EINTR(lseek(fd, message_size - 4, SEEK_CUR)) == -1) {
- DLOG(ERROR) << "error while skipping message. abort";
- return false;
- }
- // Badly formatted message was skipped. Treat the badly formatted sample as
- // an empty sample.
- message->clear();
- return true;
- }
-
- if (message_size < message_hdr_size) {
- DLOG(ERROR) << "message too short : " << message_size;
- return false;
- }
-
- message_size -= message_hdr_size; // The message size includes itself.
- char buffer[SerializationUtils::kMessageMaxLength];
- if (!base::ReadFromFD(fd, buffer, message_size)) {
- DPLOG(ERROR) << "reading metrics message body";
- return false;
- }
- *message = std::string(buffer, message_size);
- return true;
-}
-
-} // namespace
-
-scoped_ptr<MetricSample> SerializationUtils::ParseSample(
- const std::string& sample) {
- if (sample.empty())
- return scoped_ptr<MetricSample>();
-
- std::vector<std::string> parts;
- base::SplitString(sample, '\0', &parts);
- // We should have two null terminated strings so split should produce
- // three chunks.
- if (parts.size() != 3) {
- DLOG(ERROR) << "splitting message on \\0 produced " << parts.size()
- << " parts (expected 3)";
- return scoped_ptr<MetricSample>();
- }
- const std::string& name = parts[0];
- const std::string& value = parts[1];
-
- if (base::LowerCaseEqualsASCII(name, "crash")) {
- return MetricSample::CrashSample(value);
- } else if (base::LowerCaseEqualsASCII(name, "histogram")) {
- return MetricSample::ParseHistogram(value);
- } else if (base::LowerCaseEqualsASCII(name, "linearhistogram")) {
- return MetricSample::ParseLinearHistogram(value);
- } else if (base::LowerCaseEqualsASCII(name, "sparsehistogram")) {
- return MetricSample::ParseSparseHistogram(value);
- } else if (base::LowerCaseEqualsASCII(name, "useraction")) {
- return MetricSample::UserActionSample(value);
- } else {
- DLOG(ERROR) << "invalid event type: " << name << ", value: " << value;
- }
- return scoped_ptr<MetricSample>();
-}
-
-void SerializationUtils::ReadAndTruncateMetricsFromFile(
- const std::string& filename,
- ScopedVector<MetricSample>* metrics) {
- struct stat stat_buf;
- int result;
-
- result = stat(filename.c_str(), &stat_buf);
- if (result < 0) {
- if (errno != ENOENT)
- DPLOG(ERROR) << filename << ": bad metrics file stat";
-
- // Nothing to collect---try later.
- return;
- }
- if (stat_buf.st_size == 0) {
- // Also nothing to collect.
- return;
- }
- base::ScopedFD fd(open(filename.c_str(), O_RDWR));
- if (fd.get() < 0) {
- DPLOG(ERROR) << filename << ": cannot open";
- return;
- }
- result = flock(fd.get(), LOCK_EX);
- if (result < 0) {
- DPLOG(ERROR) << filename << ": cannot lock";
- return;
- }
-
- // This processes all messages in the log. When all messages are
- // read and processed, or an error occurs, truncate the file to zero size.
- for (;;) {
- std::string message;
-
- if (!ReadMessage(fd.get(), &message))
- break;
-
- scoped_ptr<MetricSample> sample = ParseSample(message);
- if (sample)
- metrics->push_back(sample.release());
- }
-
- result = ftruncate(fd.get(), 0);
- if (result < 0)
- DPLOG(ERROR) << "truncate metrics log";
-
- result = flock(fd.get(), LOCK_UN);
- if (result < 0)
- DPLOG(ERROR) << "unlock metrics log";
-}
-
-bool SerializationUtils::WriteMetricToFile(const MetricSample& sample,
- const std::string& filename) {
- if (!sample.IsValid())
- return false;
-
- base::ScopedFD file_descriptor(open(filename.c_str(),
- O_WRONLY | O_APPEND | O_CREAT,
- READ_WRITE_ALL_FILE_FLAGS));
-
- if (file_descriptor.get() < 0) {
- DPLOG(ERROR) << filename << ": cannot open";
- return false;
- }
-
- fchmod(file_descriptor.get(), READ_WRITE_ALL_FILE_FLAGS);
- // Grab a lock to avoid chrome truncating the file
- // underneath us. Keep the file locked as briefly as possible.
- // Freeing file_descriptor will close the file and and remove the lock.
- if (HANDLE_EINTR(flock(file_descriptor.get(), LOCK_EX)) < 0) {
- DPLOG(ERROR) << filename << ": cannot lock";
- return false;
- }
-
- std::string msg = sample.ToString();
- int32 size = msg.length() + sizeof(int32);
- if (size > kMessageMaxLength) {
- DLOG(ERROR) << "cannot write message: too long";
- return false;
- }
-
- // The file containing the metrics samples will only be read by programs on
- // the same device so we do not check endianness.
- if (!base::WriteFileDescriptor(file_descriptor.get(),
- reinterpret_cast<char*>(&size),
- sizeof(size))) {
- DPLOG(ERROR) << "error writing message length";
- return false;
- }
-
- if (!base::WriteFileDescriptor(
- file_descriptor.get(), msg.c_str(), msg.size())) {
- DPLOG(ERROR) << "error writing message";
- return false;
- }
-
- return true;
-}
-
-} // namespace metrics
diff --git a/metricsd/serialization/serialization_utils.h b/metricsd/serialization/serialization_utils.h
deleted file mode 100644
index 67d4675..0000000
--- a/metricsd/serialization/serialization_utils.h
+++ /dev/null
@@ -1,60 +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 METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
-#define METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
-
-#include <string>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-
-namespace metrics {
-
-class MetricSample;
-
-// Metrics helpers to serialize and deserialize metrics collected by
-// ChromeOS.
-namespace SerializationUtils {
-
-// Deserializes a sample passed as a string and return a sample.
-// The return value will either be a scoped_ptr to a Metric sample (if the
-// deserialization was successful) or a NULL scoped_ptr.
-scoped_ptr<MetricSample> ParseSample(const std::string& sample);
-
-// Reads all samples from a file and truncate the file when done.
-void ReadAndTruncateMetricsFromFile(const std::string& filename,
- ScopedVector<MetricSample>* metrics);
-
-// Serializes a sample and write it to filename.
-// The format for the message is:
-// message_size, serialized_message
-// where
-// * message_size is the total length of the message (message_size +
-// serialized_message) on 4 bytes
-// * serialized_message is the serialized version of sample (using ToString)
-//
-// NB: the file will never leave the device so message_size will be written
-// with the architecture's endianness.
-bool WriteMetricToFile(const MetricSample& sample, const std::string& filename);
-
-// Maximum length of a serialized message
-static const int kMessageMaxLength = 1024;
-
-} // namespace SerializationUtils
-} // namespace metrics
-
-#endif // METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
diff --git a/metricsd/serialization/serialization_utils_unittest.cc b/metricsd/serialization/serialization_utils_unittest.cc
deleted file mode 100644
index 7a572de..0000000
--- a/metricsd/serialization/serialization_utils_unittest.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "serialization/serialization_utils.h"
-
-#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
-#include <base/logging.h>
-#include <base/strings/stringprintf.h>
-#include <gtest/gtest.h>
-
-#include "serialization/metric_sample.h"
-
-namespace metrics {
-namespace {
-
-class SerializationUtilsTest : public testing::Test {
- protected:
- SerializationUtilsTest() {
- bool success = temporary_dir.CreateUniqueTempDir();
- if (success) {
- base::FilePath dir_path = temporary_dir.path();
- filename = dir_path.value() + "chromeossampletest";
- filepath = base::FilePath(filename);
- }
- }
-
- void SetUp() override { base::DeleteFile(filepath, false); }
-
- void TestSerialization(MetricSample* sample) {
- std::string serialized(sample->ToString());
- ASSERT_EQ('\0', serialized[serialized.length() - 1]);
- scoped_ptr<MetricSample> deserialized =
- SerializationUtils::ParseSample(serialized);
- ASSERT_TRUE(deserialized);
- EXPECT_TRUE(sample->IsEqual(*deserialized.get()));
- }
-
- std::string filename;
- base::ScopedTempDir temporary_dir;
- base::FilePath filepath;
-};
-
-TEST_F(SerializationUtilsTest, CrashSerializeTest) {
- TestSerialization(MetricSample::CrashSample("test").get());
-}
-
-TEST_F(SerializationUtilsTest, HistogramSerializeTest) {
- TestSerialization(
- MetricSample::HistogramSample("myhist", 13, 1, 100, 10).get());
-}
-
-TEST_F(SerializationUtilsTest, LinearSerializeTest) {
- TestSerialization(
- MetricSample::LinearHistogramSample("linearhist", 12, 30).get());
-}
-
-TEST_F(SerializationUtilsTest, SparseSerializeTest) {
- TestSerialization(MetricSample::SparseHistogramSample("mysparse", 30).get());
-}
-
-TEST_F(SerializationUtilsTest, UserActionSerializeTest) {
- TestSerialization(MetricSample::UserActionSample("myaction").get());
-}
-
-TEST_F(SerializationUtilsTest, IllegalNameAreFilteredTest) {
- scoped_ptr<MetricSample> sample1 =
- MetricSample::SparseHistogramSample("no space", 10);
- scoped_ptr<MetricSample> sample2 = MetricSample::LinearHistogramSample(
- base::StringPrintf("here%cbhe", '\0'), 1, 3);
-
- EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample1.get(), filename));
- EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample2.get(), filename));
- int64 size = 0;
-
- ASSERT_TRUE(!PathExists(filepath) || base::GetFileSize(filepath, &size));
-
- EXPECT_EQ(0, size);
-}
-
-TEST_F(SerializationUtilsTest, BadInputIsCaughtTest) {
- std::string input(
- base::StringPrintf("sparsehistogram%cname foo%c", '\0', '\0'));
- EXPECT_EQ(NULL, MetricSample::ParseSparseHistogram(input).get());
-}
-
-TEST_F(SerializationUtilsTest, MessageSeparatedByZero) {
- scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
-
- SerializationUtils::WriteMetricToFile(*crash.get(), filename);
- int64 size = 0;
- ASSERT_TRUE(base::GetFileSize(filepath, &size));
- // 4 bytes for the size
- // 5 bytes for crash
- // 7 bytes for mycrash
- // 2 bytes for the \0
- // -> total of 18
- EXPECT_EQ(size, 18);
-}
-
-TEST_F(SerializationUtilsTest, MessagesTooLongAreDiscardedTest) {
- // Creates a message that is bigger than the maximum allowed size.
- // As we are adding extra character (crash, \0s, etc), if the name is
- // kMessageMaxLength long, it will be too long.
- std::string name(SerializationUtils::kMessageMaxLength, 'c');
-
- scoped_ptr<MetricSample> crash = MetricSample::CrashSample(name);
- EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*crash.get(), filename));
- int64 size = 0;
- ASSERT_TRUE(base::GetFileSize(filepath, &size));
- EXPECT_EQ(0, size);
-}
-
-TEST_F(SerializationUtilsTest, ReadLongMessageTest) {
- base::File test_file(filepath,
- base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
- std::string message(SerializationUtils::kMessageMaxLength + 1, 'c');
-
- int32 message_size = message.length() + sizeof(int32);
- test_file.WriteAtCurrentPos(reinterpret_cast<const char*>(&message_size),
- sizeof(message_size));
- test_file.WriteAtCurrentPos(message.c_str(), message.length());
- test_file.Close();
-
- scoped_ptr<MetricSample> crash = MetricSample::CrashSample("test");
- SerializationUtils::WriteMetricToFile(*crash.get(), filename);
-
- ScopedVector<MetricSample> samples;
- SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &samples);
- ASSERT_EQ(size_t(1), samples.size());
- ASSERT_TRUE(samples[0] != NULL);
- EXPECT_TRUE(crash->IsEqual(*samples[0]));
-}
-
-TEST_F(SerializationUtilsTest, WriteReadTest) {
- scoped_ptr<MetricSample> hist =
- MetricSample::HistogramSample("myhist", 1, 2, 3, 4);
- scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
- scoped_ptr<MetricSample> lhist =
- MetricSample::LinearHistogramSample("linear", 1, 10);
- scoped_ptr<MetricSample> shist =
- MetricSample::SparseHistogramSample("mysparse", 30);
- scoped_ptr<MetricSample> action = MetricSample::UserActionSample("myaction");
-
- SerializationUtils::WriteMetricToFile(*hist.get(), filename);
- SerializationUtils::WriteMetricToFile(*crash.get(), filename);
- SerializationUtils::WriteMetricToFile(*lhist.get(), filename);
- SerializationUtils::WriteMetricToFile(*shist.get(), filename);
- SerializationUtils::WriteMetricToFile(*action.get(), filename);
- ScopedVector<MetricSample> vect;
- SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &vect);
- ASSERT_EQ(vect.size(), size_t(5));
- for (int i = 0; i < 5; i++) {
- ASSERT_TRUE(vect[0] != NULL);
- }
- EXPECT_TRUE(hist->IsEqual(*vect[0]));
- EXPECT_TRUE(crash->IsEqual(*vect[1]));
- EXPECT_TRUE(lhist->IsEqual(*vect[2]));
- EXPECT_TRUE(shist->IsEqual(*vect[3]));
- EXPECT_TRUE(action->IsEqual(*vect[4]));
-
- int64 size = 0;
- ASSERT_TRUE(base::GetFileSize(filepath, &size));
- ASSERT_EQ(0, size);
-}
-
-} // namespace
-} // namespace metrics
diff --git a/metricsd/timer.cc b/metricsd/timer.cc
deleted file mode 100644
index 7b00cc0..0000000
--- a/metricsd/timer.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "timer.h"
-
-#include <string>
-
-#include <base/memory/scoped_ptr.h>
-
-#include "metrics/metrics_library.h"
-
-namespace chromeos_metrics {
-
-base::TimeTicks ClockWrapper::GetCurrentTime() const {
- return base::TimeTicks::Now();
-}
-
-Timer::Timer()
- : timer_state_(kTimerStopped),
- clock_wrapper_(new ClockWrapper()) {}
-
-bool Timer::Start() {
- elapsed_time_ = base::TimeDelta(); // Sets elapsed_time_ to zero.
- start_time_ = clock_wrapper_->GetCurrentTime();
- timer_state_ = kTimerRunning;
- return true;
-}
-
-bool Timer::Stop() {
- if (timer_state_ == kTimerStopped)
- return false;
- if (timer_state_ == kTimerRunning)
- elapsed_time_ += clock_wrapper_->GetCurrentTime() - start_time_;
- timer_state_ = kTimerStopped;
- return true;
-}
-
-bool Timer::Pause() {
- switch (timer_state_) {
- case kTimerStopped:
- if (!Start())
- return false;
- timer_state_ = kTimerPaused;
- return true;
- case kTimerRunning:
- timer_state_ = kTimerPaused;
- elapsed_time_ += clock_wrapper_->GetCurrentTime() - start_time_;
- return true;
- default:
- return false;
- }
-}
-
-bool Timer::Resume() {
- switch (timer_state_) {
- case kTimerStopped:
- return Start();
- case kTimerPaused:
- start_time_ = clock_wrapper_->GetCurrentTime();
- timer_state_ = kTimerRunning;
- return true;
- default:
- return false;
- }
-}
-
-bool Timer::Reset() {
- elapsed_time_ = base::TimeDelta(); // Sets elapsed_time_ to zero.
- timer_state_ = kTimerStopped;
- return true;
-}
-
-bool Timer::HasStarted() const {
- return timer_state_ != kTimerStopped;
-}
-
-bool Timer::GetElapsedTime(base::TimeDelta* elapsed_time) const {
- if (start_time_.is_null() || !elapsed_time)
- return false;
- *elapsed_time = elapsed_time_;
- if (timer_state_ == kTimerRunning) {
- *elapsed_time += clock_wrapper_->GetCurrentTime() - start_time_;
- }
- return true;
-}
-
-// static
-MetricsLibraryInterface* TimerReporter::metrics_lib_ = nullptr;
-
-TimerReporter::TimerReporter(const std::string& histogram_name, int min,
- int max, int num_buckets)
- : histogram_name_(histogram_name),
- min_(min),
- max_(max),
- num_buckets_(num_buckets) {}
-
-bool TimerReporter::ReportMilliseconds() const {
- base::TimeDelta elapsed_time;
- if (!metrics_lib_ || !GetElapsedTime(&elapsed_time)) return false;
- return metrics_lib_->SendToUMA(histogram_name_,
- elapsed_time.InMilliseconds(),
- min_,
- max_,
- num_buckets_);
-}
-
-} // namespace chromeos_metrics
diff --git a/metricsd/timer.h b/metricsd/timer.h
deleted file mode 100644
index b36ffff..0000000
--- a/metricsd/timer.h
+++ /dev/null
@@ -1,170 +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.
- */
-
-// Timer - class that provides timer tracking.
-
-#ifndef METRICS_TIMER_H_
-#define METRICS_TIMER_H_
-
-#include <string>
-
-#include <base/macros.h>
-#include <base/memory/scoped_ptr.h>
-#include <base/time/time.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-class MetricsLibraryInterface;
-
-namespace chromeos_metrics {
-
-class TimerInterface {
- public:
- virtual ~TimerInterface() {}
-
- virtual bool Start() = 0;
- virtual bool Stop() = 0;
- virtual bool Reset() = 0;
- virtual bool HasStarted() const = 0;
-};
-
-// Wrapper for calls to the system clock.
-class ClockWrapper {
- public:
- ClockWrapper() {}
- virtual ~ClockWrapper() {}
-
- // Returns the current time from the system.
- virtual base::TimeTicks GetCurrentTime() const;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ClockWrapper);
-};
-
-// Implements a Timer.
-class Timer : public TimerInterface {
- public:
- Timer();
- virtual ~Timer() {}
-
- // Starts the timer. If a timer is already running, also resets current
- // timer. Always returns true.
- virtual bool Start();
-
- // Stops the timer and calculates the total time elapsed between now and when
- // Start() was called. Note that this method needs a prior call to Start().
- // Otherwise, it fails (returns false).
- virtual bool Stop();
-
- // Pauses a timer. If the timer is stopped, this call starts the timer in
- // the paused state. Fails (returns false) if the timer is already paused.
- virtual bool Pause();
-
- // Restarts a paused timer (or starts a stopped timer). This method fails
- // (returns false) if the timer is already running; otherwise, returns true.
- virtual bool Resume();
-
- // Resets the timer, erasing the current duration being tracked. Always
- // returns true.
- virtual bool Reset();
-
- // Returns whether the timer has started or not.
- virtual bool HasStarted() const;
-
- // Stores the current elapsed time in |elapsed_time|. If timer is stopped,
- // stores the elapsed time from when Stop() was last called. Otherwise,
- // calculates and stores the elapsed time since the last Start().
- // Returns false if the timer was never Start()'ed or if called with a null
- // pointer argument.
- virtual bool GetElapsedTime(base::TimeDelta* elapsed_time) const;
-
- private:
- enum TimerState { kTimerStopped, kTimerRunning, kTimerPaused };
- friend class TimerTest;
- friend class TimerReporterTest;
- FRIEND_TEST(TimerReporterTest, StartStopReport);
- FRIEND_TEST(TimerTest, InvalidElapsedTime);
- FRIEND_TEST(TimerTest, InvalidStop);
- FRIEND_TEST(TimerTest, PauseResumeStop);
- FRIEND_TEST(TimerTest, PauseStartStopResume);
- FRIEND_TEST(TimerTest, PauseStop);
- FRIEND_TEST(TimerTest, Reset);
- FRIEND_TEST(TimerTest, ReStart);
- FRIEND_TEST(TimerTest, ResumeStartStopPause);
- FRIEND_TEST(TimerTest, SeparatedTimers);
- FRIEND_TEST(TimerTest, StartPauseResumePauseResumeStop);
- FRIEND_TEST(TimerTest, StartPauseResumePauseStop);
- FRIEND_TEST(TimerTest, StartPauseResumeStop);
- FRIEND_TEST(TimerTest, StartPauseStop);
- FRIEND_TEST(TimerTest, StartResumeStop);
- FRIEND_TEST(TimerTest, StartStop);
-
- // Elapsed time of the last use of the timer.
- base::TimeDelta elapsed_time_;
-
- // Starting time value.
- base::TimeTicks start_time_;
-
- // Whether the timer is running, stopped, or paused.
- TimerState timer_state_;
-
- // Wrapper for the calls to the system clock.
- scoped_ptr<ClockWrapper> clock_wrapper_;
-
- DISALLOW_COPY_AND_ASSIGN(Timer);
-};
-
-// Extends the Timer class to report the elapsed time in milliseconds through
-// the UMA metrics library.
-class TimerReporter : public Timer {
- public:
- // Initializes the timer by providing a |histogram_name| to report to with
- // |min|, |max| and |num_buckets| attributes for the histogram.
- TimerReporter(const std::string& histogram_name, int min, int max,
- int num_buckets);
- virtual ~TimerReporter() {}
-
- // Sets the metrics library used by all instances of this class.
- static void set_metrics_lib(MetricsLibraryInterface* metrics_lib) {
- metrics_lib_ = metrics_lib;
- }
-
- // Reports the current duration to UMA, in milliseconds. Returns false if
- // there is nothing to report, e.g. a metrics library is not set.
- virtual bool ReportMilliseconds() const;
-
- // Accessor methods.
- const std::string& histogram_name() const { return histogram_name_; }
- int min() const { return min_; }
- int max() const { return max_; }
- int num_buckets() const { return num_buckets_; }
-
- private:
- friend class TimerReporterTest;
- FRIEND_TEST(TimerReporterTest, StartStopReport);
- FRIEND_TEST(TimerReporterTest, InvalidReport);
-
- static MetricsLibraryInterface* metrics_lib_;
- std::string histogram_name_;
- int min_;
- int max_;
- int num_buckets_;
-
- DISALLOW_COPY_AND_ASSIGN(TimerReporter);
-};
-
-} // namespace chromeos_metrics
-
-#endif // METRICS_TIMER_H_
diff --git a/metricsd/timer_mock.h b/metricsd/timer_mock.h
deleted file mode 100644
index 8c9e8d8..0000000
--- a/metricsd/timer_mock.h
+++ /dev/null
@@ -1,59 +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 METRICS_TIMER_MOCK_H_
-#define METRICS_TIMER_MOCK_H_
-
-#include <string>
-
-#include <gmock/gmock.h>
-
-#include "timer.h"
-
-namespace chromeos_metrics {
-
-class TimerMock : public Timer {
- public:
- MOCK_METHOD0(Start, bool());
- MOCK_METHOD0(Stop, bool());
- MOCK_METHOD0(Reset, bool());
- MOCK_CONST_METHOD0(HasStarted, bool());
- MOCK_CONST_METHOD1(GetElapsedTime, bool(base::TimeDelta* elapsed_time));
-};
-
-class TimerReporterMock : public TimerReporter {
- public:
- TimerReporterMock() : TimerReporter("", 0, 0, 0) {}
- MOCK_METHOD0(Start, bool());
- MOCK_METHOD0(Stop, bool());
- MOCK_METHOD0(Reset, bool());
- MOCK_CONST_METHOD0(HasStarted, bool());
- MOCK_CONST_METHOD1(GetElapsedTime, bool(base::TimeDelta* elapsed_time));
- MOCK_CONST_METHOD0(ReportMilliseconds, bool());
- MOCK_CONST_METHOD0(histogram_name, std::string&());
- MOCK_CONST_METHOD0(min, int());
- MOCK_CONST_METHOD0(max, int());
- MOCK_CONST_METHOD0(num_buckets, int());
-};
-
-class ClockWrapperMock : public ClockWrapper {
- public:
- MOCK_CONST_METHOD0(GetCurrentTime, base::TimeTicks());
-};
-
-} // namespace chromeos_metrics
-
-#endif // METRICS_TIMER_MOCK_H_
diff --git a/metricsd/timer_test.cc b/metricsd/timer_test.cc
deleted file mode 100644
index ab027d4..0000000
--- a/metricsd/timer_test.cc
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-
-#include <base/memory/scoped_ptr.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "metrics_library_mock.h"
-#include "timer.h"
-#include "timer_mock.h"
-
-using ::testing::_;
-using ::testing::Return;
-
-namespace chromeos_metrics {
-
-namespace {
-const int64_t kStime1MSec = 1400;
-const int64_t kEtime1MSec = 3000;
-const int64_t kDelta1MSec = 1600;
-
-const int64_t kStime2MSec = 4200;
-const int64_t kEtime2MSec = 5000;
-const int64_t kDelta2MSec = 800;
-
-const int64_t kStime3MSec = 6600;
-const int64_t kEtime3MSec = 6800;
-const int64_t kDelta3MSec = 200;
-} // namespace
-
-class TimerTest : public testing::Test {
- public:
- TimerTest() : clock_wrapper_mock_(new ClockWrapperMock()) {}
-
- protected:
- virtual void SetUp() {
- EXPECT_EQ(Timer::kTimerStopped, timer_.timer_state_);
- stime += base::TimeDelta::FromMilliseconds(kStime1MSec);
- etime += base::TimeDelta::FromMilliseconds(kEtime1MSec);
- stime2 += base::TimeDelta::FromMilliseconds(kStime2MSec);
- etime2 += base::TimeDelta::FromMilliseconds(kEtime2MSec);
- stime3 += base::TimeDelta::FromMilliseconds(kStime3MSec);
- etime3 += base::TimeDelta::FromMilliseconds(kEtime3MSec);
- }
-
- virtual void TearDown() {}
-
- Timer timer_;
- scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
- base::TimeTicks stime, etime, stime2, etime2, stime3, etime3;
-};
-
-TEST_F(TimerTest, StartStop) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Start());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
-
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-
- ASSERT_FALSE(timer_.HasStarted());
-}
-
-TEST_F(TimerTest, ReStart) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- timer_.Start();
- base::TimeTicks buffer = timer_.start_time_;
- timer_.Start();
- ASSERT_FALSE(timer_.start_time_ == buffer);
-}
-
-TEST_F(TimerTest, Reset) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- timer_.Start();
- ASSERT_TRUE(timer_.Reset());
- ASSERT_FALSE(timer_.HasStarted());
-}
-
-TEST_F(TimerTest, SeparatedTimers) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime))
- .WillOnce(Return(stime2))
- .WillOnce(Return(etime2));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Start());
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
- ASSERT_TRUE(timer_.Start());
- ASSERT_TRUE(timer_.start_time_ == stime2);
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
- ASSERT_FALSE(timer_.HasStarted());
-
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-}
-
-TEST_F(TimerTest, InvalidStop) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_FALSE(timer_.Stop());
- // Now we try it again, but after a valid start/stop.
- timer_.Start();
- timer_.Stop();
- base::TimeDelta elapsed_time = timer_.elapsed_time_;
- ASSERT_FALSE(timer_.Stop());
- ASSERT_TRUE(elapsed_time == timer_.elapsed_time_);
-}
-
-TEST_F(TimerTest, InvalidElapsedTime) {
- base::TimeDelta elapsed_time;
- ASSERT_FALSE(timer_.GetElapsedTime(&elapsed_time));
-}
-
-TEST_F(TimerTest, PauseStartStopResume) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(stime2))
- .WillOnce(Return(etime2))
- .WillOnce(Return(stime3))
- .WillOnce(Return(etime3));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Pause()); // Starts timer paused.
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Start()); // Restarts timer.
- ASSERT_TRUE(timer_.start_time_ == stime2);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
- ASSERT_FALSE(timer_.HasStarted());
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-
- ASSERT_TRUE(timer_.Resume());
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(kDelta3MSec, elapsed_time.InMilliseconds());
-}
-
-TEST_F(TimerTest, ResumeStartStopPause) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(stime2))
- .WillOnce(Return(etime2))
- .WillOnce(Return(stime3));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Resume());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Start());
- ASSERT_TRUE(timer_.start_time_ == stime2);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
- ASSERT_FALSE(timer_.HasStarted());
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-
- ASSERT_TRUE(timer_.Pause());
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(0, elapsed_time.InMilliseconds());
-}
-
-TEST_F(TimerTest, StartResumeStop) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Start());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_FALSE(timer_.Resume());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
- ASSERT_FALSE(timer_.HasStarted());
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-}
-
-TEST_F(TimerTest, StartPauseStop) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Start());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Pause());
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
- ASSERT_FALSE(timer_.HasStarted());
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-}
-
-TEST_F(TimerTest, StartPauseResumeStop) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime))
- .WillOnce(Return(stime2))
- .WillOnce(Return(etime2));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Start());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Pause());
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-
- ASSERT_TRUE(timer_.Resume());
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec + kDelta2MSec);
- ASSERT_FALSE(timer_.HasStarted());
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-}
-
-TEST_F(TimerTest, PauseStop) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Pause());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 0);
-
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 0);
- ASSERT_FALSE(timer_.HasStarted());
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-}
-
-TEST_F(TimerTest, PauseResumeStop) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(stime2))
- .WillOnce(Return(etime2));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Pause());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Resume());
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
- ASSERT_FALSE(timer_.HasStarted());
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-}
-
-TEST_F(TimerTest, StartPauseResumePauseStop) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime))
- .WillOnce(Return(stime2))
- .WillOnce(Return(stime3))
- .WillOnce(Return(etime3));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Start());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Pause());
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-
- ASSERT_TRUE(timer_.Resume());
- ASSERT_TRUE(timer_.HasStarted());
- // Make sure GetElapsedTime works while we're running.
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(kDelta1MSec + kStime3MSec - kStime2MSec,
- elapsed_time.InMilliseconds());
-
- ASSERT_TRUE(timer_.Pause());
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- kDelta1MSec + kEtime3MSec - kStime2MSec);
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- kDelta1MSec + kEtime3MSec - kStime2MSec);
- ASSERT_FALSE(timer_.HasStarted());
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-}
-
-TEST_F(TimerTest, StartPauseResumePauseResumeStop) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime))
- .WillOnce(Return(stime2))
- .WillOnce(Return(etime2))
- .WillOnce(Return(stime3))
- .WillOnce(Return(etime3));
- timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- ASSERT_TRUE(timer_.Start());
- ASSERT_TRUE(timer_.start_time_ == stime);
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Pause());
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
- base::TimeDelta elapsed_time;
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-
- ASSERT_TRUE(timer_.Resume());
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Pause());
- ASSERT_TRUE(timer_.HasStarted());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec + kDelta2MSec);
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-
- ASSERT_TRUE(timer_.Resume());
- ASSERT_TRUE(timer_.HasStarted());
-
- ASSERT_TRUE(timer_.Stop());
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- kDelta1MSec + kDelta2MSec + kDelta3MSec);
- ASSERT_FALSE(timer_.HasStarted());
- ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
- ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
- elapsed_time.InMilliseconds());
-}
-
-static const char kMetricName[] = "test-timer";
-static const int kMinSample = 0;
-static const int kMaxSample = 120 * 1E6;
-static const int kNumBuckets = 50;
-
-class TimerReporterTest : public testing::Test {
- public:
- TimerReporterTest() : timer_reporter_(kMetricName, kMinSample, kMaxSample,
- kNumBuckets),
- clock_wrapper_mock_(new ClockWrapperMock()) {}
-
- protected:
- virtual void SetUp() {
- timer_reporter_.set_metrics_lib(&lib_);
- EXPECT_EQ(timer_reporter_.histogram_name_, kMetricName);
- EXPECT_EQ(timer_reporter_.min_, kMinSample);
- EXPECT_EQ(timer_reporter_.max_, kMaxSample);
- EXPECT_EQ(timer_reporter_.num_buckets_, kNumBuckets);
- stime += base::TimeDelta::FromMilliseconds(kStime1MSec);
- etime += base::TimeDelta::FromMilliseconds(kEtime1MSec);
- }
-
- virtual void TearDown() {
- timer_reporter_.set_metrics_lib(nullptr);
- }
-
- TimerReporter timer_reporter_;
- MetricsLibraryMock lib_;
- scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
- base::TimeTicks stime, etime;
-};
-
-TEST_F(TimerReporterTest, StartStopReport) {
- EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
- .WillOnce(Return(stime))
- .WillOnce(Return(etime));
- timer_reporter_.clock_wrapper_.reset(clock_wrapper_mock_.release());
- EXPECT_CALL(lib_, SendToUMA(kMetricName, kDelta1MSec, kMinSample, kMaxSample,
- kNumBuckets)).WillOnce(Return(true));
- ASSERT_TRUE(timer_reporter_.Start());
- ASSERT_TRUE(timer_reporter_.Stop());
- ASSERT_TRUE(timer_reporter_.ReportMilliseconds());
-}
-
-TEST_F(TimerReporterTest, InvalidReport) {
- ASSERT_FALSE(timer_reporter_.ReportMilliseconds());
-}
-
-} // namespace chromeos_metrics
-
-int main(int argc, char **argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/metricsd/uploader/metrics_hashes.cc b/metricsd/uploader/metrics_hashes.cc
deleted file mode 100644
index 208c560..0000000
--- a/metricsd/uploader/metrics_hashes.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uploader/metrics_hashes.h"
-
-#include "base/logging.h"
-#include "base/md5.h"
-#include "base/sys_byteorder.h"
-
-namespace metrics {
-
-namespace {
-
-// Converts the 8-byte prefix of an MD5 hash into a uint64 value.
-inline uint64_t HashToUInt64(const std::string& hash) {
- uint64_t value;
- DCHECK_GE(hash.size(), sizeof(value));
- memcpy(&value, hash.data(), sizeof(value));
- return base::HostToNet64(value);
-}
-
-} // namespace
-
-uint64_t HashMetricName(const std::string& name) {
- // Create an MD5 hash of the given |name|, represented as a byte buffer
- // encoded as an std::string.
- base::MD5Context context;
- base::MD5Init(&context);
- base::MD5Update(&context, name);
-
- base::MD5Digest digest;
- base::MD5Final(&digest, &context);
-
- std::string hash_str(reinterpret_cast<char*>(digest.a), arraysize(digest.a));
- return HashToUInt64(hash_str);
-}
-
-} // namespace metrics
diff --git a/metricsd/uploader/metrics_hashes.h b/metricsd/uploader/metrics_hashes.h
deleted file mode 100644
index 1082b42..0000000
--- a/metricsd/uploader/metrics_hashes.h
+++ /dev/null
@@ -1,30 +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 METRICS_UPLOADER_METRICS_HASHES_H_
-#define METRICS_UPLOADER_METRICS_HASHES_H_
-
-#include <string>
-
-namespace metrics {
-
-// Computes a uint64 hash of a given string based on its MD5 hash. Suitable for
-// metric names.
-uint64_t HashMetricName(const std::string& name);
-
-} // namespace metrics
-
-#endif // METRICS_UPLOADER_METRICS_HASHES_H_
diff --git a/metricsd/uploader/metrics_hashes_unittest.cc b/metricsd/uploader/metrics_hashes_unittest.cc
deleted file mode 100644
index b8c2575..0000000
--- a/metricsd/uploader/metrics_hashes_unittest.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uploader/metrics_hashes.h"
-
-#include <base/format_macros.h>
-#include <base/macros.h>
-#include <base/strings/stringprintf.h>
-#include <gtest/gtest.h>
-
-namespace metrics {
-
-// Make sure our ID hashes are the same as what we see on the server side.
-TEST(MetricsUtilTest, HashMetricName) {
- static const struct {
- std::string input;
- std::string output;
- } cases[] = {
- {"Back", "0x0557fa923dcee4d0"},
- {"Forward", "0x67d2f6740a8eaebf"},
- {"NewTab", "0x290eb683f96572f1"},
- };
-
- for (size_t i = 0; i < arraysize(cases); ++i) {
- uint64_t hash = HashMetricName(cases[i].input);
- std::string hash_hex = base::StringPrintf("0x%016" PRIx64, hash);
- EXPECT_EQ(cases[i].output, hash_hex);
- }
-}
-
-} // namespace metrics
diff --git a/metricsd/uploader/metrics_log.cc b/metricsd/uploader/metrics_log.cc
deleted file mode 100644
index 5f4c599..0000000
--- a/metricsd/uploader/metrics_log.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uploader/metrics_log.h"
-
-#include <string>
-
-#include "uploader/proto/system_profile.pb.h"
-#include "uploader/system_profile_setter.h"
-
-// We use default values for the MetricsLogBase constructor as the setter will
-// override them.
-MetricsLog::MetricsLog()
- : MetricsLogBase("", 0, metrics::MetricsLogBase::ONGOING_LOG, "") {
-}
-
-void MetricsLog::IncrementUserCrashCount() {
- metrics::SystemProfileProto::Stability* stability(
- uma_proto()->mutable_system_profile()->mutable_stability());
- int current = stability->other_user_crash_count();
- stability->set_other_user_crash_count(current + 1);
-}
-
-void MetricsLog::IncrementKernelCrashCount() {
- metrics::SystemProfileProto::Stability* stability(
- uma_proto()->mutable_system_profile()->mutable_stability());
- int current = stability->kernel_crash_count();
- stability->set_kernel_crash_count(current + 1);
-}
-
-void MetricsLog::IncrementUncleanShutdownCount() {
- metrics::SystemProfileProto::Stability* stability(
- uma_proto()->mutable_system_profile()->mutable_stability());
- int current = stability->unclean_system_shutdown_count();
- stability->set_unclean_system_shutdown_count(current + 1);
-}
-
-void MetricsLog::PopulateSystemProfile(SystemProfileSetter* profile_setter) {
- profile_setter->Populate(uma_proto());
-}
diff --git a/metricsd/uploader/metrics_log.h b/metricsd/uploader/metrics_log.h
deleted file mode 100644
index 50fed89..0000000
--- a/metricsd/uploader/metrics_log.h
+++ /dev/null
@@ -1,54 +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 METRICS_UPLOADER_METRICS_LOG_H_
-#define METRICS_UPLOADER_METRICS_LOG_H_
-
-#include <string>
-
-#include <base/macros.h>
-
-#include "uploader/metrics_log_base.h"
-
-// This file defines a set of user experience metrics data recorded by
-// the MetricsService. This is the unit of data that is sent to the server.
-class SystemProfileSetter;
-
-// This class provides base functionality for logging metrics data.
-class MetricsLog : public metrics::MetricsLogBase {
- public:
- // The constructor doesn't set any metadata. The metadata is only set by a
- // SystemProfileSetter.
- MetricsLog();
-
- void IncrementUserCrashCount();
- void IncrementKernelCrashCount();
- void IncrementUncleanShutdownCount();
-
- // Populate the system profile with system information using setter.
- void PopulateSystemProfile(SystemProfileSetter* setter);
-
- private:
- FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
- FRIEND_TEST(UploadServiceTest, LogKernelCrash);
- FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
- FRIEND_TEST(UploadServiceTest, LogUserCrash);
- FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
-
- DISALLOW_COPY_AND_ASSIGN(MetricsLog);
-};
-
-#endif // METRICS_UPLOADER_METRICS_LOG_H_
diff --git a/metricsd/uploader/metrics_log_base.cc b/metricsd/uploader/metrics_log_base.cc
deleted file mode 100644
index ee325ae..0000000
--- a/metricsd/uploader/metrics_log_base.cc
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uploader/metrics_log_base.h"
-
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_samples.h"
-#include "uploader/metrics_hashes.h"
-#include "uploader/proto/histogram_event.pb.h"
-#include "uploader/proto/system_profile.pb.h"
-#include "uploader/proto/user_action_event.pb.h"
-
-using base::Histogram;
-using base::HistogramBase;
-using base::HistogramSamples;
-using base::SampleCountIterator;
-using base::Time;
-using base::TimeDelta;
-using metrics::HistogramEventProto;
-using metrics::SystemProfileProto;
-using metrics::UserActionEventProto;
-
-namespace metrics {
-namespace {
-
-// Any id less than 16 bytes is considered to be a testing id.
-bool IsTestingID(const std::string& id) {
- return id.size() < 16;
-}
-
-} // namespace
-
-MetricsLogBase::MetricsLogBase(const std::string& client_id,
- int session_id,
- LogType log_type,
- const std::string& version_string)
- : num_events_(0),
- locked_(false),
- log_type_(log_type) {
- DCHECK_NE(NO_LOG, log_type);
- if (IsTestingID(client_id))
- uma_proto_.set_client_id(0);
- else
- uma_proto_.set_client_id(Hash(client_id));
-
- uma_proto_.set_session_id(session_id);
- uma_proto_.mutable_system_profile()->set_build_timestamp(GetBuildTime());
- uma_proto_.mutable_system_profile()->set_app_version(version_string);
-}
-
-MetricsLogBase::~MetricsLogBase() {}
-
-// static
-uint64_t MetricsLogBase::Hash(const std::string& value) {
- uint64_t hash = metrics::HashMetricName(value);
-
- // The following log is VERY helpful when folks add some named histogram into
- // the code, but forgot to update the descriptive list of histograms. When
- // that happens, all we get to see (server side) is a hash of the histogram
- // name. We can then use this logging to find out what histogram name was
- // being hashed to a given MD5 value by just running the version of Chromium
- // in question with --enable-logging.
- DVLOG(1) << "Metrics: Hash numeric [" << value << "]=[" << hash << "]";
-
- return hash;
-}
-
-// static
-int64_t MetricsLogBase::GetBuildTime() {
- static int64_t integral_build_time = 0;
- if (!integral_build_time) {
- Time time;
- const char* kDateTime = __DATE__ " " __TIME__ " GMT";
- bool result = Time::FromString(kDateTime, &time);
- DCHECK(result);
- integral_build_time = static_cast<int64_t>(time.ToTimeT());
- }
- return integral_build_time;
-}
-
-// static
-int64_t MetricsLogBase::GetCurrentTime() {
- return (base::TimeTicks::Now() - base::TimeTicks()).InSeconds();
-}
-
-void MetricsLogBase::CloseLog() {
- DCHECK(!locked_);
- locked_ = true;
-}
-
-void MetricsLogBase::GetEncodedLog(std::string* encoded_log) {
- DCHECK(locked_);
- uma_proto_.SerializeToString(encoded_log);
-}
-
-void MetricsLogBase::RecordUserAction(const std::string& key) {
- DCHECK(!locked_);
-
- UserActionEventProto* user_action = uma_proto_.add_user_action_event();
- user_action->set_name_hash(Hash(key));
- user_action->set_time(GetCurrentTime());
-
- ++num_events_;
-}
-
-void MetricsLogBase::RecordHistogramDelta(const std::string& histogram_name,
- const HistogramSamples& snapshot) {
- DCHECK(!locked_);
- DCHECK_NE(0, snapshot.TotalCount());
-
- // We will ignore the MAX_INT/infinite value in the last element of range[].
-
- HistogramEventProto* histogram_proto = uma_proto_.add_histogram_event();
- histogram_proto->set_name_hash(Hash(histogram_name));
- histogram_proto->set_sum(snapshot.sum());
-
- for (scoped_ptr<SampleCountIterator> it = snapshot.Iterator(); !it->Done();
- it->Next()) {
- HistogramBase::Sample min;
- HistogramBase::Sample max;
- HistogramBase::Count count;
- it->Get(&min, &max, &count);
- HistogramEventProto::Bucket* bucket = histogram_proto->add_bucket();
- bucket->set_min(min);
- bucket->set_max(max);
- bucket->set_count(count);
- }
-
- // Omit fields to save space (see rules in histogram_event.proto comments).
- for (int i = 0; i < histogram_proto->bucket_size(); ++i) {
- HistogramEventProto::Bucket* bucket = histogram_proto->mutable_bucket(i);
- if (i + 1 < histogram_proto->bucket_size() &&
- bucket->max() == histogram_proto->bucket(i + 1).min()) {
- bucket->clear_max();
- } else if (bucket->max() == bucket->min() + 1) {
- bucket->clear_min();
- }
- }
-}
-
-} // namespace metrics
diff --git a/metricsd/uploader/metrics_log_base.h b/metricsd/uploader/metrics_log_base.h
deleted file mode 100644
index f4e1995..0000000
--- a/metricsd/uploader/metrics_log_base.h
+++ /dev/null
@@ -1,122 +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.
- */
-
-// This file defines a set of user experience metrics data recorded by
-// the MetricsService. This is the unit of data that is sent to the server.
-
-#ifndef METRICS_UPLOADER_METRICS_LOG_BASE_H_
-#define METRICS_UPLOADER_METRICS_LOG_BASE_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/metrics/histogram.h"
-#include "base/time/time.h"
-#include "uploader/proto/chrome_user_metrics_extension.pb.h"
-
-namespace base {
-class HistogramSamples;
-} // namespace base
-
-namespace metrics {
-
-// This class provides base functionality for logging metrics data.
-class MetricsLogBase {
- public:
- // TODO(asvitkine): Remove the NO_LOG value.
- enum LogType {
- INITIAL_STABILITY_LOG, // The initial log containing stability stats.
- ONGOING_LOG, // Subsequent logs in a session.
- NO_LOG, // Placeholder value for when there is no log.
- };
-
- // Creates a new metrics log of the specified type.
- // client_id is the identifier for this profile on this installation
- // session_id is an integer that's incremented on each application launch
- MetricsLogBase(const std::string& client_id,
- int session_id,
- LogType log_type,
- const std::string& version_string);
- virtual ~MetricsLogBase();
-
- // Computes the MD5 hash of the given string, and returns the first 8 bytes of
- // the hash.
- static uint64_t Hash(const std::string& value);
-
- // Get the GMT buildtime for the current binary, expressed in seconds since
- // January 1, 1970 GMT.
- // The value is used to identify when a new build is run, so that previous
- // reliability stats, from other builds, can be abandoned.
- static int64_t GetBuildTime();
-
- // Convenience function to return the current time at a resolution in seconds.
- // This wraps base::TimeTicks, and hence provides an abstract time that is
- // always incrementing for use in measuring time durations.
- static int64_t GetCurrentTime();
-
- // Records a user-initiated action.
- void RecordUserAction(const std::string& key);
-
- // Record any changes in a given histogram for transmission.
- void RecordHistogramDelta(const std::string& histogram_name,
- const base::HistogramSamples& snapshot);
-
- // Stop writing to this record and generate the encoded representation.
- // None of the Record* methods can be called after this is called.
- void CloseLog();
-
- // Fills |encoded_log| with the serialized protobuf representation of the
- // record. Must only be called after CloseLog() has been called.
- void GetEncodedLog(std::string* encoded_log);
-
- int num_events() { return num_events_; }
-
- void set_hardware_class(const std::string& hardware_class) {
- uma_proto_.mutable_system_profile()->mutable_hardware()->set_hardware_class(
- hardware_class);
- }
-
- LogType log_type() const { return log_type_; }
-
- protected:
- bool locked() const { return locked_; }
-
- metrics::ChromeUserMetricsExtension* uma_proto() { return &uma_proto_; }
- const metrics::ChromeUserMetricsExtension* uma_proto() const {
- return &uma_proto_;
- }
-
- // TODO(isherman): Remove this once the XML pipeline is outta here.
- int num_events_; // the number of events recorded in this log
-
- private:
- // locked_ is true when record has been packed up for sending, and should
- // no longer be written to. It is only used for sanity checking and is
- // not a real lock.
- bool locked_;
-
- // The type of the log, i.e. initial or ongoing.
- const LogType log_type_;
-
- // Stores the protocol buffer representation for this log.
- metrics::ChromeUserMetricsExtension uma_proto_;
-
- DISALLOW_COPY_AND_ASSIGN(MetricsLogBase);
-};
-
-} // namespace metrics
-
-#endif // METRICS_UPLOADER_METRICS_LOG_BASE_H_
diff --git a/metricsd/uploader/metrics_log_base_unittest.cc b/metricsd/uploader/metrics_log_base_unittest.cc
deleted file mode 100644
index 980afd5..0000000
--- a/metricsd/uploader/metrics_log_base_unittest.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uploader/metrics_log_base.h"
-
-#include <string>
-
-#include <base/metrics/bucket_ranges.h>
-#include <base/metrics/sample_vector.h>
-#include <gtest/gtest.h>
-
-#include "uploader/proto/chrome_user_metrics_extension.pb.h"
-
-namespace metrics {
-
-namespace {
-
-class TestMetricsLogBase : public MetricsLogBase {
- public:
- TestMetricsLogBase()
- : MetricsLogBase("client_id", 1, MetricsLogBase::ONGOING_LOG, "1.2.3.4") {
- }
- virtual ~TestMetricsLogBase() {}
-
- using MetricsLogBase::uma_proto;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestMetricsLogBase);
-};
-
-} // namespace
-
-TEST(MetricsLogBaseTest, LogType) {
- MetricsLogBase log1("id", 0, MetricsLogBase::ONGOING_LOG, "1.2.3");
- EXPECT_EQ(MetricsLogBase::ONGOING_LOG, log1.log_type());
-
- MetricsLogBase log2("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "1.2.3");
- EXPECT_EQ(MetricsLogBase::INITIAL_STABILITY_LOG, log2.log_type());
-}
-
-TEST(MetricsLogBaseTest, EmptyRecord) {
- MetricsLogBase log("totally bogus client ID", 137,
- MetricsLogBase::ONGOING_LOG, "bogus version");
- log.set_hardware_class("sample-class");
- log.CloseLog();
-
- std::string encoded;
- log.GetEncodedLog(&encoded);
-
- // A couple of fields are hard to mock, so these will be copied over directly
- // for the expected output.
- metrics::ChromeUserMetricsExtension parsed;
- ASSERT_TRUE(parsed.ParseFromString(encoded));
-
- metrics::ChromeUserMetricsExtension expected;
- expected.set_client_id(5217101509553811875); // Hashed bogus client ID
- expected.set_session_id(137);
- expected.mutable_system_profile()->set_build_timestamp(
- parsed.system_profile().build_timestamp());
- expected.mutable_system_profile()->set_app_version("bogus version");
- expected.mutable_system_profile()->mutable_hardware()->set_hardware_class(
- "sample-class");
-
- EXPECT_EQ(expected.SerializeAsString(), encoded);
-}
-
-TEST(MetricsLogBaseTest, HistogramBucketFields) {
- // Create buckets: 1-5, 5-7, 7-8, 8-9, 9-10, 10-11, 11-12.
- base::BucketRanges ranges(8);
- ranges.set_range(0, 1);
- ranges.set_range(1, 5);
- ranges.set_range(2, 7);
- ranges.set_range(3, 8);
- ranges.set_range(4, 9);
- ranges.set_range(5, 10);
- ranges.set_range(6, 11);
- ranges.set_range(7, 12);
-
- base::SampleVector samples(&ranges);
- samples.Accumulate(3, 1); // Bucket 1-5.
- samples.Accumulate(6, 1); // Bucket 5-7.
- samples.Accumulate(8, 1); // Bucket 8-9. (7-8 skipped)
- samples.Accumulate(10, 1); // Bucket 10-11. (9-10 skipped)
- samples.Accumulate(11, 1); // Bucket 11-12.
-
- TestMetricsLogBase log;
- log.RecordHistogramDelta("Test", samples);
-
- const metrics::ChromeUserMetricsExtension* uma_proto = log.uma_proto();
- const metrics::HistogramEventProto& histogram_proto =
- uma_proto->histogram_event(uma_proto->histogram_event_size() - 1);
-
- // Buckets with samples: 1-5, 5-7, 8-9, 10-11, 11-12.
- // Should become: 1-/, 5-7, /-9, 10-/, /-12.
- ASSERT_EQ(5, histogram_proto.bucket_size());
-
- // 1-5 becomes 1-/ (max is same as next min).
- EXPECT_TRUE(histogram_proto.bucket(0).has_min());
- EXPECT_FALSE(histogram_proto.bucket(0).has_max());
- EXPECT_EQ(1, histogram_proto.bucket(0).min());
-
- // 5-7 stays 5-7 (no optimization possible).
- EXPECT_TRUE(histogram_proto.bucket(1).has_min());
- EXPECT_TRUE(histogram_proto.bucket(1).has_max());
- EXPECT_EQ(5, histogram_proto.bucket(1).min());
- EXPECT_EQ(7, histogram_proto.bucket(1).max());
-
- // 8-9 becomes /-9 (min is same as max - 1).
- EXPECT_FALSE(histogram_proto.bucket(2).has_min());
- EXPECT_TRUE(histogram_proto.bucket(2).has_max());
- EXPECT_EQ(9, histogram_proto.bucket(2).max());
-
- // 10-11 becomes 10-/ (both optimizations apply, omit max is prioritized).
- EXPECT_TRUE(histogram_proto.bucket(3).has_min());
- EXPECT_FALSE(histogram_proto.bucket(3).has_max());
- EXPECT_EQ(10, histogram_proto.bucket(3).min());
-
- // 11-12 becomes /-12 (last record must keep max, min is same as max - 1).
- EXPECT_FALSE(histogram_proto.bucket(4).has_min());
- EXPECT_TRUE(histogram_proto.bucket(4).has_max());
- EXPECT_EQ(12, histogram_proto.bucket(4).max());
-}
-
-} // namespace metrics
diff --git a/metricsd/uploader/mock/mock_system_profile_setter.h b/metricsd/uploader/mock/mock_system_profile_setter.h
deleted file mode 100644
index c714e9c..0000000
--- a/metricsd/uploader/mock/mock_system_profile_setter.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
-#define METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
-
-#include "uploader/system_profile_setter.h"
-
-namespace metrics {
-class ChromeUserMetricsExtension;
-}
-
-// Mock profile setter used for testing.
-class MockSystemProfileSetter : public SystemProfileSetter {
- public:
- void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override {}
-};
-
-#endif // METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
diff --git a/metricsd/uploader/mock/sender_mock.cc b/metricsd/uploader/mock/sender_mock.cc
deleted file mode 100644
index bb4dc7d..0000000
--- a/metricsd/uploader/mock/sender_mock.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uploader/mock/sender_mock.h"
-
-SenderMock::SenderMock() {
- Reset();
-}
-
-bool SenderMock::Send(const std::string& content, const std::string& hash) {
- send_call_count_ += 1;
- last_message_ = content;
- is_good_proto_ = last_message_proto_.ParseFromString(content);
- return should_succeed_;
-}
-
-void SenderMock::Reset() {
- send_call_count_ = 0;
- last_message_ = "";
- should_succeed_ = true;
- last_message_proto_.Clear();
- is_good_proto_ = false;
-}
diff --git a/metricsd/uploader/mock/sender_mock.h b/metricsd/uploader/mock/sender_mock.h
deleted file mode 100644
index e79233f..0000000
--- a/metricsd/uploader/mock/sender_mock.h
+++ /dev/null
@@ -1,60 +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 METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
-#define METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "uploader/proto/chrome_user_metrics_extension.pb.h"
-#include "uploader/sender.h"
-
-class SenderMock : public Sender {
- public:
- SenderMock();
-
- bool Send(const std::string& content, const std::string& hash) override;
- void Reset();
-
- bool is_good_proto() { return is_good_proto_; }
- int send_call_count() { return send_call_count_; }
- const std::string last_message() { return last_message_; }
- metrics::ChromeUserMetricsExtension last_message_proto() {
- return last_message_proto_;
- }
- void set_should_succeed(bool succeed) { should_succeed_ = succeed; }
-
- private:
- // Is set to true if the proto was parsed successfully.
- bool is_good_proto_;
-
- // If set to true, the Send method will return true to simulate a successful
- // send.
- bool should_succeed_;
-
- // Count of how many times Send was called since the last reset.
- int send_call_count_;
-
- // Last message received by Send.
- std::string last_message_;
-
- // If is_good_proto is true, last_message_proto is the deserialized
- // representation of last_message.
- metrics::ChromeUserMetricsExtension last_message_proto_;
-};
-
-#endif // METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
diff --git a/metricsd/uploader/proto/README b/metricsd/uploader/proto/README
deleted file mode 100644
index 4292a40..0000000
--- a/metricsd/uploader/proto/README
+++ /dev/null
@@ -1,37 +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.
-
-
-
-
-This directory contains the protocol buffers used by the standalone metrics
-uploader. Those protobuffers are copied from the chromium protobuffers from
-https://chromium.googlesource.com/chromium/src/+/master/components/metrics/proto/
-at 3bfe5f2b4c03d2cac718d137ed14cd2c6354bfed.
-
-Any change to this protobuf must first be made to the backend's protobuf and be
-compatible with the chromium protobuffers.
-
-
-Q: Why fork the chromium protobuffers ?
-A: The standalone metrics uploader needs chromium os fields that are not defined
-by the chromium protobufs. Instead of pushing chromium os specific changes to
-chromium, we can add them only to chromium os (and to the backend of course).
-
-
-Q: What's the difference between those protobuffers and chromium's protobuffers?
-A: When the protobuffers were copied, some chromium specific protobuffers were
-not imported:
-* omnibox related protobuffers.
-* performance profiling protobuffers (not used in chromium os).
diff --git a/metricsd/uploader/proto/chrome_user_metrics_extension.proto b/metricsd/uploader/proto/chrome_user_metrics_extension.proto
deleted file mode 100644
index a07830f..0000000
--- a/metricsd/uploader/proto/chrome_user_metrics_extension.proto
+++ /dev/null
@@ -1,72 +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.
- */
-//
-// Protocol buffer for Chrome UMA (User Metrics Analysis).
-//
-// Note: this protobuf must be compatible with the one in chromium.
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-option java_outer_classname = "ChromeUserMetricsExtensionProtos";
-option java_package = "org.chromium.components.metrics";
-
-package metrics;
-
-import "system/core/metricsd/uploader/proto/histogram_event.proto";
-import "system/core/metricsd/uploader/proto/system_profile.proto";
-import "system/core/metricsd/uploader/proto/user_action_event.proto";
-
-// Next tag: 13
-message ChromeUserMetricsExtension {
- // The product (i.e. end user application) for a given UMA log.
- enum Product {
- // Google Chrome product family.
- CHROME = 0;
- }
- // The product corresponding to this log. The field type is int32 instead of
- // Product so that downstream users of the Chromium metrics component can
- // introduce products without needing to make changes to the Chromium code
- // (though they still need to add the new product to the server-side enum).
- // Note: The default value is Chrome, so Chrome products will not transmit
- // this field.
- optional int32 product = 10 [default = 0];
-
- // The id of the client install that generated these events.
- //
- // For Chrome clients, this id is unique to a top-level (one level above the
- // "Default" directory) Chrome user data directory [1], and so is shared among
- // all Chrome user profiles contained in this user data directory.
- // An id of 0 is reserved for test data (monitoring and internal testing) and
- // should normally be ignored in analysis of the data.
- // [1] http://www.chromium.org/user-experience/user-data-directory
- optional fixed64 client_id = 1;
-
- // The session id for this user.
- // Values such as tab ids are only meaningful within a particular session.
- // The client keeps track of the session id and sends it with each event.
- // The session id is simply an integer that is incremented each time the user
- // relaunches Chrome.
- optional int32 session_id = 2;
-
- // Information about the user's browser and system configuration.
- optional SystemProfileProto system_profile = 3;
-
- // This message will log one or more of the following event types:
- repeated UserActionEventProto user_action_event = 4;
- repeated HistogramEventProto histogram_event = 6;
-
-}
diff --git a/metricsd/uploader/proto/histogram_event.proto b/metricsd/uploader/proto/histogram_event.proto
deleted file mode 100644
index 3825063..0000000
--- a/metricsd/uploader/proto/histogram_event.proto
+++ /dev/null
@@ -1,59 +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.
- */
-//
-// Histogram-collected metrics.
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-option java_outer_classname = "HistogramEventProtos";
-option java_package = "org.chromium.components.metrics";
-
-package metrics;
-
-// Next tag: 4
-message HistogramEventProto {
- // The name of the histogram, hashed.
- optional fixed64 name_hash = 1;
-
- // The sum of all the sample values.
- // Together with the total count of the sample values, this allows us to
- // compute the average value. The count of all sample values is just the sum
- // of the counts of all the buckets.
- optional int64 sum = 2;
-
- // The per-bucket data.
- message Bucket {
- // Each bucket's range is bounded by min <= x < max.
- // It is valid to omit one of these two fields in a bucket, but not both.
- // If the min field is omitted, its value is assumed to be equal to max - 1.
- // If the max field is omitted, its value is assumed to be equal to the next
- // bucket's min value (possibly computed per above). The last bucket in a
- // histogram should always include the max field.
- optional int64 min = 1;
- optional int64 max = 2;
-
- // The bucket's index in the list of buckets, sorted in ascending order.
- // This field was intended to provide extra redundancy to detect corrupted
- // records, but was never used. As of M31, it is no longer sent by Chrome
- // clients to reduce the UMA upload size.
- optional int32 bucket_index = 3 [deprecated = true];
-
- // The number of entries in this bucket.
- optional int64 count = 4;
- }
- repeated Bucket bucket = 3;
-}
diff --git a/metricsd/uploader/proto/system_profile.proto b/metricsd/uploader/proto/system_profile.proto
deleted file mode 100644
index 4cab0d9..0000000
--- a/metricsd/uploader/proto/system_profile.proto
+++ /dev/null
@@ -1,759 +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.
- */
-//
-// Stores information about the user's brower and system configuration.
-// The system configuration fields are recorded once per client session.
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-option java_outer_classname = "SystemProfileProtos";
-option java_package = "org.chromium.components.metrics";
-
-package metrics;
-
-// Next tag: 21
-message SystemProfileProto {
- // The time when the client was compiled/linked, in seconds since the epoch.
- optional int64 build_timestamp = 1;
-
- // A version number string for the application.
- // Most commonly this is the browser version number found in a user agent
- // string, and is typically a 4-tuple of numbers separated by periods. In
- // cases where the user agent version might be ambiguous (example: Linux 64-
- // bit build, rather than 32-bit build, or a Windows version used in some
- // special context, such as ChromeFrame running in IE), then this may include
- // some additional postfix to provide clarification not available in the UA
- // string.
- //
- // An example of a browser version 4-tuple is "5.0.322.0". Currently used
- // postfixes are:
- //
- // "-64": a 64-bit build
- // "-F": Chrome is running under control of ChromeFrame
- // "-devel": this is not an official build of Chrome
- //
- // A full version number string could look similar to:
- // "5.0.322.0-F-devel".
- //
- // This value, when available, is more trustworthy than the UA string
- // associated with the request; and including the postfix, may be more
- // specific.
- optional string app_version = 2;
-
- // The brand code or distribution tag assigned to a partner, if available.
- // Brand codes are only available on Windows. Not every Windows install
- // though will have a brand code.
- optional string brand_code = 12;
-
- // The possible channels for an installation, from least to most stable.
- enum Channel {
- CHANNEL_UNKNOWN = 0; // Unknown channel -- perhaps an unofficial build?
- CHANNEL_CANARY = 1;
- CHANNEL_DEV = 2;
- CHANNEL_BETA = 3;
- CHANNEL_STABLE = 4;
- }
- optional Channel channel = 10;
-
- // True if Chrome build is ASan-instrumented.
- optional bool is_asan_build = 20 [default = false];
-
- // The date the user enabled UMA, in seconds since the epoch.
- // If the user has toggled the UMA enabled state multiple times, this will
- // be the most recent date on which UMA was enabled.
- // For privacy, this is rounded to the nearest hour.
- optional int64 uma_enabled_date = 3;
-
- // The time when the client was installed, in seconds since the epoch.
- // For privacy, this is rounded to the nearest hour.
- optional int64 install_date = 16;
-
- // The user's selected application locale, i.e. the user interface language.
- // The locale includes a language code and, possibly, also a country code,
- // e.g. "en-US".
- optional string application_locale = 4;
-
- message BrilloDeviceData {
- optional string build_target_id = 1;
- }
- optional BrilloDeviceData brillo = 21;
-
- // Information on the user's operating system.
- message OS {
- // The user's operating system. This should be one of:
- // - Android
- // - Windows NT
- // - Linux (includes ChromeOS)
- // - iPhone OS
- // - Mac OS X
- optional string name = 1;
-
- // The version of the OS. The meaning of this field is OS-dependent.
- optional string version = 2;
-
- // The fingerprint of the build. This field is used only on Android.
- optional string fingerprint = 3;
-
- // Whether the version of iOS appears to be "jailbroken". This field is
- // used only on iOS. Chrome for iOS detects whether device contains a
- // DynamicLibraries/ directory. It's a necessary but insufficient indicator
- // of whether the operating system has been jailbroken.
- optional bool is_jailbroken = 4;
- }
- optional OS os = 5;
-
- // Next tag for Hardware: 18
- // Information on the user's hardware.
- message Hardware {
- // The CPU architecture (x86, PowerPC, x86_64, ...)
- optional string cpu_architecture = 1;
-
- // The amount of RAM present on the system, in megabytes.
- optional int64 system_ram_mb = 2;
-
- // The base memory address that chrome.dll was loaded at.
- // (Logged only on Windows.)
- optional int64 dll_base = 3;
-
- // The Chrome OS device hardware class ID is a unique string associated with
- // each Chrome OS device product revision generally assigned at hardware
- // qualification time. The hardware class effectively identifies the
- // configured system components such as CPU, WiFi adapter, etc.
- //
- // An example of such a hardware class is "IEC MARIO PONY 6101". An
- // internal database associates this hardware class with the qualified
- // device specifications including OEM information, schematics, hardware
- // qualification reports, test device tags, etc.
- optional string hardware_class = 4;
-
- // The number of physical screens.
- optional int32 screen_count = 5;
-
- // The screen dimensions of the primary screen, in pixels.
- optional int32 primary_screen_width = 6;
- optional int32 primary_screen_height = 7;
-
- // The device scale factor of the primary screen.
- optional float primary_screen_scale_factor = 12;
-
- // Max DPI for any attached screen. (Windows only)
- optional float max_dpi_x = 9;
- optional float max_dpi_y = 10;
-
- // Information on the CPU obtained by CPUID.
- message CPU {
- // A 12 character string naming the vendor, e.g. "GeniuneIntel".
- optional string vendor_name = 1;
-
- // The signature reported by CPUID (from EAX).
- optional uint32 signature = 2;
-
- // Number of logical processors/cores on the current machine.
- optional uint32 num_cores = 3;
- }
- optional CPU cpu = 13;
-
- // Information on the GPU
- message Graphics {
- // The GPU manufacturer's vendor id.
- optional uint32 vendor_id = 1;
-
- // The GPU manufacturer's device id for the chip set.
- optional uint32 device_id = 2;
-
- // The driver version on the GPU.
- optional string driver_version = 3;
-
- // The driver date on the GPU.
- optional string driver_date = 4;
-
- // The GL_VENDOR string. An example of a gl_vendor string is
- // "Imagination Technologies". "" if we are not using OpenGL.
- optional string gl_vendor = 6;
-
- // The GL_RENDERER string. An example of a gl_renderer string is
- // "PowerVR SGX 540". "" if we are not using OpenGL.
- optional string gl_renderer = 7;
- }
- optional Graphics gpu = 8;
-
- // Information about Bluetooth devices paired with the system.
- message Bluetooth {
- // Whether Bluetooth is present on this system.
- optional bool is_present = 1;
-
- // Whether Bluetooth is enabled on this system.
- optional bool is_enabled = 2;
-
- // Describes a paired device.
- message PairedDevice {
- // Assigned class of the device. This is a bitfield according to the
- // Bluetooth specification available at the following URL:
- // https://www.bluetooth.org/en-us/specification/assigned-numbers-overview/baseband
- optional uint32 bluetooth_class = 1;
-
- // Decoded device type.
- enum Type {
- DEVICE_UNKNOWN = 0;
- DEVICE_COMPUTER = 1;
- DEVICE_PHONE = 2;
- DEVICE_MODEM = 3;
- DEVICE_AUDIO = 4;
- DEVICE_CAR_AUDIO = 5;
- DEVICE_VIDEO = 6;
- DEVICE_PERIPHERAL = 7;
- DEVICE_JOYSTICK = 8;
- DEVICE_GAMEPAD = 9;
- DEVICE_KEYBOARD = 10;
- DEVICE_MOUSE = 11;
- DEVICE_TABLET = 12;
- DEVICE_KEYBOARD_MOUSE_COMBO = 13;
- }
- optional Type type = 2;
-
- // Vendor prefix of the Bluetooth address, these are OUI registered by
- // the IEEE and are encoded with the first byte in bits 16-23, the
- // second byte in bits 8-15 and the third byte in bits 0-7.
- //
- // ie. Google's OUI (00:1A:11) is encoded as 0x00001A11
- optional uint32 vendor_prefix = 4;
-
- // The Vendor ID of a device, returned in vendor_id below, can be
- // either allocated by the Bluetooth SIG or USB IF, providing two
- // completely overlapping namespaces for identifiers.
- //
- // This field should be read along with vendor_id to correctly
- // identify the vendor. For example Google is identified by either
- // vendor_id_source = VENDOR_ID_BLUETOOTH, vendor_id = 0x00E0 or
- // vendor_id_source = VENDOR_ID_USB, vendor_id = 0x18D1.
- //
- // If the device does not support the Device ID specification the
- // unknown value will be set.
- enum VendorIDSource {
- VENDOR_ID_UNKNOWN = 0;
- VENDOR_ID_BLUETOOTH = 1;
- VENDOR_ID_USB = 2;
- }
- optional VendorIDSource vendor_id_source = 8;
-
- // Vendor ID of the device, where available.
- optional uint32 vendor_id = 5;
-
- // Product ID of the device, where available.
- optional uint32 product_id = 6;
-
- // Device ID of the device, generally the release or version number in
- // BCD format, where available.
- optional uint32 device_id = 7;
- }
- repeated PairedDevice paired_device = 3;
- }
- optional Bluetooth bluetooth = 11;
-
- // Whether the internal display produces touch events. Omitted if unknown.
- // Logged on ChromeOS only.
- optional bool internal_display_supports_touch = 14;
-
- // Vendor ids and product ids of external touchscreens.
- message TouchScreen {
- // Touch screen vendor id.
- optional uint32 vendor_id = 1;
- // Touch screen product id.
- optional uint32 product_id = 2;
- }
- // Lists vendor and product ids of external touchscreens.
- // Logged on ChromeOS only.
- repeated TouchScreen external_touchscreen = 15;
-
- // Drive messages are currently logged on Windows 7+, iOS, and Android.
- message Drive {
- // Whether this drive incurs a time penalty when randomly accessed. This
- // should be true for spinning disks but false for SSDs or other
- // flash-based drives.
- optional bool has_seek_penalty = 1;
- }
- // The drive that the application executable was loaded from.
- optional Drive app_drive = 16;
- // The drive that the current user data directory was loaded from.
- optional Drive user_data_drive = 17;
- }
- optional Hardware hardware = 6;
-
- // Information about the network connection.
- message Network {
- // Set to true if connection_type changed during the lifetime of the log.
- optional bool connection_type_is_ambiguous = 1;
-
- // See net::NetworkChangeNotifier::ConnectionType.
- enum ConnectionType {
- CONNECTION_UNKNOWN = 0;
- CONNECTION_ETHERNET = 1;
- CONNECTION_WIFI = 2;
- CONNECTION_2G = 3;
- CONNECTION_3G = 4;
- CONNECTION_4G = 5;
- CONNECTION_BLUETOOTH = 6;
- }
- // The connection type according to NetworkChangeNotifier.
- optional ConnectionType connection_type = 2;
-
- // Set to true if wifi_phy_layer_protocol changed during the lifetime of the log.
- optional bool wifi_phy_layer_protocol_is_ambiguous = 3;
-
- // See net::WifiPHYLayerProtocol.
- enum WifiPHYLayerProtocol {
- WIFI_PHY_LAYER_PROTOCOL_NONE = 0;
- WIFI_PHY_LAYER_PROTOCOL_ANCIENT = 1;
- WIFI_PHY_LAYER_PROTOCOL_A = 2;
- WIFI_PHY_LAYER_PROTOCOL_B = 3;
- WIFI_PHY_LAYER_PROTOCOL_G = 4;
- WIFI_PHY_LAYER_PROTOCOL_N = 5;
- WIFI_PHY_LAYER_PROTOCOL_UNKNOWN = 6;
- }
- // The physical layer mode of the associated wifi access point, if any.
- optional WifiPHYLayerProtocol wifi_phy_layer_protocol = 4;
-
- // Describe wifi access point information.
- message WifiAccessPoint {
- // Vendor prefix of the access point's BSSID, these are OUIs
- // (Organizationally Unique Identifiers) registered by
- // the IEEE and are encoded with the first byte in bits 16-23, the
- // second byte in bits 8-15 and the third byte in bits 0-7.
- optional uint32 vendor_prefix = 1;
-
- // Access point seurity mode definitions.
- enum SecurityMode {
- SECURITY_UNKNOWN = 0;
- SECURITY_WPA = 1;
- SECURITY_WEP = 2;
- SECURITY_RSN = 3;
- SECURITY_802_1X = 4;
- SECURITY_PSK = 5;
- SECURITY_NONE = 6;
- }
- // The security mode of the access point.
- optional SecurityMode security_mode = 2;
-
- // Vendor specific information.
- message VendorInformation {
- // The model number, for example "0".
- optional string model_number = 1;
-
- // The model name (sometimes the same as the model_number),
- // for example "WZR-HP-AG300H".
- optional string model_name = 2;
-
- // The device name (sometimes the same as the model_number),
- // for example "Dummynet"
- optional string device_name = 3;
-
- // The list of vendor-specific OUIs (Organziationally Unqiue
- // Identifiers). These are provided by the vendor through WPS
- // (Wireless Provisioning Service) information elements, which
- // identifies the content of the element.
- repeated uint32 element_identifier = 4;
- }
- // The wireless access point vendor information.
- optional VendorInformation vendor_info = 3;
- }
- // Information of the wireless AP that device is connected to.
- optional WifiAccessPoint access_point_info = 5;
- }
- optional Network network = 13;
-
- // Information on the Google Update install that is managing this client.
- message GoogleUpdate {
- // Whether the Google Update install is system-level or user-level.
- optional bool is_system_install = 1;
-
- // The date at which Google Update last started performing an automatic
- // update check, in seconds since the Unix epoch.
- optional int64 last_automatic_start_timestamp = 2;
-
- // The date at which Google Update last successfully sent an update check
- // and recieved an intact response from the server, in seconds since the
- // Unix epoch. (The updates don't need to be successfully installed.)
- optional int64 last_update_check_timestamp = 3;
-
- // Describes a product being managed by Google Update. (This can also
- // describe Google Update itself.)
- message ProductInfo {
- // The current version of the product that is installed.
- optional string version = 1;
-
- // The date at which Google Update successfully updated this product,
- // stored in seconds since the Unix epoch. This is updated when an update
- // is successfully applied, or if the server reports that no update
- // is available.
- optional int64 last_update_success_timestamp = 2;
-
- // The result reported by the product updater on its last run.
- enum InstallResult {
- INSTALL_RESULT_SUCCESS = 0;
- INSTALL_RESULT_FAILED_CUSTOM_ERROR = 1;
- INSTALL_RESULT_FAILED_MSI_ERROR = 2;
- INSTALL_RESULT_FAILED_SYSTEM_ERROR = 3;
- INSTALL_RESULT_EXIT_CODE = 4;
- }
- optional InstallResult last_result = 3;
-
- // The error code reported by the product updater on its last run. This
- // will typically be a error code specific to the product installer.
- optional int32 last_error = 4;
-
- // The extra error code reported by the product updater on its last run.
- // This will typically be a Win32 error code.
- optional int32 last_extra_error = 5;
- }
- optional ProductInfo google_update_status = 4;
- optional ProductInfo client_status = 5;
- }
- optional GoogleUpdate google_update = 11;
-
- // Information on all installed plugins.
- message Plugin {
- // The plugin's self-reported name and filename (without path).
- optional string name = 1;
- optional string filename = 2;
-
- // The plugin's version.
- optional string version = 3;
-
- // True if the plugin is disabled.
- // If a client has multiple local Chrome user accounts, this is logged based
- // on the first user account launched during the current session.
- optional bool is_disabled = 4;
-
- // True if the plugin is PPAPI.
- optional bool is_pepper = 5;
- }
- repeated Plugin plugin = 7;
-
- // Figures that can be used to generate application stability metrics.
- // All values are counts of events since the last time that these
- // values were reported.
- // Next tag: 24
- message Stability {
- // Total amount of time that the program was running, in seconds,
- // since the last time a log was recorded, as measured using a client-side
- // clock implemented via TimeTicks, which guarantees that it is monotonic
- // and does not jump if the user changes his/her clock. The TimeTicks
- // implementation also makes the clock not count time the computer is
- // suspended.
- optional int64 incremental_uptime_sec = 1;
-
- // Total amount of time that the program was running, in seconds,
- // since startup, as measured using a client-side clock implemented
- // via TimeTicks, which guarantees that it is monotonic and does not
- // jump if the user changes his/her clock. The TimeTicks implementation
- // also makes the clock not count time the computer is suspended.
- // This field was added for M-35.
- optional int64 uptime_sec = 23;
-
- // Page loads along with renderer crashes and hangs, since page load count
- // roughly corresponds to usage.
- optional int32 page_load_count = 2;
- optional int32 renderer_crash_count = 3;
- optional int32 renderer_hang_count = 4;
-
- // Number of renderer crashes that were for extensions. These crashes are
- // not counted in renderer_crash_count.
- optional int32 extension_renderer_crash_count = 5;
-
- // Number of non-renderer child process crashes.
- optional int32 child_process_crash_count = 6;
-
- // Number of times the browser has crashed while logged in as the "other
- // user" (guest) account.
- // Logged on ChromeOS only.
- optional int32 other_user_crash_count = 7;
-
- // Number of times the kernel has crashed.
- // Logged on ChromeOS only.
- optional int32 kernel_crash_count = 8;
-
- // Number of times the system has shut down uncleanly.
- // Logged on ChromeOS only.
- optional int32 unclean_system_shutdown_count = 9;
-
- //
- // All the remaining fields in the Stability are recorded at most once per
- // client session.
- //
-
- // The number of times the program was launched.
- // This will typically be equal to 1. However, it is possible that Chrome
- // was unable to upload stability metrics for previous launches (e.g. due to
- // crashing early during startup), and hence this value might be greater
- // than 1.
- optional int32 launch_count = 15;
- // The number of times that it didn't exit cleanly (which we assume to be
- // mostly crashes).
- optional int32 crash_count = 16;
-
- // The number of times the program began, but did not complete, the shutdown
- // process. (For example, this may occur when Windows is shutting down, and
- // it only gives the process a few seconds to clean up.)
- optional int32 incomplete_shutdown_count = 17;
-
- // The number of times the program was able register with breakpad crash
- // services.
- optional int32 breakpad_registration_success_count = 18;
-
- // The number of times the program failed to register with breakpad crash
- // services. If crash registration fails then when the program crashes no
- // crash report will be generated.
- optional int32 breakpad_registration_failure_count = 19;
-
- // The number of times the program has run under a debugger. This should
- // be an exceptional condition. Running under a debugger prevents crash
- // dumps from being generated.
- optional int32 debugger_present_count = 20;
-
- // The number of times the program has run without a debugger attached.
- // This should be most common scenario and should be very close to
- // |launch_count|.
- optional int32 debugger_not_present_count = 21;
-
- // Stability information for all installed plugins.
- message PluginStability {
- // The relevant plugin's information (name, etc.)
- optional Plugin plugin = 1;
-
- // The number of times this plugin's process was launched.
- optional int32 launch_count = 2;
-
- // The number of times this plugin was instantiated on a web page.
- // This will be >= |launch_count|.
- // (A page load with multiple sections drawn by this plugin will
- // increase this count multiple times.)
- optional int32 instance_count = 3;
-
- // The number of times this plugin process crashed.
- // This value will be <= |launch_count|.
- optional int32 crash_count = 4;
-
- // The number of times this plugin could not be loaded.
- optional int32 loading_error_count = 5;
- }
- repeated PluginStability plugin_stability = 22;
- }
- optional Stability stability = 8;
-
- // Description of a field trial or experiment that the user is currently
- // enrolled in.
- // All metrics reported in this upload can potentially be influenced by the
- // field trial.
- message FieldTrial {
- // The name of the field trial, as a 32-bit identifier.
- // Currently, the identifier is a hash of the field trial's name.
- optional fixed32 name_id = 1;
-
- // The user's group within the field trial, as a 32-bit identifier.
- // Currently, the identifier is a hash of the group's name.
- optional fixed32 group_id = 2;
- }
- repeated FieldTrial field_trial = 9;
-
- // Information about the A/V output device(s) (typically just a TV).
- // However, a configuration may have one or more intermediate A/V devices
- // between the source device and the TV (e.g. an A/V receiver, video
- // processor, etc.).
- message ExternalAudioVideoDevice {
- // The manufacturer name (possibly encoded as a 3-letter code, e.g. "YMH"
- // for Yamaha).
- optional string manufacturer_name = 1;
-
- // The model name (e.g. "RX-V1900"). Some devices may report generic names
- // like "receiver" or use the full manufacturer name (e.g "PHILIPS").
- optional string model_name = 2;
-
- // The product code (e.g. "0218").
- optional string product_code = 3;
-
- // The device types. A single device can have multiple types (e.g. a set-top
- // box could be both a tuner and a player). The same type may even be
- // repeated (e.g a device that reports two tuners).
- enum AVDeviceType {
- AV_DEVICE_TYPE_UNKNOWN = 0;
- AV_DEVICE_TYPE_TV = 1;
- AV_DEVICE_TYPE_RECORDER = 2;
- AV_DEVICE_TYPE_TUNER = 3;
- AV_DEVICE_TYPE_PLAYER = 4;
- AV_DEVICE_TYPE_AUDIO_SYSTEM = 5;
- }
- repeated AVDeviceType av_device_type = 4;
-
- // The year of manufacture.
- optional int32 manufacture_year = 5;
-
- // The week of manufacture.
- // Note: per the Wikipedia EDID article, numbering for this field may not
- // be consistent between manufacturers.
- optional int32 manufacture_week = 6;
-
- // Max horizontal resolution in pixels.
- optional int32 horizontal_resolution = 7;
-
- // Max vertical resolution in pixels.
- optional int32 vertical_resolution = 8;
-
- // Audio capabilities of the device.
- // Ref: http://en.wikipedia.org/wiki/Extended_display_identification_data
- message AudioDescription {
- // Audio format
- enum AudioFormat {
- AUDIO_FORMAT_UNKNOWN = 0;
- AUDIO_FORMAT_LPCM = 1;
- AUDIO_FORMAT_AC_3 = 2;
- AUDIO_FORMAT_MPEG1 = 3;
- AUDIO_FORMAT_MP3 = 4;
- AUDIO_FORMAT_MPEG2 = 5;
- AUDIO_FORMAT_AAC = 6;
- AUDIO_FORMAT_DTS = 7;
- AUDIO_FORMAT_ATRAC = 8;
- AUDIO_FORMAT_ONE_BIT = 9;
- AUDIO_FORMAT_DD_PLUS = 10;
- AUDIO_FORMAT_DTS_HD = 11;
- AUDIO_FORMAT_MLP_DOLBY_TRUEHD = 12;
- AUDIO_FORMAT_DST_AUDIO = 13;
- AUDIO_FORMAT_MICROSOFT_WMA_PRO = 14;
- }
- optional AudioFormat audio_format = 1;
-
- // Number of channels (e.g. 1, 2, 8, etc.).
- optional int32 num_channels = 2;
-
- // Supported sample frequencies in Hz (e.g. 32000, 44100, etc.).
- // Multiple frequencies may be specified.
- repeated int32 sample_frequency_hz = 3;
-
- // Maximum bit rate in bits/s.
- optional int32 max_bit_rate_per_second = 4;
-
- // Bit depth (e.g. 16, 20, 24, etc.).
- optional int32 bit_depth = 5;
- }
- repeated AudioDescription audio_description = 9;
-
- // The position in AV setup.
- // A value of 0 means this device is the TV.
- // A value of 1 means this device is directly connected to one of
- // the TV's inputs.
- // Values > 1 indicate there are 1 or more devices between this device
- // and the TV.
- optional int32 position_in_setup = 10;
-
- // Whether this device is in the path to the TV.
- optional bool is_in_path_to_tv = 11;
-
- // The CEC version the device supports.
- // CEC stands for Consumer Electronics Control, a part of the HDMI
- // specification. Not all HDMI devices support CEC.
- // Only devices that support CEC will report a value here.
- optional int32 cec_version = 12;
-
- // This message reports CEC commands seen by a device.
- // After each log is sent, this information is cleared and gathered again.
- // By collecting CEC status information by opcode we can determine
- // which CEC features can be supported.
- message CECCommand {
- // The CEC command opcode. CEC supports up to 256 opcodes.
- // We add only one CECCommand message per unique opcode. Only opcodes
- // seen by the device will be reported. The remainder of the message
- // accumulates status for this opcode (and device).
- optional int32 opcode = 1;
-
- // The total number of commands received from the external device.
- optional int32 num_received_direct = 2;
-
- // The number of commands received from the external device as part of a
- // broadcast message.
- optional int32 num_received_broadcast = 3;
-
- // The total number of commands sent to the external device.
- optional int32 num_sent_direct = 4;
-
- // The number of commands sent to the external device as part of a
- // broadcast message.
- optional int32 num_sent_broadcast = 5;
-
- // The number of aborted commands for unknown reasons.
- optional int32 num_aborted_unknown_reason = 6;
-
- // The number of aborted commands because of an unrecognized opcode.
- optional int32 num_aborted_unrecognized = 7;
- }
- repeated CECCommand cec_command = 13;
- }
- repeated ExternalAudioVideoDevice external_audio_video_device = 14;
-
- // Information about the current wireless access point. Collected directly
- // from the wireless access point via standard apis if the device is
- // connected to the Internet wirelessly. Introduced for Chrome on TV devices
- // but also can be collected by ChromeOS, Android or other clients.
- message ExternalAccessPoint {
- // The manufacturer name, for example "ASUSTeK Computer Inc.".
- optional string manufacturer = 1;
-
- // The model name, for example "Wi-Fi Protected Setup Router".
- optional string model_name = 2;
-
- // The model number, for example "RT-N16".
- optional string model_number = 3;
-
- // The device name (sometime same as model_number), for example "RT-N16".
- optional string device_name = 4;
- }
- optional ExternalAccessPoint external_access_point = 15;
-
- // Number of users currently signed into a multiprofile session.
- // A zero value indicates that the user count changed while the log is open.
- // Logged only on ChromeOS.
- optional uint32 multi_profile_user_count = 17;
-
- // Information about extensions that are installed, masked to provide better
- // privacy. Only extensions from a single profile are reported; this will
- // generally be the profile used when the browser is started. The profile
- // reported on will remain consistent at least until the browser is
- // relaunched (or the profile is deleted by the user).
- //
- // Each client first picks a value for client_key derived from its UMA
- // client_id:
- // client_key = client_id % 4096
- // Then, each installed extension is mapped into a hash bucket according to
- // bucket = CityHash64(StringPrintf("%d:%s",
- // client_key, extension_id)) % 1024
- // The client reports the set of hash buckets occupied by all installed
- // extensions. If multiple extensions map to the same bucket, that bucket is
- // still only reported once.
- repeated int32 occupied_extension_bucket = 18;
-
- // The state of loaded extensions for this system. The system can have either
- // no applicable extensions, extensions only from the webstore and verified by
- // the webstore, extensions only from the webstore but not verified, or
- // extensions not from the store. If there is a single off-store extension,
- // then HAS_OFFSTORE is reported. This should be kept in sync with the
- // corresponding enum in chrome/browser/metrics/extensions_metrics_provider.cc
- enum ExtensionsState {
- NO_EXTENSIONS = 0;
- NO_OFFSTORE_VERIFIED = 1;
- NO_OFFSTORE_UNVERIFIED = 2;
- HAS_OFFSTORE = 3;
- }
- optional ExtensionsState offstore_extensions_state = 19;
-}
diff --git a/metricsd/uploader/proto/user_action_event.proto b/metricsd/uploader/proto/user_action_event.proto
deleted file mode 100644
index 464f3c8..0000000
--- a/metricsd/uploader/proto/user_action_event.proto
+++ /dev/null
@@ -1,35 +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.
- */
-//
-// Stores information about an event that occurs in response to a user action,
-// e.g. an interaction with a browser UI element.
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-option java_outer_classname = "UserActionEventProtos";
-option java_package = "org.chromium.components.metrics";
-
-package metrics;
-
-// Next tag: 3
-message UserActionEventProto {
- // The name of the action, hashed.
- optional fixed64 name_hash = 1;
-
- // The timestamp for the event, in seconds since the epoch.
- optional int64 time = 2;
-}
diff --git a/metricsd/uploader/sender.h b/metricsd/uploader/sender.h
deleted file mode 100644
index 369c9c2..0000000
--- a/metricsd/uploader/sender.h
+++ /dev/null
@@ -1,30 +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 METRICS_UPLOADER_SENDER_H_
-#define METRICS_UPLOADER_SENDER_H_
-
-#include <string>
-
-// Abstract class for a Sender that uploads a metrics message.
-class Sender {
- public:
- virtual ~Sender() {}
- // Sends a message |content| with its sha1 hash |hash|
- virtual bool Send(const std::string& content, const std::string& hash) = 0;
-};
-
-#endif // METRICS_UPLOADER_SENDER_H_
diff --git a/metricsd/uploader/sender_http.cc b/metricsd/uploader/sender_http.cc
deleted file mode 100644
index 953afc1..0000000
--- a/metricsd/uploader/sender_http.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uploader/sender_http.h"
-
-#include <string>
-
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <chromeos/http/http_utils.h>
-#include <chromeos/mime_utils.h>
-
-HttpSender::HttpSender(const std::string server_url)
- : server_url_(server_url) {}
-
-bool HttpSender::Send(const std::string& content,
- const std::string& content_hash) {
- const std::string hash =
- base::HexEncode(content_hash.data(), content_hash.size());
-
- chromeos::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}};
- chromeos::ErrorPtr error;
- auto response = chromeos::http::PostTextAndBlock(
- server_url_,
- content,
- chromeos::mime::application::kWwwFormUrlEncoded,
- headers,
- chromeos::http::Transport::CreateDefault(),
- &error);
- if (!response || response->ExtractDataAsString() != "OK") {
- if (error) {
- DLOG(ERROR) << "Failed to send data: " << error->GetMessage();
- }
- return false;
- }
- return true;
-}
diff --git a/metricsd/uploader/sender_http.h b/metricsd/uploader/sender_http.h
deleted file mode 100644
index 6249d90..0000000
--- a/metricsd/uploader/sender_http.h
+++ /dev/null
@@ -1,41 +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 METRICS_UPLOADER_SENDER_HTTP_H_
-#define METRICS_UPLOADER_SENDER_HTTP_H_
-
-#include <string>
-
-#include <base/macros.h>
-
-#include "uploader/sender.h"
-
-// Sender implemented using http_utils from libchromeos
-class HttpSender : public Sender {
- public:
- explicit HttpSender(std::string server_url);
- ~HttpSender() override = default;
- // Sends |content| whose SHA1 hash is |hash| to server_url with a synchronous
- // POST request to server_url.
- bool Send(const std::string& content, const std::string& hash) override;
-
- private:
- const std::string server_url_;
-
- DISALLOW_COPY_AND_ASSIGN(HttpSender);
-};
-
-#endif // METRICS_UPLOADER_SENDER_HTTP_H_
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
deleted file mode 100644
index 35910d7..0000000
--- a/metricsd/uploader/system_profile_cache.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uploader/system_profile_cache.h"
-
-#include <base/files/file_util.h>
-#include <base/guid.h>
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_util.h>
-#include <base/sys_info.h>
-#include <string>
-#include <vector>
-
-#include "constants.h"
-#include "persistent_integer.h"
-#include "uploader/metrics_log_base.h"
-#include "uploader/proto/chrome_user_metrics_extension.pb.h"
-
-namespace {
-
-const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId";
-
-} // namespace
-
-std::string ChannelToString(
- const metrics::SystemProfileProto_Channel& channel) {
- switch (channel) {
- case metrics::SystemProfileProto::CHANNEL_STABLE:
- return "STABLE";
- case metrics::SystemProfileProto::CHANNEL_DEV:
- return "DEV";
- case metrics::SystemProfileProto::CHANNEL_BETA:
- return "BETA";
- case metrics::SystemProfileProto::CHANNEL_CANARY:
- return "CANARY";
- default:
- return "UNKNOWN";
- }
-}
-
-SystemProfileCache::SystemProfileCache()
- : initialized_(false),
- testing_(false),
- config_root_("/"),
- session_id_(new chromeos_metrics::PersistentInteger(
- kPersistentSessionIdFilename)) {
-}
-
-SystemProfileCache::SystemProfileCache(bool testing,
- const std::string& config_root)
- : initialized_(false),
- testing_(testing),
- config_root_(config_root),
- session_id_(new chromeos_metrics::PersistentInteger(
- kPersistentSessionIdFilename)) {
-}
-
-bool SystemProfileCache::Initialize() {
- CHECK(!initialized_)
- << "this should be called only once in the metrics_daemon lifetime.";
-
- if (!base::SysInfo::GetLsbReleaseValue("BRILLO_BUILD_TARGET_ID",
- &profile_.build_target_id)) {
- LOG(ERROR) << "Could not initialize system profile.";
- return false;
- }
-
- std::string channel;
- if (!base::SysInfo::GetLsbReleaseValue("BRILLO_CHANNEL", &channel) ||
- !base::SysInfo::GetLsbReleaseValue("BRILLO_VERSION", &profile_.version)) {
- // If the channel or version is missing, the image is not official.
- // In this case, set the channel to unknown and the version to 0.0.0.0 to
- // avoid polluting the production data.
- channel = "";
- profile_.version = metrics::kDefaultVersion;
-
- }
- profile_.client_id =
- testing_ ? "client_id_test" :
- GetPersistentGUID(metrics::kMetricsGUIDFilePath);
- profile_.hardware_class = "unknown";
- profile_.channel = ProtoChannelFromString(channel);
-
- // Increment the session_id everytime we initialize this. If metrics_daemon
- // does not crash, this should correspond to the number of reboots of the
- // system.
- session_id_->Add(1);
- profile_.session_id = static_cast<int32_t>(session_id_->Get());
-
- initialized_ = true;
- return initialized_;
-}
-
-bool SystemProfileCache::InitializeOrCheck() {
- return initialized_ || Initialize();
-}
-
-void SystemProfileCache::Populate(
- metrics::ChromeUserMetricsExtension* metrics_proto) {
- CHECK(metrics_proto);
- CHECK(InitializeOrCheck())
- << "failed to initialize system information.";
-
- // The client id is hashed before being sent.
- metrics_proto->set_client_id(
- metrics::MetricsLogBase::Hash(profile_.client_id));
- metrics_proto->set_session_id(profile_.session_id);
-
- // Sets the product id.
- metrics_proto->set_product(9);
-
- metrics::SystemProfileProto* profile_proto =
- metrics_proto->mutable_system_profile();
- profile_proto->mutable_hardware()->set_hardware_class(
- profile_.hardware_class);
- profile_proto->set_app_version(profile_.version);
- profile_proto->set_channel(profile_.channel);
- metrics::SystemProfileProto_BrilloDeviceData* device_data =
- profile_proto->mutable_brillo();
- device_data->set_build_target_id(profile_.build_target_id);
-}
-
-std::string SystemProfileCache::GetPersistentGUID(
- const std::string& filename) {
- std::string guid;
- base::FilePath filepath(filename);
- if (!base::ReadFileToString(filepath, &guid)) {
- guid = base::GenerateGUID();
- // If we can't read or write the file, the guid will not be preserved during
- // the next reboot. Crash.
- CHECK(base::WriteFile(filepath, guid.c_str(), guid.size()));
- }
- return guid;
-}
-
-metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
- const std::string& channel) {
- if (channel == "stable") {
- return metrics::SystemProfileProto::CHANNEL_STABLE;
- } else if (channel == "dev") {
- return metrics::SystemProfileProto::CHANNEL_DEV;
- } else if (channel == "beta") {
- return metrics::SystemProfileProto::CHANNEL_BETA;
- } else if (channel == "canary") {
- return metrics::SystemProfileProto::CHANNEL_CANARY;
- }
-
- DLOG(INFO) << "unknown channel: " << channel;
- return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
-}
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
deleted file mode 100644
index c53a18e..0000000
--- a/metricsd/uploader/system_profile_cache.h
+++ /dev/null
@@ -1,85 +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 METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
-#define METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "persistent_integer.h"
-#include "uploader/proto/system_profile.pb.h"
-#include "uploader/system_profile_setter.h"
-
-namespace metrics {
-class ChromeUserMetricsExtension;
-}
-
-struct SystemProfile {
- std::string version;
- std::string hardware_class;
- std::string client_id;
- int session_id;
- metrics::SystemProfileProto::Channel channel;
- std::string build_target_id;
-};
-
-// Retrieves general system informations needed by the protobuf for context and
-// remembers them to avoid expensive calls.
-//
-// The cache is populated lazily. The only method needed is Populate.
-class SystemProfileCache : public SystemProfileSetter {
- public:
- SystemProfileCache();
-
- SystemProfileCache(bool testing, const std::string& config_root);
-
- // Populates the ProfileSystem protobuf with system information.
- void Populate(metrics::ChromeUserMetricsExtension* metrics_proto) override;
-
- // Converts a string representation of the channel to a
- // SystemProfileProto_Channel
- static metrics::SystemProfileProto_Channel ProtoChannelFromString(
- const std::string& channel);
-
- // Gets the persistent GUID and create it if it has not been created yet.
- static std::string GetPersistentGUID(const std::string& filename);
-
- private:
- friend class UploadServiceTest;
- FRIEND_TEST(UploadServiceTest, ExtractChannelFromDescription);
- FRIEND_TEST(UploadServiceTest, ReadKeyValueFromFile);
- FRIEND_TEST(UploadServiceTest, SessionIdIncrementedAtInitialization);
- FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
-
- // Fetches all informations and populates |profile_|
- bool Initialize();
-
- // Initializes |profile_| only if it has not been yet initialized.
- bool InitializeOrCheck();
-
- bool initialized_;
- bool testing_;
- std::string config_root_;
- scoped_ptr<chromeos_metrics::PersistentInteger> session_id_;
- SystemProfile profile_;
-};
-
-#endif // METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
diff --git a/metricsd/uploader/system_profile_setter.h b/metricsd/uploader/system_profile_setter.h
deleted file mode 100644
index cd311a4..0000000
--- a/metricsd/uploader/system_profile_setter.h
+++ /dev/null
@@ -1,33 +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 METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
-#define METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
-
-namespace metrics {
-class ChromeUserMetricsExtension;
-}
-
-// Abstract class used to delegate populating SystemProfileProto with system
-// information to simplify testing.
-class SystemProfileSetter {
- public:
- virtual ~SystemProfileSetter() {}
- // Populates the protobuf with system informations.
- virtual void Populate(metrics::ChromeUserMetricsExtension* profile_proto) = 0;
-};
-
-#endif // METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
deleted file mode 100644
index 63b5789..0000000
--- a/metricsd/uploader/upload_service.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "uploader/upload_service.h"
-
-#include <string>
-
-#include <base/bind.h>
-#include <base/logging.h>
-#include <base/memory/scoped_vector.h>
-#include <base/message_loop/message_loop.h>
-#include <base/metrics/histogram.h>
-#include <base/metrics/histogram_base.h>
-#include <base/metrics/histogram_snapshot_manager.h>
-#include <base/metrics/sparse_histogram.h>
-#include <base/metrics/statistics_recorder.h>
-#include <base/sha1.h>
-
-#include "serialization/metric_sample.h"
-#include "serialization/serialization_utils.h"
-#include "uploader/metrics_log.h"
-#include "uploader/sender_http.h"
-#include "uploader/system_profile_setter.h"
-
-const int UploadService::kMaxFailedUpload = 10;
-
-UploadService::UploadService(SystemProfileSetter* setter,
- MetricsLibraryInterface* metrics_lib,
- const std::string& server)
- : system_profile_setter_(setter),
- metrics_lib_(metrics_lib),
- histogram_snapshot_manager_(this),
- sender_(new HttpSender(server)),
- testing_(false) {
-}
-
-UploadService::UploadService(SystemProfileSetter* setter,
- MetricsLibraryInterface* metrics_lib,
- const std::string& server,
- bool testing)
- : UploadService(setter, metrics_lib, server) {
- testing_ = testing;
-}
-
-void UploadService::Init(const base::TimeDelta& upload_interval,
- const std::string& metrics_file) {
- base::StatisticsRecorder::Initialize();
- metrics_file_ = metrics_file;
-
- if (!testing_) {
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&UploadService::UploadEventCallback,
- base::Unretained(this),
- upload_interval),
- upload_interval);
- }
-}
-
-void UploadService::StartNewLog() {
- CHECK(!staged_log_) << "the staged log should be discarded before starting "
- "a new metrics log";
- MetricsLog* log = new MetricsLog();
- log->PopulateSystemProfile(system_profile_setter_.get());
- current_log_.reset(log);
-}
-
-void UploadService::UploadEventCallback(const base::TimeDelta& interval) {
- UploadEvent();
-
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&UploadService::UploadEventCallback,
- base::Unretained(this),
- interval),
- interval);
-}
-
-void UploadService::UploadEvent() {
- if (staged_log_) {
- // Previous upload failed, retry sending the logs.
- SendStagedLog();
- return;
- }
-
- // Previous upload successful, reading metrics sample from the file.
- ReadMetrics();
- GatherHistograms();
-
- // No samples found. Exit to avoid sending an empty log.
- if (!current_log_)
- return;
-
- StageCurrentLog();
- SendStagedLog();
-}
-
-void UploadService::SendStagedLog() {
- CHECK(staged_log_) << "staged_log_ must exist to be sent";
-
- // If metrics are not enabled, discard the log and exit.
- if (!metrics_lib_->AreMetricsEnabled()) {
- LOG(INFO) << "Metrics disabled. Don't upload metrics samples.";
- staged_log_.reset();
- return;
- }
-
- std::string log_text;
- staged_log_->GetEncodedLog(&log_text);
- if (!sender_->Send(log_text, base::SHA1HashString(log_text))) {
- ++failed_upload_count_;
- if (failed_upload_count_ <= kMaxFailedUpload) {
- LOG(WARNING) << "log upload failed " << failed_upload_count_
- << " times. It will be retried later.";
- return;
- }
- LOG(WARNING) << "log failed more than " << kMaxFailedUpload << " times.";
- } else {
- LOG(INFO) << "uploaded " << log_text.length() << " bytes";
- }
- // Discard staged log.
- staged_log_.reset();
-}
-
-void UploadService::Reset() {
- staged_log_.reset();
- current_log_.reset();
- failed_upload_count_ = 0;
-}
-
-void UploadService::ReadMetrics() {
- CHECK(!staged_log_)
- << "cannot read metrics until the old logs have been discarded";
-
- ScopedVector<metrics::MetricSample> vector;
- metrics::SerializationUtils::ReadAndTruncateMetricsFromFile(
- metrics_file_, &vector);
-
- int i = 0;
- for (ScopedVector<metrics::MetricSample>::iterator it = vector.begin();
- it != vector.end(); it++) {
- metrics::MetricSample* sample = *it;
- AddSample(*sample);
- i++;
- }
- DLOG(INFO) << i << " samples read";
-}
-
-void UploadService::AddSample(const metrics::MetricSample& sample) {
- base::HistogramBase* counter;
- switch (sample.type()) {
- case metrics::MetricSample::CRASH:
- AddCrash(sample.name());
- break;
- case metrics::MetricSample::HISTOGRAM:
- counter = base::Histogram::FactoryGet(
- sample.name(), sample.min(), sample.max(), sample.bucket_count(),
- base::Histogram::kUmaTargetedHistogramFlag);
- counter->Add(sample.sample());
- break;
- case metrics::MetricSample::SPARSE_HISTOGRAM:
- counter = base::SparseHistogram::FactoryGet(
- sample.name(), base::HistogramBase::kUmaTargetedHistogramFlag);
- counter->Add(sample.sample());
- break;
- case metrics::MetricSample::LINEAR_HISTOGRAM:
- counter = base::LinearHistogram::FactoryGet(
- sample.name(),
- 1,
- sample.max(),
- sample.max() + 1,
- base::Histogram::kUmaTargetedHistogramFlag);
- counter->Add(sample.sample());
- break;
- case metrics::MetricSample::USER_ACTION:
- GetOrCreateCurrentLog()->RecordUserAction(sample.name());
- break;
- default:
- break;
- }
-}
-
-void UploadService::AddCrash(const std::string& crash_name) {
- if (crash_name == "user") {
- GetOrCreateCurrentLog()->IncrementUserCrashCount();
- } else if (crash_name == "kernel") {
- GetOrCreateCurrentLog()->IncrementKernelCrashCount();
- } else if (crash_name == "uncleanshutdown") {
- GetOrCreateCurrentLog()->IncrementUncleanShutdownCount();
- } else {
- DLOG(ERROR) << "crash name unknown" << crash_name;
- }
-}
-
-void UploadService::GatherHistograms() {
- base::StatisticsRecorder::Histograms histograms;
- base::StatisticsRecorder::GetHistograms(&histograms);
-
- histogram_snapshot_manager_.PrepareDeltas(
- base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag);
-}
-
-void UploadService::RecordDelta(const base::HistogramBase& histogram,
- const base::HistogramSamples& snapshot) {
- GetOrCreateCurrentLog()->RecordHistogramDelta(histogram.histogram_name(),
- snapshot);
-}
-
-void UploadService::StageCurrentLog() {
- CHECK(!staged_log_)
- << "staged logs must be discarded before another log can be staged";
-
- if (!current_log_) return;
-
- staged_log_.swap(current_log_);
- staged_log_->CloseLog();
- failed_upload_count_ = 0;
-}
-
-MetricsLog* UploadService::GetOrCreateCurrentLog() {
- if (!current_log_) {
- StartNewLog();
- }
- return current_log_.get();
-}
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
deleted file mode 100644
index 7f2f413..0000000
--- a/metricsd/uploader/upload_service.h
+++ /dev/null
@@ -1,165 +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 METRICS_UPLOADER_UPLOAD_SERVICE_H_
-#define METRICS_UPLOADER_UPLOAD_SERVICE_H_
-
-#include <string>
-
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_flattener.h"
-#include "base/metrics/histogram_snapshot_manager.h"
-
-#include "metrics/metrics_library.h"
-#include "uploader/metrics_log.h"
-#include "uploader/sender.h"
-#include "uploader/system_profile_cache.h"
-
-namespace metrics {
-class ChromeUserMetricsExtension;
-class CrashSample;
-class HistogramSample;
-class LinearHistogramSample;
-class MetricSample;
-class SparseHistogramSample;
-class UserActionSample;
-}
-
-class SystemProfileSetter;
-
-// Service responsible for uploading the metrics periodically to the server.
-// This service works as a simple 2-state state-machine.
-//
-// The two states are the presence or not of a staged log.
-// A staged log is a compressed protobuffer containing both the aggregated
-// metrics and event and information about the client. (product, hardware id,
-// etc...).
-//
-// At regular intervals, the upload event will be triggered and the following
-// will happen:
-// * if a staged log is present:
-// The previous upload may have failed for various reason. We then retry to
-// upload the same log.
-// - if the upload is successful, we discard the log (therefore
-// transitioning back to no staged log)
-// - if the upload fails, we keep the log to try again later.
-// We do not try to read the metrics that are stored on
-// the disk as we want to avoid storing the metrics in memory.
-//
-// * if no staged logs are present:
-// Read all metrics from the disk, aggregate them and try to send them.
-// - if the upload succeeds, we discard the staged log (transitioning back
-// to the no staged log state)
-// - if the upload fails, we keep the staged log in memory to retry
-// uploading later.
-//
-class UploadService : public base::HistogramFlattener {
- public:
- explicit UploadService(SystemProfileSetter* setter,
- MetricsLibraryInterface* metrics_lib,
- const std::string& server);
-
- void Init(const base::TimeDelta& upload_interval,
- const std::string& metrics_file);
-
- // Starts a new log. The log needs to be regenerated after each successful
- // launch as it is destroyed when staging the log.
- void StartNewLog();
-
- // Event callback for handling MessageLoop events.
- void UploadEventCallback(const base::TimeDelta& interval);
-
- // Triggers an upload event.
- void UploadEvent();
-
- // Sends the staged log.
- void SendStagedLog();
-
- // Implements inconsistency detection to match HistogramFlattener's
- // interface.
- void InconsistencyDetected(
- base::HistogramBase::Inconsistency problem) override {}
- void UniqueInconsistencyDetected(
- base::HistogramBase::Inconsistency problem) override {}
- void InconsistencyDetectedInLoggedCount(int amount) override {}
-
- private:
- friend class UploadServiceTest;
-
- FRIEND_TEST(UploadServiceTest, CanSendMultipleTimes);
- FRIEND_TEST(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload);
- FRIEND_TEST(UploadServiceTest, EmptyLogsAreNotSent);
- FRIEND_TEST(UploadServiceTest, FailedSendAreRetried);
- FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
- FRIEND_TEST(UploadServiceTest, LogEmptyAfterUpload);
- FRIEND_TEST(UploadServiceTest, LogEmptyByDefault);
- FRIEND_TEST(UploadServiceTest, LogKernelCrash);
- FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
- FRIEND_TEST(UploadServiceTest, LogUserCrash);
- FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
- FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
-
- // Private constructor for use in unit testing.
- UploadService(SystemProfileSetter* setter,
- MetricsLibraryInterface* metrics_lib,
- const std::string& server,
- bool testing);
-
- // If a staged log fails to upload more than kMaxFailedUpload times, it
- // will be discarded.
- static const int kMaxFailedUpload;
-
- // Resets the internal state.
- void Reset();
-
- // Reads all the metrics from the disk.
- void ReadMetrics();
-
- // Adds a generic sample to the current log.
- void AddSample(const metrics::MetricSample& sample);
-
- // Adds a crash to the current log.
- void AddCrash(const std::string& crash_name);
-
- // Aggregates all histogram available in memory and store them in the current
- // log.
- void GatherHistograms();
-
- // Callback for HistogramSnapshotManager to store the histograms.
- void RecordDelta(const base::HistogramBase& histogram,
- const base::HistogramSamples& snapshot) override;
-
- // Compiles all the samples received into a single protobuf and adds all
- // system information.
- void StageCurrentLog();
-
- // Returns the current log. If there is no current log, creates it first.
- MetricsLog* GetOrCreateCurrentLog();
-
- scoped_ptr<SystemProfileSetter> system_profile_setter_;
- MetricsLibraryInterface* metrics_lib_;
- base::HistogramSnapshotManager histogram_snapshot_manager_;
- scoped_ptr<Sender> sender_;
- int failed_upload_count_;
- scoped_ptr<MetricsLog> current_log_;
- scoped_ptr<MetricsLog> staged_log_;
-
- std::string metrics_file_;
-
- bool testing_;
-};
-
-#endif // METRICS_UPLOADER_UPLOAD_SERVICE_H_
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
deleted file mode 100644
index cbb5277..0000000
--- a/metricsd/uploader/upload_service_test.cc
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "base/at_exit.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/logging.h"
-#include "base/sys_info.h"
-#include "metrics_library_mock.h"
-#include "serialization/metric_sample.h"
-#include "uploader/metrics_log.h"
-#include "uploader/mock/mock_system_profile_setter.h"
-#include "uploader/mock/sender_mock.h"
-#include "uploader/proto/chrome_user_metrics_extension.pb.h"
-#include "uploader/proto/histogram_event.pb.h"
-#include "uploader/proto/system_profile.pb.h"
-#include "uploader/system_profile_cache.h"
-#include "uploader/upload_service.h"
-
-class UploadServiceTest : public testing::Test {
- protected:
- UploadServiceTest()
- : cache_(true, "/"),
- upload_service_(new MockSystemProfileSetter(), &metrics_lib_,
- kMetricsServer, true),
- exit_manager_(new base::AtExitManager()) {
- sender_ = new SenderMock;
- upload_service_.sender_.reset(sender_);
- upload_service_.Init(base::TimeDelta::FromMinutes(30), kMetricsFilePath);
- }
-
- virtual void SetUp() {
- CHECK(dir_.CreateUniqueTempDir());
- upload_service_.GatherHistograms();
- upload_service_.Reset();
- sender_->Reset();
-
- chromeos_metrics::PersistentInteger::SetTestingMode(true);
- cache_.session_id_.reset(new chromeos_metrics::PersistentInteger(
- dir_.path().Append("session_id").value()));
- }
-
- scoped_ptr<metrics::MetricSample> Crash(const std::string& name) {
- return metrics::MetricSample::CrashSample(name);
- }
-
- base::ScopedTempDir dir_;
- SenderMock* sender_;
- SystemProfileCache cache_;
- UploadService upload_service_;
- MetricsLibraryMock metrics_lib_;
-
- scoped_ptr<base::AtExitManager> exit_manager_;
-};
-
-// Tests that the right crash increments a values.
-TEST_F(UploadServiceTest, LogUserCrash) {
- upload_service_.AddSample(*Crash("user").get());
-
- MetricsLog* log = upload_service_.current_log_.get();
- metrics::ChromeUserMetricsExtension* proto = log->uma_proto();
-
- EXPECT_EQ(1, proto->system_profile().stability().other_user_crash_count());
-}
-
-TEST_F(UploadServiceTest, LogUncleanShutdown) {
- upload_service_.AddSample(*Crash("uncleanshutdown"));
-
- EXPECT_EQ(1, upload_service_.current_log_
- ->uma_proto()
- ->system_profile()
- .stability()
- .unclean_system_shutdown_count());
-}
-
-TEST_F(UploadServiceTest, LogKernelCrash) {
- upload_service_.AddSample(*Crash("kernel"));
-
- EXPECT_EQ(1, upload_service_.current_log_
- ->uma_proto()
- ->system_profile()
- .stability()
- .kernel_crash_count());
-}
-
-TEST_F(UploadServiceTest, UnknownCrashIgnored) {
- upload_service_.AddSample(*Crash("foo"));
-
- // The log should be empty.
- EXPECT_FALSE(upload_service_.current_log_);
-}
-
-TEST_F(UploadServiceTest, FailedSendAreRetried) {
- sender_->set_should_succeed(false);
-
- upload_service_.AddSample(*Crash("user"));
- upload_service_.UploadEvent();
- EXPECT_EQ(1, sender_->send_call_count());
- std::string sent_string = sender_->last_message();
-
- upload_service_.UploadEvent();
- EXPECT_EQ(2, sender_->send_call_count());
- EXPECT_EQ(sent_string, sender_->last_message());
-}
-
-TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) {
- sender_->set_should_succeed(false);
- upload_service_.AddSample(*Crash("user"));
-
- for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
- upload_service_.UploadEvent();
- }
-
- EXPECT_TRUE(upload_service_.staged_log_);
- upload_service_.UploadEvent();
- EXPECT_FALSE(upload_service_.staged_log_);
-}
-
-TEST_F(UploadServiceTest, EmptyLogsAreNotSent) {
- upload_service_.UploadEvent();
- EXPECT_FALSE(upload_service_.current_log_);
- EXPECT_EQ(0, sender_->send_call_count());
-}
-
-TEST_F(UploadServiceTest, LogEmptyByDefault) {
- UploadService upload_service(new MockSystemProfileSetter(), &metrics_lib_,
- kMetricsServer);
-
- // current_log_ should be initialized later as it needs AtExitManager to exit
- // in order to gather system information from SysInfo.
- EXPECT_FALSE(upload_service.current_log_);
-}
-
-TEST_F(UploadServiceTest, CanSendMultipleTimes) {
- upload_service_.AddSample(*Crash("user"));
- upload_service_.UploadEvent();
-
- std::string first_message = sender_->last_message();
-
- upload_service_.AddSample(*Crash("kernel"));
- upload_service_.UploadEvent();
-
- EXPECT_NE(first_message, sender_->last_message());
-}
-
-TEST_F(UploadServiceTest, LogEmptyAfterUpload) {
- upload_service_.AddSample(*Crash("user"));
-
- EXPECT_TRUE(upload_service_.current_log_);
-
- upload_service_.UploadEvent();
- EXPECT_FALSE(upload_service_.current_log_);
-}
-
-TEST_F(UploadServiceTest, LogContainsAggregatedValues) {
- scoped_ptr<metrics::MetricSample> histogram =
- metrics::MetricSample::HistogramSample("foo", 10, 0, 42, 10);
- upload_service_.AddSample(*histogram.get());
-
-
- scoped_ptr<metrics::MetricSample> histogram2 =
- metrics::MetricSample::HistogramSample("foo", 11, 0, 42, 10);
- upload_service_.AddSample(*histogram2.get());
-
- upload_service_.GatherHistograms();
- metrics::ChromeUserMetricsExtension* proto =
- upload_service_.current_log_->uma_proto();
- EXPECT_EQ(1, proto->histogram_event().size());
-}
-
-TEST_F(UploadServiceTest, ExtractChannelFromString) {
- EXPECT_EQ(
- SystemProfileCache::ProtoChannelFromString(
- "developer-build"),
- metrics::SystemProfileProto::CHANNEL_UNKNOWN);
-
- EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV,
- SystemProfileCache::ProtoChannelFromString("dev-channel"));
-
- EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_UNKNOWN,
- SystemProfileCache::ProtoChannelFromString("dev-channel test"));
-}
-
-TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) {
- std::string name("os name");
- std::string content(
- "CHROMEOS_RELEASE_NAME=" + name +
- "\nCHROMEOS_RELEASE_VERSION=version\n"
- "CHROMEOS_RELEASE_DESCRIPTION=description beta-channel test\n"
- "CHROMEOS_RELEASE_TRACK=beta-channel\n"
- "CHROMEOS_RELEASE_BUILD_TYPE=developer build\n"
- "CHROMEOS_RELEASE_BOARD=myboard");
-
- base::SysInfo::SetChromeOSVersionInfoForTest(content, base::Time());
- scoped_ptr<metrics::MetricSample> histogram =
- metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
- SystemProfileCache* local_cache_ = new SystemProfileCache(true, "/");
- local_cache_->session_id_.reset(new chromeos_metrics::PersistentInteger(
- dir_.path().Append("session_id").value()));
-
- upload_service_.system_profile_setter_.reset(local_cache_);
- // Reset to create the new log with the profile setter.
- upload_service_.Reset();
- upload_service_.AddSample(*histogram.get());
- upload_service_.UploadEvent();
-
- EXPECT_EQ(1, sender_->send_call_count());
- EXPECT_TRUE(sender_->is_good_proto());
- EXPECT_EQ(1, sender_->last_message_proto().histogram_event().size());
-
- EXPECT_EQ(name, sender_->last_message_proto().system_profile().os().name());
- EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_BETA,
- sender_->last_message_proto().system_profile().channel());
- EXPECT_NE(0, sender_->last_message_proto().client_id());
- EXPECT_NE(0,
- sender_->last_message_proto().system_profile().build_timestamp());
- EXPECT_NE(0, sender_->last_message_proto().session_id());
-}
-
-TEST_F(UploadServiceTest, PersistentGUID) {
- std::string tmp_file = dir_.path().Append("tmpfile").value();
-
- std::string first_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
- std::string second_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
-
- // The GUID are cached.
- EXPECT_EQ(first_guid, second_guid);
-
- base::DeleteFile(base::FilePath(tmp_file), false);
-
- first_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
- base::DeleteFile(base::FilePath(tmp_file), false);
- second_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
-
- // Random GUIDs are generated (not all the same).
- EXPECT_NE(first_guid, second_guid);
-}
-
-TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) {
- cache_.Initialize();
- int session_id = cache_.profile_.session_id;
- cache_.initialized_ = false;
- cache_.Initialize();
- EXPECT_EQ(cache_.profile_.session_id, session_id + 1);
-}
-
-int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
-
- return RUN_ALL_TESTS();
-}
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 5ab6195..60834fe 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -43,7 +43,14 @@
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
- uint32_t unused[2]; /* future expansion: should be 0 */
+ uint32_t unused; /* reserved for future expansion: MUST be 0 */
+
+ /* operating system version and security patch level; for
+ * version "A.B.C" and patch level "Y-M-D":
+ * ver = A << 14 | B << 7 | C (7 bits for each of A, B, C)
+ * lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M)
+ * os_version = ver << 11 | lvl */
+ uint32_t os_version;
uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index aea2585..5a13da2 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -19,6 +19,8 @@
from os import fstat
from struct import pack
from hashlib import sha1
+import sys
+import re
def filesize(f):
if f is None:
@@ -46,7 +48,7 @@
def write_header(args):
BOOT_MAGIC = 'ANDROID!'.encode()
args.output.write(pack('8s', BOOT_MAGIC))
- args.output.write(pack('8I',
+ args.output.write(pack('10I',
filesize(args.kernel), # size in bytes
args.base + args.kernel_offset, # physical load addr
filesize(args.ramdisk), # size in bytes
@@ -54,8 +56,9 @@
filesize(args.second), # size in bytes
args.base + args.second_offset, # physical load addr
args.base + args.tags_offset, # physical addr for kernel tags
- args.pagesize)) # flash page size we assume
- args.output.write(pack('8x')) # future expansion: should be 0
+ args.pagesize, # flash page size we assume
+ 0, # future expansion: MUST be 0
+ (args.os_version << 11) | args.os_patch_level)) # os version and patch level
args.output.write(pack('16s', args.board.encode())) # asciiz product name
args.output.write(pack('512s', args.cmdline[:512].encode()))
@@ -96,6 +99,32 @@
def parse_int(x):
return int(x, 0)
+def parse_os_version(x):
+ match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
+ if match:
+ a = int(match.group(1))
+ b = c = 0
+ if match.lastindex >= 2:
+ b = int(match.group(2))
+ if match.lastindex == 3:
+ c = int(match.group(3))
+ # 7 bits allocated for each field
+ assert a < 128
+ assert b < 128
+ assert c < 128
+ return (a << 14) | (b << 7) | c
+ return 0
+
+def parse_os_patch_level(x):
+ match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)
+ if match:
+ y = int(match.group(1)) - 2000
+ m = int(match.group(2))
+ # 7 bits allocated for the year, 4 bits for the month
+ assert y >= 0 and y < 128
+ assert m > 0 and m <= 12
+ return (y << 4) | m
+ return 0
def parse_cmdline():
parser = ArgumentParser()
@@ -110,6 +139,10 @@
parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
default=0x00f00000)
+ parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
+ default=0)
+ parser.add_argument('--os_patch_level', help='operating system patch level',
+ type=parse_os_patch_level, default=0)
parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
maxlen=16)
@@ -133,8 +166,10 @@
img_id = write_header(args)
write_data(args)
if args.id:
- print('0x' + ''.join('{:02x}'.format(ord(c)) for c in img_id))
-
+ if isinstance(img_id, str):
+ # Python 2's struct.pack returns a string, but py3 returns bytes.
+ img_id = [ord(x) for x in img_id]
+ print('0x' + ''.join('{:02x}'.format(c) for c in img_id))
if __name__ == '__main__':
main()
diff --git a/reboot/Android.bp b/reboot/Android.bp
new file mode 100644
index 0000000..805fd9a
--- /dev/null
+++ b/reboot/Android.bp
@@ -0,0 +1,8 @@
+// Copyright 2013 The Android Open Source Project
+
+cc_binary {
+ name: "reboot",
+ srcs: ["reboot.c"],
+ shared_libs: ["libcutils"],
+ cflags: ["-Werror"],
+}
diff --git a/reboot/Android.mk b/reboot/Android.mk
deleted file mode 100644
index 7a24f99..0000000
--- a/reboot/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := reboot.c
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-LOCAL_MODULE := reboot
-
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index e1ba2e9..7d0c87d 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -2,8 +2,6 @@
#######################################
# init.rc
-# Only copy init.rc if the target doesn't have its own.
-ifneq ($(TARGET_PROVIDES_INIT_RC),true)
include $(CLEAR_VARS)
LOCAL_MODULE := init.rc
@@ -12,11 +10,23 @@
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
include $(BUILD_PREBUILT)
-endif
+
+#######################################
+# init-debug.rc
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init-debug.rc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
+
+include $(BUILD_PREBUILT)
#######################################
# asan.options
-ifeq (address,$(strip $(SANITIZE_TARGET)))
+ifneq ($(filter address,$(SANITIZE_TARGET)),)
+
include $(CLEAR_VARS)
LOCAL_MODULE := asan.options
@@ -25,6 +35,72 @@
LOCAL_MODULE_PATH := $(TARGET_OUT)
include $(BUILD_PREBUILT)
+
+# Modules for asan.options.X files.
+
+ASAN_OPTIONS_FILES :=
+
+define create-asan-options-module
+include $$(CLEAR_VARS)
+LOCAL_MODULE := asan.options.$(1)
+ASAN_OPTIONS_FILES += asan.options.$(1)
+LOCAL_MODULE_CLASS := ETC
+# The asan.options.off.template tries to turn off as much of ASAN as is possible.
+LOCAL_SRC_FILES := asan.options.off.template
+LOCAL_MODULE_PATH := $(TARGET_OUT)
+include $$(BUILD_PREBUILT)
+endef
+
+# Pretty comprehensive set of native services. This list is helpful if all that's to be checked is an
+# app.
+ifeq ($(SANITIZE_LITE),true)
+SANITIZE_ASAN_OPTIONS_FOR := \
+ adbd \
+ ATFWD-daemon \
+ audioserver \
+ bridgemgrd \
+ cameraserver \
+ cnd \
+ debuggerd \
+ debuggerd64 \
+ dex2oat \
+ drmserver \
+ fingerprintd \
+ gatekeeperd \
+ installd \
+ keystore \
+ lmkd \
+ logcat \
+ logd \
+ lowi-server \
+ media.codec \
+ mediadrmserver \
+ media.extractor \
+ mediaserver \
+ mm-qcamera-daemon \
+ mpdecision \
+ netmgrd \
+ perfd \
+ perfprofd \
+ qmuxd \
+ qseecomd \
+ rild \
+ sdcard \
+ servicemanager \
+ slim_daemon \
+ surfaceflinger \
+ thermal-engine \
+ time_daemon \
+ update_engine \
+ vold \
+ wpa_supplicant \
+ zip
+endif
+
+ifneq ($(SANITIZE_ASAN_OPTIONS_FOR),)
+ $(foreach binary, $(SANITIZE_ASAN_OPTIONS_FOR), $(eval $(call create-asan-options-module,$(binary))))
+endif
+
endif
#######################################
@@ -36,25 +112,42 @@
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
EXPORT_GLOBAL_ASAN_OPTIONS :=
-ifeq (address,$(strip $(SANITIZE_TARGET)))
+ifneq ($(filter address,$(SANITIZE_TARGET)),)
EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
- LOCAL_REQUIRED_MODULES := asan.options
+ LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES)
endif
# Put it here instead of in init.rc module definition,
# because init.rc is conditionally included.
#
# create some directories (some are mount points) and symlinks
-local_post_install_cmd_base := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data oem acct cache config storage mnt root); \
+LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
+ sbin dev proc sys system data oem acct config storage mnt root $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
+ ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
-ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
- LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base); mkdir -p $(TARGET_ROOT_OUT)/vendor
+ifdef BOARD_USES_VENDORIMAGE
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
else
- LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base)
+ LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
endif
-local_post_install_cmd_base :=
+ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
+else
+ LOCAL_POST_INSTALL_CMD += ; ln -sf /data/cache $(TARGET_ROOT_OUT)/cache
+endif
+ifdef BOARD_ROOT_EXTRA_SYMLINKS
+# BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.
+ LOCAL_POST_INSTALL_CMD += $(foreach s, $(BOARD_ROOT_EXTRA_SYMLINKS),\
+ $(eval p := $(subst :,$(space),$(s)))\
+ ; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
+ ; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
+endif
+# The A/B updater uses a top-level /postinstall directory to mount the new
+# system before reboot.
+ifeq ($(AB_OTA_UPDATER),true)
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/postinstall
+endif
include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/rootdir/asan.options b/rootdir/asan.options
index 2f12341..d728f12 100644
--- a/rootdir/asan.options
+++ b/rootdir/asan.options
@@ -1 +1,7 @@
-allow_user_segv_handler=1:detect_odr_violation=0:alloc_dealloc_mismatch=0:allocator_may_return_null=1
+allow_user_segv_handler=1
+detect_odr_violation=0
+alloc_dealloc_mismatch=0
+allocator_may_return_null=1
+detect_container_overflow=0
+abort_on_error=1
+include_if_exists=/system/asan.options.%b
diff --git a/rootdir/asan.options.off.template b/rootdir/asan.options.off.template
new file mode 100644
index 0000000..59a1249
--- /dev/null
+++ b/rootdir/asan.options.off.template
@@ -0,0 +1,7 @@
+quarantine_size_mb=0
+max_redzone=16
+poison_heap=false
+poison_partial=false
+poison_array_cookie=false
+alloc_dealloc_mismatch=false
+new_delete_type_mismatch=false
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
new file mode 100644
index 0000000..e6c94ff
--- /dev/null
+++ b/rootdir/etc/public.libraries.android.txt
@@ -0,0 +1,21 @@
+libandroid.so
+libc.so
+libcamera2ndk.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libvulkan.so
+libwebviewchromium_plat_support.so
+libz.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
new file mode 100644
index 0000000..292730a
--- /dev/null
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -0,0 +1,20 @@
+libandroid.so
+libc.so
+libcamera2ndk.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libvulkan.so
+libz.so
diff --git a/rootdir/init-debug.rc b/rootdir/init-debug.rc
new file mode 100644
index 0000000..44d34d8
--- /dev/null
+++ b/rootdir/init-debug.rc
@@ -0,0 +1,11 @@
+on property:persist.mmc.max_read_speed=*
+ write /sys/block/mmcblk0/max_read_speed ${persist.mmc.max_read_speed}
+
+on property:persist.mmc.max_write_speed=*
+ write /sys/block/mmcblk0/max_write_speed ${persist.mmc.max_write_speed}
+
+on property:persist.mmc.cache_size=*
+ write /sys/block/mmcblk0/cache_size ${persist.mmc.cache_size}
+
+on early-init
+ mount debugfs debugfs /sys/kernel/debug
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index e8b46eb..32817fa 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -5,8 +5,8 @@
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data
export ANDROID_STORAGE /storage
+ export EXTERNAL_STORAGE /sdcard
export ASEC_MOUNTPOINT /mnt/asec
- export LOOP_MOUNTPOINT /mnt/obb
export BOOTCLASSPATH %BOOTCLASSPATH%
export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
%EXPORT_GLOBAL_ASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 12999bd..9ab2333 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -7,24 +7,34 @@
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
+import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
-import /init.trace.rc
on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
+ # Disable sysrq from keyboard
+ write /proc/sys/kernel/sysrq 0
+
# Set the security context of /adb_keys if present.
restorecon /adb_keys
# Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
mkdir /mnt 0775 root system
+ # Set the security context of /postinstall if present.
+ restorecon /postinstall
+
start ueventd
on init
sysclktz 0
+ # Mix device-specific information into the entropy pool
+ copy /proc/cmdline /dev/urandom
+ copy /default.prop /dev/urandom
+
# Backward compatibility.
symlink /system/etc /etc
symlink /sys/kernel/debug /d
@@ -36,61 +46,90 @@
mount cgroup none /acct cpuacct
mkdir /acct/uid
- # Create cgroup mount point for memory
- mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000
- mkdir /sys/fs/cgroup/memory 0750 root system
- mount cgroup none /sys/fs/cgroup/memory memory
- write /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1
- chown root system /sys/fs/cgroup/memory/tasks
- chmod 0660 /sys/fs/cgroup/memory/tasks
- mkdir /sys/fs/cgroup/memory/sw 0750 root system
- write /sys/fs/cgroup/memory/sw/memory.swappiness 100
- write /sys/fs/cgroup/memory/sw/memory.move_charge_at_immigrate 1
- chown root system /sys/fs/cgroup/memory/sw/tasks
- chmod 0660 /sys/fs/cgroup/memory/sw/tasks
+ # Create energy-aware scheduler tuning nodes
+ mkdir /dev/stune
+ mount cgroup none /dev/stune schedtune
+ mkdir /dev/stune/foreground
+ mkdir /dev/stune/background
+ mkdir /dev/stune/top-app
+ chown system system /dev/stune
+ chown system system /dev/stune/foreground
+ chown system system /dev/stune/background
+ chown system system /dev/stune/top-app
+ chown system system /dev/stune/tasks
+ chown system system /dev/stune/foreground/tasks
+ chown system system /dev/stune/background/tasks
+ chown system system /dev/stune/top-app/tasks
+ chmod 0664 /dev/stune/tasks
+ chmod 0664 /dev/stune/foreground/tasks
+ chmod 0664 /dev/stune/background/tasks
+ chmod 0664 /dev/stune/top-app/tasks
+ # Mount staging areas for devices managed by vold
# See storage config details at http://source.android.com/tech/storage/
- mkdir /mnt/shell 0700 shell shell
- mkdir /mnt/media_rw 0700 media_rw media_rw
- mkdir /storage 0751 root sdcard_r
+ mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000
+ restorecon_recursive /mnt
- # Directory for putting things only root should see.
+ mount configfs none /config
+ chmod 0775 /config/sdcardfs
+ chown system package_info /config/sdcardfs
+
mkdir /mnt/secure 0700 root root
+ mkdir /mnt/secure/asec 0700 root root
+ mkdir /mnt/asec 0755 root system
+ mkdir /mnt/obb 0755 root system
+ mkdir /mnt/media_rw 0750 root media_rw
+ mkdir /mnt/user 0755 root root
+ mkdir /mnt/user/0 0755 root root
+ mkdir /mnt/expand 0771 system system
+ mkdir /mnt/appfuse 0711 root root
- # Directory for staging bindmounts
- mkdir /mnt/secure/staging 0700 root root
+ # Storage views to support runtime permissions
+ mkdir /mnt/runtime 0700 root root
+ mkdir /mnt/runtime/default 0755 root root
+ mkdir /mnt/runtime/default/self 0755 root root
+ mkdir /mnt/runtime/read 0755 root root
+ mkdir /mnt/runtime/read/self 0755 root root
+ mkdir /mnt/runtime/write 0755 root root
+ mkdir /mnt/runtime/write/self 0755 root root
- # Directory-target for where the secure container
- # imagefile directory will be bind-mounted
- mkdir /mnt/secure/asec 0700 root root
+ # Symlink to keep legacy apps working in multi-user world
+ symlink /storage/self/primary /sdcard
+ symlink /storage/self/primary /mnt/sdcard
+ symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
- # Secure container public mount points.
- mkdir /mnt/asec 0700 root system
- mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000
-
- # Filesystem image public mount points.
- mkdir /mnt/obb 0700 root system
- mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000
-
- # memory control cgroup
+ # root memory control cgroup, used by lmkd
mkdir /dev/memcg 0700 root system
mount cgroup none /dev/memcg memory
+ # app mem cgroups, used by activity manager, lmkd and zygote
+ mkdir /dev/memcg/apps/ 0755 system system
write /proc/sys/kernel/panic_on_oops 1
write /proc/sys/kernel/hung_task_timeout_secs 0
write /proc/cpu/alignment 4
+
+ # scheduler tunables
+ # Disable auto-scaling of scheduler tunables with hotplug. The tunables
+ # will vary across devices in unpredictable ways if allowed to scale with
+ # cpu cores.
+ write /proc/sys/kernel/sched_tunable_scaling 0
write /proc/sys/kernel/sched_latency_ns 10000000
write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
- write /proc/sys/kernel/sched_compat_yield 1
write /proc/sys/kernel/sched_child_runs_first 0
+
write /proc/sys/kernel/randomize_va_space 2
- write /proc/sys/kernel/kptr_restrict 2
write /proc/sys/vm/mmap_min_addr 32768
write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
- write /proc/sys/net/unix/max_dgram_qlen 300
+ write /proc/sys/net/unix/max_dgram_qlen 600
write /proc/sys/kernel/sched_rt_runtime_us 950000
write /proc/sys/kernel/sched_rt_period_us 1000000
+ # Assign reasonable ceiling values for socket rcv/snd buffers.
+ # These should almost always be overridden by the target per the
+ # the corresponding technology maximums.
+ write /proc/sys/net/core/rmem_max 262144
+ write /proc/sys/net/core/wmem_max 262144
+
# reflect fwmark from incoming packets onto generated replies
write /proc/sys/net/ipv4/fwmark_reflect 1
write /proc/sys/net/ipv6/fwmark_reflect 1
@@ -98,23 +137,79 @@
# set fwmark on accepted sockets
write /proc/sys/net/ipv4/tcp_fwmark_accept 1
+ # disable icmp redirects
+ write /proc/sys/net/ipv4/conf/all/accept_redirects 0
+ write /proc/sys/net/ipv6/conf/all/accept_redirects 0
+
# Create cgroup mount points for process groups
mkdir /dev/cpuctl
mount cgroup none /dev/cpuctl cpu
chown system system /dev/cpuctl
chown system system /dev/cpuctl/tasks
chmod 0666 /dev/cpuctl/tasks
- write /dev/cpuctl/cpu.shares 1024
- write /dev/cpuctl/cpu.rt_runtime_us 800000
write /dev/cpuctl/cpu.rt_period_us 1000000
+ write /dev/cpuctl/cpu.rt_runtime_us 950000
mkdir /dev/cpuctl/bg_non_interactive
chown system system /dev/cpuctl/bg_non_interactive/tasks
chmod 0666 /dev/cpuctl/bg_non_interactive/tasks
# 5.0 %
write /dev/cpuctl/bg_non_interactive/cpu.shares 52
- write /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 700000
write /dev/cpuctl/bg_non_interactive/cpu.rt_period_us 1000000
+ # active FIFO threads will never be in BG
+ write /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 10000
+
+ # sets up initial cpusets for ActivityManager
+ mkdir /dev/cpuset
+ mount cpuset none /dev/cpuset
+
+ # this ensures that the cpusets are present and usable, but the device's
+ # init.rc must actually set the correct cpus
+ mkdir /dev/cpuset/foreground
+ write /dev/cpuset/foreground/cpus 0
+ write /dev/cpuset/foreground/mems 0
+ mkdir /dev/cpuset/foreground/boost
+ write /dev/cpuset/foreground/boost/cpus 0
+ write /dev/cpuset/foreground/boost/mems 0
+ mkdir /dev/cpuset/background
+ write /dev/cpuset/background/cpus 0
+ write /dev/cpuset/background/mems 0
+
+ # system-background is for system tasks that should only run on
+ # little cores, not on bigs
+ # to be used only by init, so don't change system-bg permissions
+ mkdir /dev/cpuset/system-background
+ write /dev/cpuset/system-background/cpus 0
+ write /dev/cpuset/system-background/mems 0
+
+ mkdir /dev/cpuset/top-app
+ write /dev/cpuset/top-app/cpus 0
+ write /dev/cpuset/top-app/mems 0
+
+ # change permissions for all cpusets we'll touch at runtime
+ chown system system /dev/cpuset
+ chown system system /dev/cpuset/foreground
+ chown system system /dev/cpuset/foreground/boost
+ chown system system /dev/cpuset/background
+ chown system system /dev/cpuset/system-background
+ chown system system /dev/cpuset/top-app
+ chown system system /dev/cpuset/tasks
+ chown system system /dev/cpuset/foreground/tasks
+ chown system system /dev/cpuset/foreground/boost/tasks
+ chown system system /dev/cpuset/background/tasks
+ chown system system /dev/cpuset/system-background/tasks
+ chown system system /dev/cpuset/top-app/tasks
+
+ # set system-background to 0775 so SurfaceFlinger can touch it
+ chmod 0775 /dev/cpuset/system-background
+
+ chmod 0664 /dev/cpuset/foreground/tasks
+ chmod 0664 /dev/cpuset/foreground/boost/tasks
+ chmod 0664 /dev/cpuset/background/tasks
+ chmod 0664 /dev/cpuset/system-background/tasks
+ chmod 0664 /dev/cpuset/top-app/tasks
+ chmod 0664 /dev/cpuset/tasks
+
# qtaguid will limit access to specific data based on group memberships.
# net_bw_acct grants impersonation of socket owners.
@@ -140,6 +235,15 @@
# enable armv8_deprecated instruction hooks
write /proc/sys/abi/swp 1
+ # Linux's execveat() syscall may construct paths containing /dev/fd
+ # expecting it to point to /proc/self/fd
+ symlink /proc/self/fd /dev/fd
+
+ export DOWNLOAD_CACHE /data/cache
+
+ # set RLIMIT_NICE to allow priorities from 19 to -20
+ setrlimit 13 40 40
+
# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
@@ -147,8 +251,11 @@
trigger late-init
# Load properties from /system/ + /factory after fs mount.
-on load_all_props_action
- load_all_props
+on load_system_props_action
+ load_system_props
+
+on load_persist_props_action
+ load_persist_props
start logd
start logd-reinit
@@ -159,14 +266,32 @@
# Mount filesystems and start core system services.
on late-init
trigger early-fs
+
+ # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
+ # '--early' can be specified to skip entries with 'latemount'.
+ # /system and /vendor must be mounted by the end of the fs stage,
+ # while /data is optional.
trigger fs
trigger post-fs
- trigger post-fs-data
# Load properties from /system/ + /factory after fs mount. Place
# this in another action so that the load will be scheduled after the prior
# issued fs triggers have completed.
- trigger load_all_props_action
+ trigger load_system_props_action
+
+ # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
+ # to only mount entries with 'latemount'. This is needed if '--early' is
+ # specified in the previous mount_all command on the fs stage.
+ # With /system mounted and properties form /system + /factory available,
+ # some services can be started.
+ trigger late-fs
+
+ # Now we can mount /data. File encryption requires keymaster to decrypt
+ # /data, which in turn can only be loaded when system properties are present.
+ trigger post-fs-data
+
+ # Load persist properties and override properties (if enabled) from /data.
+ trigger load_persist_props_action
# Remove a file to wake up anything waiting for firmware.
trigger firmware_mounts_complete
@@ -174,13 +299,19 @@
trigger early-boot
trigger boot
-
on post-fs
start logd
# once everything is setup, no need to modify /
mount rootfs rootfs / ro remount
- # mount shared so changes propagate into child namespaces
+ # Mount shared so changes propagate into child namespaces
mount rootfs rootfs / shared rec
+ # Mount default storage into root namespace
+ mount none /mnt/runtime/default /storage slave bind rec
+
+ # Make sure /sys/kernel/debug (if present) is labeled properly
+ # Note that tracefs may be mounted under debug, so we need to cross filesystems
+ restorecon --recursive --cross-filesystems /sys/kernel/debug
+ chmod 0755 /sys/kernel/debug/tracing
# We chown/chmod /cache again so because mount is run as root + defaults
chown system cache /cache
@@ -192,6 +323,10 @@
# permissions if created by the recovery system.
mkdir /cache/recovery 0770 system cache
+ # Backup/restore mechanism uses the cache partition
+ mkdir /cache/backup_stage 0700 system system
+ mkdir /cache/backup 0700 system system
+
#change permissions on vmallocinfo so we can grab it from bugreports
chown root log /proc/vmallocinfo
chmod 0440 /proc/vmallocinfo
@@ -220,24 +355,26 @@
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
- # Make sure we have the device encryption key
- start logd
+ # Make sure we have the device encryption key.
start vold
installkey /data
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell
- bootchart_init
+ bootchart start
# Avoid predictable entropy pool. Carry over entropy from previous boot.
copy /data/system/entropy.dat /dev/urandom
# create basic filesystem structure
mkdir /data/misc 01771 system misc
- mkdir /data/misc/adb 02750 system shell
- mkdir /data/misc/bluedroid 0770 bluetooth net_bt_stack
- mkdir /data/misc/bluetooth 0770 system system
+ mkdir /data/misc/bluedroid 02770 bluetooth bluetooth
+ # Fix the access permissions and group ownership for 'bt_config.conf'
+ chmod 0660 /data/misc/bluedroid/bt_config.conf
+ chown bluetooth bluetooth /data/misc/bluedroid/bt_config.conf
+ mkdir /data/misc/bluetooth 0770 bluetooth bluetooth
+ mkdir /data/misc/bluetooth/logs 0770 bluetooth bluetooth
mkdir /data/misc/keystore 0700 keystore keystore
mkdir /data/misc/gatekeeper 0700 system system
mkdir /data/misc/keychain 0771 system system
@@ -259,13 +396,24 @@
chmod 0660 /data/misc/wifi/wpa_supplicant.conf
mkdir /data/local 0751 root root
mkdir /data/misc/media 0700 media media
+ mkdir /data/misc/audioserver 0700 audioserver audioserver
+ mkdir /data/misc/cameraserver 0700 cameraserver cameraserver
+ mkdir /data/misc/vold 0700 root root
mkdir /data/misc/boottrace 0771 system shell
+ mkdir /data/misc/update_engine 0700 root root
+ mkdir /data/misc/trace 0700 root root
+ # profile file layout
+ mkdir /data/misc/profiles 0771 system system
+ mkdir /data/misc/profiles/cur 0771 system system
+ mkdir /data/misc/profiles/ref 0771 system system
+ mkdir /data/misc/profman 0770 system shell
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp
mkdir /data/local/tmp 0771 shell shell
mkdir /data/data 0771 system system
mkdir /data/app-private 0771 system system
+ mkdir /data/app-ephemeral 0771 system system
mkdir /data/app-asec 0700 root root
mkdir /data/app-lib 0771 system system
mkdir /data/app 0771 system system
@@ -274,7 +422,12 @@
# create dalvik-cache, so as to enforce our permissions
mkdir /data/dalvik-cache 0771 root root
- mkdir /data/dalvik-cache/profiles 0711 system system
+ # create the A/B OTA directory, so as to enforce our permissions
+ mkdir /data/ota 0771 root root
+
+ # create the OTA package directory. It will be accessed by GmsCore (cache
+ # group), update_engine and update_verifier.
+ mkdir /data/ota_package 0770 system cache
# create resource-cache and double-check the perms
mkdir /data/resource-cache 0771 system system
@@ -292,34 +445,44 @@
# the following directory.
mkdir /data/mediadrm 0770 mediadrm mediadrm
- mkdir /data/adb 0700 root root
mkdir /data/anr 0775 system system
- # symlink to bugreport storage location
- symlink /data/data/com.android.shell/files/bugreports /data/bugreports
-
- # Separate location for storing security policy files on data
- mkdir /data/security 0711 system system
-
# Create all remaining /data root dirs so that they are made through init
# and get proper encryption policy installed
mkdir /data/backup 0700 system system
- mkdir /data/media 0770 media_rw media_rw
mkdir /data/ss 0700 system system
+
mkdir /data/system 0775 system system
mkdir /data/system/heapdump 0700 system system
- mkdir /data/user 0711 system system
+ mkdir /data/system/users 0775 system system
- # Reload policy from /data/security if present.
- setprop selinux.reload_policy 1
+ mkdir /data/system_de 0770 system system
+ mkdir /data/system_ce 0770 system system
+
+ mkdir /data/misc_de 01771 system misc
+ mkdir /data/misc_ce 01771 system misc
+
+ mkdir /data/user 0711 system system
+ mkdir /data/user_de 0711 system system
+ symlink /data/data /data/user/0
+
+ mkdir /data/media 0770 media_rw media_rw
+ mkdir /data/media/obb 0770 media_rw media_rw
+
+ mkdir /data/cache 0770 system cache
+ mkdir /data/cache/recovery 0770 system cache
+ mkdir /data/cache/backup_stage 0700 system system
+ mkdir /data/cache/backup 0700 system system
+
+ init_user0
# Set SELinux security contexts on upgrade or policy update.
- restorecon_recursive /data
+ restorecon --recursive --skip-ce /data
# Check any timezone data in /data is newer than the copy in /system, delete if not.
exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
- # If there is no fs-post-data action in the init.<device>.rc file, you
+ # If there is no post-fs-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
# won't work.
# Set indication (checked by vold) that we have finished this action
@@ -331,18 +494,15 @@
hostname localhost
domainname localdomain
- # set RLIMIT_NICE to allow priorities from 19 to -20
- setrlimit 13 40 40
-
# Memory management. Basic kernel parameters, and allow the high
# level system server to be able to adjust the kernel OOM driver
# parameters to match how it is managing things.
write /proc/sys/vm/overcommit_memory 1
write /proc/sys/vm/min_free_order_shift 4
chown root system /sys/module/lowmemorykiller/parameters/adj
- chmod 0220 /sys/module/lowmemorykiller/parameters/adj
+ chmod 0664 /sys/module/lowmemorykiller/parameters/adj
chown root system /sys/module/lowmemorykiller/parameters/minfree
- chmod 0220 /sys/module/lowmemorykiller/parameters/minfree
+ chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
# Tweak background writeout
write /proc/sys/vm/dirty_expire_centisecs 200
@@ -357,8 +517,8 @@
chown system system /sys/power/autosleep
chown system system /sys/power/state
chown system system /sys/power/wakeup_count
- chown radio system /sys/power/wake_lock
- chown radio system /sys/power/wake_unlock
+ chown radio wakelock /sys/power/wake_lock
+ chown radio wakelock /sys/power/wake_unlock
chmod 0660 /sys/power/state
chmod 0660 /sys/power/wake_lock
chmod 0660 /sys/power/wake_unlock
@@ -415,19 +575,16 @@
# Define default initial receive window size in segments.
setprop net.tcp.default_init_rwnd 60
+ # Start all binderized HAL daemons
+ start hwservicemanager
class_start core
on nonencrypted
+ # A/B update verifier that marks a successful boot.
+ exec - root cache -- /system/bin/update_verifier nonencrypted
class_start main
class_start late_start
-on property:vold.decrypt=trigger_default_encryption
- start defaultcrypto
-
-on property:vold.decrypt=trigger_encryption
- start surfaceflinger
- start encrypt
-
on property:sys.init_log_level=*
loglevel ${sys.init_log_level}
@@ -446,9 +603,13 @@
trigger post-fs-data
on property:vold.decrypt=trigger_restart_min_framework
+ # A/B update verifier that marks a successful boot.
+ exec - root cache -- /system/bin/update_verifier trigger_restart_min_framework
class_start main
on property:vold.decrypt=trigger_restart_framework
+ # A/B update verifier that marks a successful boot.
+ exec - root cache -- /system/bin/update_verifier trigger_restart_framework
class_start main
class_start late_start
@@ -459,6 +620,9 @@
on property:sys.powerctl=*
powerctl ${sys.powerctl}
+on property:sys.boot_completed=1
+ bootchart stop
+
# system server cannot write to /proc/sys files,
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.
@@ -469,6 +633,11 @@
on property:sys.sysctl.tcp_def_init_rwnd=*
write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
+on property:security.perf_harden=0
+ write /proc/sys/kernel/perf_event_paranoid 1
+
+on property:security.perf_harden=1
+ write /proc/sys/kernel/perf_event_paranoid 3
## Daemon processes to be run by init.
##
@@ -477,190 +646,26 @@
critical
seclabel u:r:ueventd:s0
-service logd /system/bin/logd
- class core
- socket logd stream 0666 logd logd
- socket logdr seqpacket 0666 logd logd
- socket logdw dgram 0222 logd logd
-
-service logd-reinit /system/bin/logd --reinit
- oneshot
- disabled
-
service healthd /sbin/healthd
class core
critical
seclabel u:r:healthd:s0
+ group root system wakelock
service console /system/bin/sh
class core
console
disabled
user shell
- group shell log
+ group shell log readproc
seclabel u:r:shell:s0
on property:ro.debuggable=1
+ # Give writes to anyone for the trace folder on debug builds.
+ # The folder is used to store method traces.
+ chmod 0773 /data/misc/trace
start console
-# adbd is controlled via property triggers in init.<platform>.usb.rc
-service adbd /sbin/adbd --root_seclabel=u:r:su:s0
- class core
- socket adbd stream 660 system system
- disabled
- seclabel u:r:adbd:s0
-
-# adbd on at boot in emulator
-on property:ro.kernel.qemu=1
- start adbd
-
-service lmkd /system/bin/lmkd
- class core
- critical
- socket lmkd seqpacket 0660 system system
-
-service servicemanager /system/bin/servicemanager
- class core
- user system
- group system
- critical
- onrestart restart healthd
- onrestart restart zygote
- onrestart restart media
- onrestart restart surfaceflinger
- onrestart restart drm
-
-service vold /system/bin/vold
- class core
- socket vold stream 0660 root mount
- ioprio be 2
-
-service netd /system/bin/netd
- class main
- socket netd stream 0660 root system
- socket dnsproxyd stream 0660 root inet
- socket mdns stream 0660 root system
- socket fwmarkd stream 0660 root inet
-
-service debuggerd /system/bin/debuggerd
- class main
-
-service debuggerd64 /system/bin/debuggerd64
- class main
-
-service ril-daemon /system/bin/rild
- class main
- socket rild stream 660 root radio
- socket rild-debug stream 660 radio system
- user root
- group radio cache inet misc audio log
-
-service surfaceflinger /system/bin/surfaceflinger
- class core
- user system
- group graphics drmrpc
- onrestart restart zygote
-
-service drm /system/bin/drmserver
- class main
- user drm
- group drm system inet drmrpc
-
-service media /system/bin/mediaserver
- class main
- user media
- group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
- ioprio rt 4
-
-# One shot invocation to deal with encrypted volume.
-service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
- disabled
- oneshot
- # vold will set vold.decrypt to trigger_restart_framework (default
- # encryption) or trigger_restart_min_framework (other encryption)
-
-# One shot invocation to encrypt unencrypted volumes
-service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default
- disabled
- oneshot
- # vold will set vold.decrypt to trigger_restart_framework (default
- # encryption)
-
-service bootanim /system/bin/bootanimation
- class core
- user graphics
- group graphics audio
- disabled
- oneshot
-
-service installd /system/bin/installd
- class main
- socket installd stream 600 system system
-
service flash_recovery /system/bin/install-recovery.sh
class main
oneshot
-
-service racoon /system/bin/racoon
- class main
- socket racoon stream 600 system system
- # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
- group vpn net_admin inet
- disabled
- oneshot
-
-service mtpd /system/bin/mtpd
- class main
- socket mtpd stream 600 system system
- user vpn
- group vpn net_admin inet net_raw
- disabled
- oneshot
-
-service keystore /system/bin/keystore /data/misc/keystore
- class main
- user keystore
- group keystore drmrpc
-
-service dumpstate /system/bin/dumpstate -s
- class main
- socket dumpstate stream 0660 shell log
- disabled
- oneshot
-
-service mdnsd /system/bin/mdnsd
- class main
- user mdnsr
- group inet net_raw
- socket mdnsd stream 0660 mdnsr inet
- disabled
- oneshot
-
-service uncrypt /system/bin/uncrypt
- class main
- disabled
- oneshot
-
-service pre-recovery /system/bin/uncrypt --reboot
- class main
- disabled
- oneshot
-
-service perfprofd /system/xbin/perfprofd
- class late_start
- user root
- oneshot
-
-on property:persist.logd.logpersistd=logcatd
- # all exec/services are called with umask(077), so no gain beyond 0700
- mkdir /data/misc/logd 0700 logd log
- # logd for write to /data/misc/logd, log group for read from pstore (-L)
- exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
- start logcatd
-
-service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
- class late_start
- disabled
- # logd for write to /data/misc/logd, log group for read from log daemon
- user logd
- group log
diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc
deleted file mode 100644
index cde9c37..0000000
--- a/rootdir/init.trace.rc
+++ /dev/null
@@ -1,62 +0,0 @@
-## Permissions to allow system-wide tracing to the kernel trace buffer.
-##
-on boot
-
-# Allow writing to the kernel trace log.
- chmod 0222 /sys/kernel/debug/tracing/trace_marker
-
-# Allow the shell group to enable (some) kernel tracing.
- chown root shell /sys/kernel/debug/tracing/trace_clock
- chown root shell /sys/kernel/debug/tracing/buffer_size_kb
- chown root shell /sys/kernel/debug/tracing/options/overwrite
- chown root shell /sys/kernel/debug/tracing/options/print-tgid
- chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable
- chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
- chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
- chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable
- chown root shell /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
- chown root shell /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
- chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
- chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
- chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
- chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
- chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction/enable
- chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable
- chown root shell /sys/kernel/debug/tracing/events/binder/binder_lock/enable
- chown root shell /sys/kernel/debug/tracing/events/binder/binder_locked/enable
- chown root shell /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
-
- chown root shell /sys/kernel/debug/tracing/tracing_on
-
- chmod 0664 /sys/kernel/debug/tracing/trace_clock
- chmod 0664 /sys/kernel/debug/tracing/buffer_size_kb
- chmod 0664 /sys/kernel/debug/tracing/options/overwrite
- chmod 0664 /sys/kernel/debug/tracing/options/print-tgid
- chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable
- chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
- chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
- chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable
- chmod 0664 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
- chmod 0664 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
- chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
- chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
- chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
- chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
- chmod 0664 /sys/kernel/debug/tracing/tracing_on
- chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction/enable
- chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable
- chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_lock/enable
- chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_locked/enable
- chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
-
-# Allow only the shell group to read and truncate the kernel trace.
- chown root shell /sys/kernel/debug/tracing/trace
- chmod 0660 /sys/kernel/debug/tracing/trace
-
-on property:persist.debug.atrace.boottrace=1
- start boottrace
-
-# Run atrace with the categories written in a file
-service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categories
- disabled
- oneshot
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
new file mode 100644
index 0000000..32f0198
--- /dev/null
+++ b/rootdir/init.usb.configfs.rc
@@ -0,0 +1,137 @@
+on property:sys.usb.config=none && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/UDC "none"
+ stop adbd
+ setprop sys.usb.ffs.ready 0
+ write /config/usb_gadget/g1/bDeviceClass 0
+ write /config/usb_gadget/g1/bDeviceSubClass 0
+ write /config/usb_gadget/g1/bDeviceProtocol 0
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ rmdir /config/usb_gadget/g1/functions/rndis.gs4
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=mtp && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
+ symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
+ symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=ptp && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp"
+ symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp_adb"
+ symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory"
+ symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_adb"
+ symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=audio_source && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource"
+ symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource_adb"
+ symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_audiosource"
+ symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_audiosource_adb"
+ symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f3
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=midi && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "midi"
+ symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=midi,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=midi,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "midi_adb"
+ symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=rndis && property:sys.usb.configfs=1
+ mkdir /config/usb_gadget/g1/functions/rndis.gs4
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "rndis"
+ symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1
+ mkdir /config/usb_gadget/g1/functions/rndis.gs4
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "rndis_adb"
+ symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index e290ca4..915d159 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -8,9 +8,25 @@
chmod 0660 /sys/class/android_usb/android0/f_mass_storage/lun/file
chown system system /sys/class/android_usb/android0/f_rndis/ethaddr
chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr
+ mkdir /data/misc/adb 02750 system shell
+ mkdir /data/adb 0700 root root
+
+# adbd is controlled via property triggers in init.<platform>.usb.rc
+service adbd /sbin/adbd --root_seclabel=u:r:su:s0
+ class core
+ socket adbd stream 660 system system
+ disabled
+ seclabel u:r:adbd:s0
+
+# adbd on at boot in emulator
+on property:ro.kernel.qemu=1
+ start adbd
+
+on boot
+ setprop sys.usb.configfs 0
# Used to disable USB when switching states
-on property:sys.usb.config=none
+on property:sys.usb.config=none && property:sys.usb.configfs=0
stop adbd
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/bDeviceClass 0
@@ -19,7 +35,7 @@
# adb only USB configuration
# This is the fallback configuration if the
# USB manager fails to set a standard configuration
-on property:sys.usb.config=adb
+on property:sys.usb.config=adb && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 4EE7
@@ -29,7 +45,7 @@
setprop sys.usb.state ${sys.usb.config}
# USB accessory configuration
-on property:sys.usb.config=accessory
+on property:sys.usb.config=accessory && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d00
@@ -38,7 +54,7 @@
setprop sys.usb.state ${sys.usb.config}
# USB accessory configuration, with adb
-on property:sys.usb.config=accessory,adb
+on property:sys.usb.config=accessory,adb && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d01
@@ -48,7 +64,7 @@
setprop sys.usb.state ${sys.usb.config}
# audio accessory configuration
-on property:sys.usb.config=audio_source
+on property:sys.usb.config=audio_source && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d02
@@ -57,7 +73,7 @@
setprop sys.usb.state ${sys.usb.config}
# audio accessory configuration, with adb
-on property:sys.usb.config=audio_source,adb
+on property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d03
@@ -67,7 +83,7 @@
setprop sys.usb.state ${sys.usb.config}
# USB and audio accessory configuration
-on property:sys.usb.config=accessory,audio_source
+on property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d04
@@ -76,7 +92,7 @@
setprop sys.usb.state ${sys.usb.config}
# USB and audio accessory configuration, with adb
-on property:sys.usb.config=accessory,audio_source,adb
+on property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d05
@@ -87,5 +103,36 @@
# Used to set USB configuration at boot and to switch the configuration
# when changing the default configuration
-on property:persist.sys.usb.config=*
+on boot && property:persist.sys.usb.config=*
setprop sys.usb.config ${persist.sys.usb.config}
+
+#
+# USB type C
+#
+
+# USB mode changes
+on property:sys.usb.typec.mode=dfp
+ write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode}
+ setprop sys.usb.typec.state ${sys.usb.typec.mode}
+
+on property:sys.usb.typec.mode=ufp
+ write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode}
+ setprop sys.usb.typec.state ${sys.usb.typec.mode}
+
+# USB data role changes
+on property:sys.usb.typec.data_role=device
+ write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role}
+ setprop sys.usb.typec.state ${sys.usb.typec.data_role}
+
+on property:sys.usb.typec.data_role=host
+ write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role}
+ setprop sys.usb.typec.state ${sys.usb.typec.data_role}
+
+# USB power role changes
+on property:sys.usb.typec.power_role=source
+ write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}
+ setprop sys.usb.typec.state ${sys.usb.typec.power_role}
+
+on property:sys.usb.typec.power_role=sink
+ write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}
+ setprop sys.usb.typec.state ${sys.usb.typec.power_role}
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 75961e6..d836c4e 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -1,8 +1,14 @@
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
+ priority -20
+ user root
+ group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
+ onrestart restart audioserver
+ onrestart restart cameraserver
onrestart restart media
onrestart restart netd
-
+ onrestart restart wificond
+ writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 68c0668..ed11164 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -1,12 +1,23 @@
service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
+ priority -20
+ user root
+ group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
+ onrestart restart audioserver
+ onrestart restart cameraserver
onrestart restart media
onrestart restart netd
+ onrestart restart wificond
+ writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
+ priority -20
+ user root
+ group root readproc
socket zygote_secondary stream 660 root system
onrestart restart zygote
+ writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index afb6d63..05ec16f 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -1,8 +1,14 @@
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
+ priority -20
+ user root
+ group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
+ onrestart restart audioserver
+ onrestart restart cameraserver
onrestart restart media
onrestart restart netd
-
+ onrestart restart wificond
+ writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 979ab3b..66e7750 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -1,12 +1,23 @@
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
+ priority -20
+ user root
+ group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
+ onrestart restart audioserver
+ onrestart restart cameraserver
onrestart restart media
onrestart restart netd
+ onrestart restart wificond
+ writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
+ priority -20
+ user root
+ group root readproc
socket zygote_secondary stream 660 root system
onrestart restart zygote
+ writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 9cf9ed9..0633a68 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,6 +1,13 @@
subsystem adf
devname uevent_devname
+# ueventd can only set permissions on device nodes and their associated
+# sysfs attributes, not on arbitrary paths.
+#
+# format for /dev rules: devname mode uid gid
+# format for /sys rules: nodename attr mode uid gid
+# shortcut: "mtd@NN" expands to "/dev/mtd/mtdNN"
+
/dev/null 0666 root root
/dev/zero 0666 root root
/dev/full 0666 root root
@@ -12,6 +19,7 @@
/dev/hw_random 0440 root system
/dev/ashmem 0666 root root
/dev/binder 0666 root root
+/dev/hwbinder 0666 root root
# Anyone can read the logs, but if they're not in the "logs"
# group, then they'll only see log entries for their UID.
@@ -30,11 +38,9 @@
# these should not be world writable
/dev/diag 0660 radio radio
/dev/diag_arm9 0660 radio radio
-/dev/android_adb 0660 adb adb
-/dev/android_adb_enable 0660 adb adb
/dev/ttyMSM0 0600 bluetooth bluetooth
-/dev/uhid 0660 system net_bt_stack
-/dev/uinput 0660 system net_bt_stack
+/dev/uhid 0660 system bluetooth
+/dev/uinput 0660 system bluetooth
/dev/alarm 0664 system radio
/dev/rtc0 0640 system system
/dev/tty0 0660 root system
@@ -95,3 +101,6 @@
/sys/devices/virtual/usb_composite/* enable 0664 root system
/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
/sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system
+
+# DVB API device nodes
+/dev/dvb* 0660 root system
diff --git a/run-as/Android.mk b/run-as/Android.mk
index 3774acc..7111fbe 100644
--- a/run-as/Android.mk
+++ b/run-as/Android.mk
@@ -1,12 +1,8 @@
LOCAL_PATH:= $(call my-dir)
+
include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := run-as.c package.c
-
-LOCAL_SHARED_LIBRARIES := libselinux
-
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_MODULE := run-as
-
-LOCAL_CFLAGS := -Werror
-
+LOCAL_SHARED_LIBRARIES := libselinux libpackagelistparser libminijail
+LOCAL_SRC_FILES := run-as.cpp
include $(BUILD_EXECUTABLE)
diff --git a/run-as/package.c b/run-as/package.c
deleted file mode 100644
index 9e1f5bb..0000000
--- a/run-as/package.c
+++ /dev/null
@@ -1,542 +0,0 @@
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <private/android_filesystem_config.h>
-#include "package.h"
-
-/*
- * WARNING WARNING WARNING WARNING
- *
- * The following code runs as root on production devices, before
- * the run-as command has dropped the uid/gid. Hence be very
- * conservative and keep in mind the following:
- *
- * - Performance does not matter here, clarity and safety of the code
- * does however. Documentation is a must.
- *
- * - Avoid calling C library functions with complex implementations
- * like malloc() and printf(). You want to depend on simple system
- * calls instead, which behaviour is not going to be altered in
- * unpredictible ways by environment variables or system properties.
- *
- * - Do not trust user input and/or the filesystem whenever possible.
- *
- */
-
-/* The file containing the list of installed packages on the system */
-#define PACKAGES_LIST_FILE "/data/system/packages.list"
-
-/* Copy 'srclen' string bytes from 'src' into buffer 'dst' of size 'dstlen'
- * This function always zero-terminate the destination buffer unless
- * 'dstlen' is 0, even in case of overflow.
- * Returns a pointer into the src string, leaving off where the copy
- * has stopped. The copy will stop when dstlen, srclen or a null
- * character on src has been reached.
- */
-static const char*
-string_copy(char* dst, size_t dstlen, const char* src, size_t srclen)
-{
- const char* srcend = src + srclen;
- const char* dstend = dst + dstlen;
-
- if (dstlen == 0)
- return src;
-
- dstend--; /* make room for terminating zero */
-
- while (dst < dstend && src < srcend && *src != '\0')
- *dst++ = *src++;
-
- *dst = '\0'; /* zero-terminate result */
- return src;
-}
-
-/* Open 'filename' and map it into our address-space.
- * Returns buffer address, or NULL on error
- * On exit, *filesize will be set to the file's size, or 0 on error
- */
-static void*
-map_file(const char* filename, size_t* filesize)
-{
- int fd, ret, old_errno;
- struct stat st;
- size_t length = 0;
- void* address = NULL;
- gid_t oldegid;
-
- *filesize = 0;
-
- /*
- * Temporarily switch effective GID to allow us to read
- * the packages file
- */
-
- oldegid = getegid();
- if (setegid(AID_PACKAGE_INFO) < 0) {
- return NULL;
- }
-
- /* open the file for reading */
- fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
- if (fd < 0) {
- return NULL;
- }
-
- /* restore back to our old egid */
- if (setegid(oldegid) < 0) {
- goto EXIT;
- }
-
- /* get its size */
- ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
- if (ret < 0)
- goto EXIT;
-
- /* Ensure that the file is owned by the system user */
- if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_PACKAGE_INFO)) {
- goto EXIT;
- }
-
- /* Ensure that the file has sane permissions */
- if ((st.st_mode & S_IWOTH) != 0) {
- goto EXIT;
- }
-
- /* Ensure that the size is not ridiculously large */
- length = (size_t)st.st_size;
- if ((off_t)length != st.st_size) {
- errno = ENOMEM;
- goto EXIT;
- }
-
- /* Memory-map the file now */
- do {
- address = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
- } while (address == MAP_FAILED && errno == EINTR);
- if (address == MAP_FAILED) {
- address = NULL;
- goto EXIT;
- }
-
- /* We're good, return size */
- *filesize = length;
-
-EXIT:
- /* close the file, preserve old errno for better diagnostics */
- old_errno = errno;
- close(fd);
- errno = old_errno;
-
- return address;
-}
-
-/* unmap the file, but preserve errno */
-static void
-unmap_file(void* address, size_t size)
-{
- int old_errno = errno;
- TEMP_FAILURE_RETRY(munmap(address, size));
- errno = old_errno;
-}
-
-/* Check that a given directory:
- * - exists
- * - is owned by a given uid/gid
- * - is a real directory, not a symlink
- * - isn't readable or writable by others
- *
- * Return 0 on success, or -1 on error.
- * errno is set to EINVAL in case of failed check.
- */
-static int
-check_directory_ownership(const char* path, uid_t uid)
-{
- int ret;
- struct stat st;
-
- do {
- ret = lstat(path, &st);
- } while (ret < 0 && errno == EINTR);
-
- if (ret < 0)
- return -1;
-
- /* must be a real directory, not a symlink */
- if (!S_ISDIR(st.st_mode))
- goto BAD;
-
- /* must be owned by specific uid/gid */
- if (st.st_uid != uid || st.st_gid != uid)
- goto BAD;
-
- /* must not be readable or writable by others */
- if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0)
- goto BAD;
-
- /* everything ok */
- return 0;
-
-BAD:
- errno = EINVAL;
- return -1;
-}
-
-/* This function is used to check the data directory path for safety.
- * We check that every sub-directory is owned by the 'system' user
- * and exists and is not a symlink. We also check that the full directory
- * path is properly owned by the user ID.
- *
- * Return 0 on success, -1 on error.
- */
-int
-check_data_path(const char* dataPath, uid_t uid)
-{
- int nn;
-
- /* the path should be absolute */
- if (dataPath[0] != '/') {
- errno = EINVAL;
- return -1;
- }
-
- /* look for all sub-paths, we do that by finding
- * directory separators in the input path and
- * checking each sub-path independently
- */
- for (nn = 1; dataPath[nn] != '\0'; nn++)
- {
- char subpath[PATH_MAX];
-
- /* skip non-separator characters */
- if (dataPath[nn] != '/')
- continue;
-
- /* handle trailing separator case */
- if (dataPath[nn+1] == '\0') {
- break;
- }
-
- /* found a separator, check that dataPath is not too long. */
- if (nn >= (int)(sizeof subpath)) {
- errno = EINVAL;
- return -1;
- }
-
- /* reject any '..' subpath */
- if (nn >= 3 &&
- dataPath[nn-3] == '/' &&
- dataPath[nn-2] == '.' &&
- dataPath[nn-1] == '.') {
- errno = EINVAL;
- return -1;
- }
-
- /* copy to 'subpath', then check ownership */
- memcpy(subpath, dataPath, nn);
- subpath[nn] = '\0';
-
- if (check_directory_ownership(subpath, AID_SYSTEM) < 0)
- return -1;
- }
-
- /* All sub-paths were checked, now verify that the full data
- * directory is owned by the application uid
- */
- if (check_directory_ownership(dataPath, uid) < 0)
- return -1;
-
- /* all clear */
- return 0;
-}
-
-/* Return TRUE iff a character is a space or tab */
-static inline int
-is_space(char c)
-{
- return (c == ' ' || c == '\t');
-}
-
-/* Skip any space or tab character from 'p' until 'end' is reached.
- * Return new position.
- */
-static const char*
-skip_spaces(const char* p, const char* end)
-{
- while (p < end && is_space(*p))
- p++;
-
- return p;
-}
-
-/* Skip any non-space and non-tab character from 'p' until 'end'.
- * Return new position.
- */
-static const char*
-skip_non_spaces(const char* p, const char* end)
-{
- while (p < end && !is_space(*p))
- p++;
-
- return p;
-}
-
-/* Find the first occurence of 'ch' between 'p' and 'end'
- * Return its position, or 'end' if none is found.
- */
-static const char*
-find_first(const char* p, const char* end, char ch)
-{
- while (p < end && *p != ch)
- p++;
-
- return p;
-}
-
-/* Check that the non-space string starting at 'p' and eventually
- * ending at 'end' equals 'name'. Return new position (after name)
- * on success, or NULL on failure.
- *
- * This function fails is 'name' is NULL, empty or contains any space.
- */
-static const char*
-compare_name(const char* p, const char* end, const char* name)
-{
- /* 'name' must not be NULL or empty */
- if (name == NULL || name[0] == '\0' || p == end)
- return NULL;
-
- /* compare characters to those in 'name', excluding spaces */
- while (*name) {
- /* note, we don't check for *p == '\0' since
- * it will be caught in the next conditional.
- */
- if (p >= end || is_space(*p))
- goto BAD;
-
- if (*p != *name)
- goto BAD;
-
- p++;
- name++;
- }
-
- /* must be followed by end of line or space */
- if (p < end && !is_space(*p))
- goto BAD;
-
- return p;
-
-BAD:
- return NULL;
-}
-
-/* Parse one or more whitespace characters starting from '*pp'
- * until 'end' is reached. Updates '*pp' on exit.
- *
- * Return 0 on success, -1 on failure.
- */
-static int
-parse_spaces(const char** pp, const char* end)
-{
- const char* p = *pp;
-
- if (p >= end || !is_space(*p)) {
- errno = EINVAL;
- return -1;
- }
- p = skip_spaces(p, end);
- *pp = p;
- return 0;
-}
-
-/* Parse a positive decimal number starting from '*pp' until 'end'
- * is reached. Adjust '*pp' on exit. Return decimal value or -1
- * in case of error.
- *
- * If the value is larger than INT_MAX, -1 will be returned,
- * and errno set to EOVERFLOW.
- *
- * If '*pp' does not start with a decimal digit, -1 is returned
- * and errno set to EINVAL.
- */
-static int
-parse_positive_decimal(const char** pp, const char* end)
-{
- const char* p = *pp;
- int value = 0;
- int overflow = 0;
-
- if (p >= end || *p < '0' || *p > '9') {
- errno = EINVAL;
- return -1;
- }
-
- while (p < end) {
- int ch = *p;
- unsigned d = (unsigned)(ch - '0');
- int val2;
-
- if (d >= 10U) /* d is unsigned, no lower bound check */
- break;
-
- val2 = value*10 + (int)d;
- if (val2 < value)
- overflow = 1;
- value = val2;
- p++;
- }
- *pp = p;
-
- if (overflow) {
- errno = EOVERFLOW;
- value = -1;
- }
- return value;
-}
-
-/* Read the system's package database and extract information about
- * 'pkgname'. Return 0 in case of success, or -1 in case of error.
- *
- * If the package is unknown, return -1 and set errno to ENOENT
- * If the package database is corrupted, return -1 and set errno to EINVAL
- */
-int
-get_package_info(const char* pkgName, PackageInfo *info)
-{
- char* buffer;
- size_t buffer_len;
- const char* p;
- const char* buffer_end;
- int result = -1;
-
- info->uid = 0;
- info->isDebuggable = 0;
- info->dataDir[0] = '\0';
- info->seinfo[0] = '\0';
-
- buffer = map_file(PACKAGES_LIST_FILE, &buffer_len);
- if (buffer == NULL)
- return -1;
-
- p = buffer;
- buffer_end = buffer + buffer_len;
-
- /* expect the following format on each line of the control file:
- *
- * <pkgName> <uid> <debugFlag> <dataDir> <seinfo>
- *
- * where:
- * <pkgName> is the package's name
- * <uid> is the application-specific user Id (decimal)
- * <debugFlag> is 1 if the package is debuggable, or 0 otherwise
- * <dataDir> is the path to the package's data directory (e.g. /data/data/com.example.foo)
- * <seinfo> is the seinfo label associated with the package
- *
- * The file is generated in com.android.server.PackageManagerService.Settings.writeLP()
- */
-
- while (p < buffer_end) {
- /* find end of current line and start of next one */
- const char* end = find_first(p, buffer_end, '\n');
- const char* next = (end < buffer_end) ? end + 1 : buffer_end;
- const char* q;
- int uid, debugFlag;
-
- /* first field is the package name */
- p = compare_name(p, end, pkgName);
- if (p == NULL)
- goto NEXT_LINE;
-
- /* skip spaces */
- if (parse_spaces(&p, end) < 0)
- goto BAD_FORMAT;
-
- /* second field is the pid */
- uid = parse_positive_decimal(&p, end);
- if (uid < 0)
- return -1;
-
- info->uid = (uid_t) uid;
-
- /* skip spaces */
- if (parse_spaces(&p, end) < 0)
- goto BAD_FORMAT;
-
- /* third field is debug flag (0 or 1) */
- debugFlag = parse_positive_decimal(&p, end);
- switch (debugFlag) {
- case 0:
- info->isDebuggable = 0;
- break;
- case 1:
- info->isDebuggable = 1;
- break;
- default:
- goto BAD_FORMAT;
- }
-
- /* skip spaces */
- if (parse_spaces(&p, end) < 0)
- goto BAD_FORMAT;
-
- /* fourth field is data directory path and must not contain
- * spaces.
- */
- q = skip_non_spaces(p, end);
- if (q == p)
- goto BAD_FORMAT;
-
- p = string_copy(info->dataDir, sizeof info->dataDir, p, q - p);
-
- /* skip spaces */
- if (parse_spaces(&p, end) < 0)
- goto BAD_FORMAT;
-
- /* fifth field is the seinfo string */
- q = skip_non_spaces(p, end);
- if (q == p)
- goto BAD_FORMAT;
-
- string_copy(info->seinfo, sizeof info->seinfo, p, q - p);
-
- /* Ignore the rest */
- result = 0;
- goto EXIT;
-
- NEXT_LINE:
- p = next;
- }
-
- /* the package is unknown */
- errno = ENOENT;
- result = -1;
- goto EXIT;
-
-BAD_FORMAT:
- errno = EINVAL;
- result = -1;
-
-EXIT:
- unmap_file(buffer, buffer_len);
- return result;
-}
diff --git a/run-as/package.h b/run-as/package.h
deleted file mode 100644
index 34603c0..0000000
--- a/run-as/package.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-#ifndef RUN_AS_PACKAGE_H
-#define RUN_AS_PACKAGE_H
-
-#include <limits.h>
-#include <sys/types.h>
-
-typedef enum {
- PACKAGE_IS_DEBUGGABLE = 0,
- PACKAGE_IS_NOT_DEBUGGABLE,
- PACKAGE_IS_UNKNOWN,
-} PackageStatus;
-
-typedef struct {
- uid_t uid;
- char isDebuggable;
- char dataDir[PATH_MAX];
- char seinfo[PATH_MAX];
-} PackageInfo;
-
-/* see documentation in package.c for these functiosn */
-
-extern int get_package_info(const char* packageName, PackageInfo* info);
-
-extern int check_data_path(const char* dataDir, uid_t uid);
-
-#endif /* RUN_AS_PACKAGE_H */
diff --git a/run-as/run-as.c b/run-as/run-as.c
deleted file mode 100644
index 368b8f1..0000000
--- a/run-as/run-as.c
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define PROGNAME "run-as"
-#define LOG_TAG PROGNAME
-
-#include <dirent.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/capability.h>
-#include <sys/cdefs.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
-
-#include "package.h"
-
-/*
- * WARNING WARNING WARNING WARNING
- *
- * This program runs with CAP_SETUID and CAP_SETGID capabilities on Android
- * production devices. Be very conservative when modifying it to avoid any
- * serious security issue. Keep in mind the following:
- *
- * - This program should only run for the 'root' or 'shell' users
- *
- * - Avoid anything that is more complex than simple system calls
- * until the uid/gid has been dropped to that of a normal user
- * or you are sure to exit.
- *
- * This avoids depending on environment variables, system properties
- * and other external factors that may affect the C library in
- * unpredictable ways.
- *
- * - Do not trust user input and/or the filesystem whenever possible.
- *
- * Read README.TXT for more details.
- *
- *
- *
- * The purpose of this program is to run a command as a specific
- * application user-id. Typical usage is:
- *
- * run-as <package-name> <command> <args>
- *
- * The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file
- * capabilities, but will check the following:
- *
- * - that it is invoked from the 'shell' or 'root' user (abort otherwise)
- * - that '<package-name>' is the name of an installed and debuggable package
- * - that the package's data directory is well-formed (see package.c)
- *
- * If so, it will drop to the application's user id / group id, cd to the
- * package's data directory, then run the command there.
- *
- * NOTE: In the future it might not be possible to cd to the package's data
- * directory under that package's user id / group id, in which case this
- * utility will need to be changed accordingly.
- *
- * This can be useful for a number of different things on production devices:
- *
- * - Allow application developers to look at their own applicative data
- * during development.
- *
- * - Run the 'gdbserver' binary executable to allow native debugging
- */
-
-__noreturn static void
-panic(const char* format, ...)
-{
- va_list args;
- int e = errno;
-
- fprintf(stderr, "%s: ", PROGNAME);
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
- exit(e ? -e : 1);
-}
-
-static void
-usage(void)
-{
- panic("Usage:\n " PROGNAME " <package-name> <command> [<args>]\n");
-}
-
-int main(int argc, char **argv)
-{
- const char* pkgname;
- int myuid, uid, gid;
- PackageInfo info;
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
-
- /* check arguments */
- if (argc < 2) {
- usage();
- }
-
- /* check userid of caller - must be 'shell' or 'root' */
- myuid = getuid();
- if (myuid != AID_SHELL && myuid != AID_ROOT) {
- panic("only 'shell' or 'root' users can run this program\n");
- }
-
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capdata[CAP_TO_INDEX(CAP_SETUID)].effective |= CAP_TO_MASK(CAP_SETUID);
- capdata[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID);
- capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
- capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
-
- if (capset(&capheader, &capdata[0]) < 0) {
- panic("Could not set capabilities: %s\n", strerror(errno));
- }
-
- /* retrieve package information from system (does setegid) */
- pkgname = argv[1];
- if (get_package_info(pkgname, &info) < 0) {
- panic("Package '%s' is unknown\n", pkgname);
- }
-
- /* reject system packages */
- if (info.uid < AID_APP) {
- panic("Package '%s' is not an application\n", pkgname);
- }
-
- /* reject any non-debuggable package */
- if (!info.isDebuggable) {
- panic("Package '%s' is not debuggable\n", pkgname);
- }
-
- /* check that the data directory path is valid */
- if (check_data_path(info.dataDir, info.uid) < 0) {
- panic("Package '%s' has corrupt installation\n", pkgname);
- }
-
- /* Ensure that we change all real/effective/saved IDs at the
- * same time to avoid nasty surprises.
- */
- uid = gid = info.uid;
- if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) {
- panic("Permission denied\n");
- }
-
- /* Required if caller has uid and gid all non-zero */
- memset(&capdata, 0, sizeof(capdata));
- if (capset(&capheader, &capdata[0]) < 0) {
- panic("Could not clear all capabilities: %s\n", strerror(errno));
- }
-
- if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
- panic("Could not set SELinux security context: %s\n", strerror(errno));
- }
-
- /* cd into the data directory */
- if (TEMP_FAILURE_RETRY(chdir(info.dataDir)) < 0) {
- panic("Could not cd to package's data directory: %s\n", strerror(errno));
- }
-
- /* User specified command for exec. */
- if ((argc >= 3) && (execvp(argv[2], argv+2) < 0)) {
- panic("exec failed for %s: %s\n", argv[2], strerror(errno));
- }
-
- /* Default exec shell. */
- execlp("/system/bin/sh", "sh", NULL);
-
- panic("exec failed: %s\n", strerror(errno));
-}
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
new file mode 100644
index 0000000..e7b8cc2
--- /dev/null
+++ b/run-as/run-as.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libminijail.h>
+#include <scoped_minijail.h>
+
+#include <packagelistparser/packagelistparser.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+
+// The purpose of this program is to run a command as a specific
+// application user-id. Typical usage is:
+//
+// run-as <package-name> <command> <args>
+//
+// The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file
+// capabilities, but will check the following:
+//
+// - that it is invoked from the 'shell' or 'root' user (abort otherwise)
+// - that '<package-name>' is the name of an installed and debuggable package
+// - that the package's data directory is well-formed
+//
+// If so, it will drop to the application's user id / group id, cd to the
+// package's data directory, then run the command there.
+//
+// This can be useful for a number of different things on production devices:
+//
+// - Allow application developers to look at their own application data
+// during development.
+//
+// - Run the 'gdbserver' binary executable to allow native debugging
+//
+
+static bool packagelist_parse_callback(pkg_info* this_package, void* userdata) {
+ pkg_info* p = reinterpret_cast<pkg_info*>(userdata);
+ if (strcmp(p->name, this_package->name) == 0) {
+ *p = *this_package;
+ return false; // Stop searching.
+ }
+ packagelist_free(this_package);
+ return true; // Keep searching.
+}
+
+static bool check_directory(const char* path, uid_t uid) {
+ struct stat st;
+ if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) return false;
+
+ // /data/user/0 is a known safe symlink.
+ if (strcmp("/data/user/0", path) == 0) return true;
+
+ // Must be a real directory, not a symlink.
+ if (!S_ISDIR(st.st_mode)) return false;
+
+ // Must be owned by specific uid/gid.
+ if (st.st_uid != uid || st.st_gid != uid) return false;
+
+ // Must not be readable or writable by others.
+ if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) return false;
+
+ return true;
+}
+
+// This function is used to check the data directory path for safety.
+// We check that every sub-directory is owned by the 'system' user
+// and exists and is not a symlink. We also check that the full directory
+// path is properly owned by the user ID.
+static bool check_data_path(const char* data_path, uid_t uid) {
+ // The path should be absolute.
+ if (data_path[0] != '/') return false;
+
+ // Look for all sub-paths, we do that by finding
+ // directory separators in the input path and
+ // checking each sub-path independently.
+ for (int nn = 1; data_path[nn] != '\0'; nn++) {
+ char subpath[PATH_MAX];
+
+ /* skip non-separator characters */
+ if (data_path[nn] != '/') continue;
+
+ /* handle trailing separator case */
+ if (data_path[nn+1] == '\0') break;
+
+ /* found a separator, check that data_path is not too long. */
+ if (nn >= (int)(sizeof subpath)) return false;
+
+ /* reject any '..' subpath */
+ if (nn >= 3 &&
+ data_path[nn-3] == '/' &&
+ data_path[nn-2] == '.' &&
+ data_path[nn-1] == '.') {
+ return false;
+ }
+
+ /* copy to 'subpath', then check ownership */
+ memcpy(subpath, data_path, nn);
+ subpath[nn] = '\0';
+
+ if (!check_directory(subpath, AID_SYSTEM)) return false;
+ }
+
+ // All sub-paths were checked, now verify that the full data
+ // directory is owned by the application uid.
+ return check_directory(data_path, uid);
+}
+
+int main(int argc, char* argv[]) {
+ // Check arguments.
+ if (argc < 2) {
+ error(1, 0, "usage: run-as <package-name> [--user <uid>] <command> [<args>]\n");
+ }
+
+ // This program runs with CAP_SETUID and CAP_SETGID capabilities on Android
+ // production devices. Check user id of caller --- must be 'shell' or 'root'.
+ if (getuid() != AID_SHELL && getuid() != AID_ROOT) {
+ error(1, 0, "only 'shell' or 'root' users can run this program");
+ }
+
+ char* pkgname = argv[1];
+ int cmd_argv_offset = 2;
+
+ // Get user_id from command line if provided.
+ int userId = 0;
+ if ((argc >= 4) && !strcmp(argv[2], "--user")) {
+ userId = atoi(argv[3]);
+ if (userId < 0) error(1, 0, "negative user id: %d", userId);
+ cmd_argv_offset += 2;
+ }
+
+ // Retrieve package information from system, switching egid so we can read the file.
+ gid_t old_egid = getegid();
+ if (setegid(AID_PACKAGE_INFO) == -1) error(1, errno, "setegid(AID_PACKAGE_INFO) failed");
+ pkg_info info;
+ memset(&info, 0, sizeof(info));
+ info.name = pkgname;
+ if (!packagelist_parse(packagelist_parse_callback, &info)) {
+ error(1, errno, "packagelist_parse failed");
+ }
+ if (info.uid == 0) {
+ error(1, 0, "unknown package: %s", pkgname);
+ }
+ if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
+
+ // Verify that user id is not too big.
+ if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) {
+ error(1, 0, "user id too big: %d", userId);
+ }
+
+ // Calculate user app ID.
+ uid_t userAppId = (AID_USER_OFFSET * userId) + info.uid;
+
+ // Reject system packages.
+ if (userAppId < AID_APP) {
+ error(1, 0, "package not an application: %s", pkgname);
+ }
+
+ // Reject any non-debuggable package.
+ if (!info.debuggable) {
+ error(1, 0, "package not debuggable: %s", pkgname);
+ }
+
+ // Check that the data directory path is valid.
+ if (!check_data_path(info.data_dir, userAppId)) {
+ error(1, 0, "package has corrupt installation: %s", pkgname);
+ }
+
+ // Ensure that we change all real/effective/saved IDs at the
+ // same time to avoid nasty surprises.
+ uid_t uid = userAppId;
+ uid_t gid = userAppId;
+ ScopedMinijail j(minijail_new());
+ minijail_change_uid(j.get(), uid);
+ minijail_change_gid(j.get(), gid);
+ minijail_enter(j.get());
+
+ if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
+ error(1, errno, "couldn't set SELinux security context");
+ }
+
+ // cd into the data directory, and set $HOME correspondingly.
+ if (TEMP_FAILURE_RETRY(chdir(info.data_dir)) == -1) {
+ error(1, errno, "couldn't chdir to package's data directory");
+ }
+ setenv("HOME", info.data_dir, 1);
+
+ // Reset parts of the environment, like su would.
+ setenv("PATH", _PATH_DEFPATH, 1);
+ unsetenv("IFS");
+
+ // Set the user-specific parts for this user.
+ passwd* pw = getpwuid(uid);
+ setenv("LOGNAME", pw->pw_name, 1);
+ setenv("SHELL", pw->pw_shell, 1);
+ setenv("USER", pw->pw_name, 1);
+
+ // User specified command for exec.
+ if ((argc >= cmd_argv_offset + 1) &&
+ (execvp(argv[cmd_argv_offset], argv+cmd_argv_offset) == -1)) {
+ error(1, errno, "exec failed for %s", argv[cmd_argv_offset]);
+ }
+
+ // Default exec shell.
+ execlp(_PATH_BSHELL, "sh", NULL);
+ error(1, errno, "exec failed");
+}
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index cb3a8fb..0c58574 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -2,10 +2,12 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := sdcard.c
+LOCAL_SRC_FILES := sdcard.cpp fuse.cpp
LOCAL_MODULE := sdcard
LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
+LOCAL_SHARED_LIBRARIES := libbase libcutils libminijail libpackagelistparser
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SANITIZE := integer
+LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
diff --git a/sdcard/fuse.cpp b/sdcard/fuse.cpp
new file mode 100644
index 0000000..3f0f95f
--- /dev/null
+++ b/sdcard/fuse.cpp
@@ -0,0 +1,1495 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LOG_TAG "sdcard"
+
+#include "fuse.h"
+
+#include <android-base/logging.h>
+
+/* FUSE_CANONICAL_PATH is not currently upstreamed */
+#define FUSE_CANONICAL_PATH 2016
+
+#define FUSE_UNKNOWN_INO 0xffffffff
+
+/* Pseudo-error constant used to indicate that no fuse status is needed
+ * or that a reply has already been written. */
+#define NO_STATUS 1
+
+static inline void *id_to_ptr(__u64 nid)
+{
+ return (void *) (uintptr_t) nid;
+}
+
+static inline __u64 ptr_to_id(void *ptr)
+{
+ return (__u64) (uintptr_t) ptr;
+}
+
+static void acquire_node_locked(struct node* node)
+{
+ node->refcount++;
+ DLOG(INFO) << "ACQUIRE " << std::hex << node << std::dec
+ << " (" << node->name << ") rc=" << node->refcount;
+}
+
+static void remove_node_from_parent_locked(struct node* node);
+
+static void release_node_locked(struct node* node)
+{
+ DLOG(INFO) << "RELEASE " << std::hex << node << std::dec
+ << " (" << node->name << ") rc=" << node->refcount;
+ if (node->refcount > 0) {
+ node->refcount--;
+ if (!node->refcount) {
+ DLOG(INFO) << "DESTROY " << std::hex << node << std::dec << " (" << node->name << ")";
+ remove_node_from_parent_locked(node);
+
+ /* TODO: remove debugging - poison memory */
+ memset(node->name, 0xef, node->namelen);
+ free(node->name);
+ free(node->actual_name);
+ memset(node, 0xfc, sizeof(*node));
+ free(node);
+ }
+ } else {
+ LOG(ERROR) << std::hex << node << std::dec << " refcount=0";
+ }
+}
+
+static void add_node_to_parent_locked(struct node *node, struct node *parent) {
+ node->parent = parent;
+ node->next = parent->child;
+ parent->child = node;
+ acquire_node_locked(parent);
+}
+
+static void remove_node_from_parent_locked(struct node* node)
+{
+ if (node->parent) {
+ if (node->parent->child == node) {
+ node->parent->child = node->parent->child->next;
+ } else {
+ struct node *node2;
+ node2 = node->parent->child;
+ while (node2->next != node)
+ node2 = node2->next;
+ node2->next = node->next;
+ }
+ release_node_locked(node->parent);
+ node->parent = NULL;
+ node->next = NULL;
+ }
+}
+
+/* Gets the absolute path to a node into the provided buffer.
+ *
+ * Populates 'buf' with the path and returns the length of the path on success,
+ * or returns -1 if the path is too long for the provided buffer.
+ */
+static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) {
+ const char* name;
+ size_t namelen;
+ if (node->graft_path) {
+ name = node->graft_path;
+ namelen = node->graft_pathlen;
+ } else if (node->actual_name) {
+ name = node->actual_name;
+ namelen = node->namelen;
+ } else {
+ name = node->name;
+ namelen = node->namelen;
+ }
+
+ if (bufsize < namelen + 1) {
+ return -1;
+ }
+
+ ssize_t pathlen = 0;
+ if (node->parent && node->graft_path == NULL) {
+ pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 1);
+ if (pathlen < 0) {
+ return -1;
+ }
+ buf[pathlen++] = '/';
+ }
+
+ memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */
+ return pathlen + namelen;
+}
+
+/* Finds the absolute path of a file within a given directory.
+ * Performs a case-insensitive search for the file and sets the buffer to the path
+ * of the first matching file. If 'search' is zero or if no match is found, sets
+ * the buffer to the path that the file would have, assuming the name were case-sensitive.
+ *
+ * Populates 'buf' with the path and returns the actual name (within 'buf') on success,
+ * or returns NULL if the path is too long for the provided buffer.
+ */
+static char* find_file_within(const char* path, const char* name,
+ char* buf, size_t bufsize, int search)
+{
+ size_t pathlen = strlen(path);
+ size_t namelen = strlen(name);
+ size_t childlen = pathlen + namelen + 1;
+ char* actual;
+
+ if (bufsize <= childlen) {
+ return NULL;
+ }
+
+ memcpy(buf, path, pathlen);
+ buf[pathlen] = '/';
+ actual = buf + pathlen + 1;
+ memcpy(actual, name, namelen + 1);
+
+ if (search && access(buf, F_OK)) {
+ struct dirent* entry;
+ DIR* dir = opendir(path);
+ if (!dir) {
+ PLOG(ERROR) << "opendir(" << path << ") failed";
+ return actual;
+ }
+ while ((entry = readdir(dir))) {
+ if (!strcasecmp(entry->d_name, name)) {
+ /* we have a match - replace the name, don't need to copy the null again */
+ memcpy(actual, entry->d_name, namelen);
+ break;
+ }
+ }
+ closedir(dir);
+ }
+ return actual;
+}
+
+static void attr_from_stat(struct fuse* fuse, struct fuse_attr *attr,
+ const struct stat *s, const struct node* node) {
+ attr->ino = node->ino;
+ attr->size = s->st_size;
+ attr->blocks = s->st_blocks;
+ attr->atime = s->st_atim.tv_sec;
+ attr->mtime = s->st_mtim.tv_sec;
+ attr->ctime = s->st_ctim.tv_sec;
+ attr->atimensec = s->st_atim.tv_nsec;
+ attr->mtimensec = s->st_mtim.tv_nsec;
+ attr->ctimensec = s->st_ctim.tv_nsec;
+ attr->mode = s->st_mode;
+ attr->nlink = s->st_nlink;
+
+ attr->uid = node->uid;
+
+ if (fuse->gid == AID_SDCARD_RW) {
+ /* As an optimization, certain trusted system components only run
+ * as owner but operate across all users. Since we're now handing
+ * out the sdcard_rw GID only to trusted apps, we're okay relaxing
+ * the user boundary enforcement for the default view. The UIDs
+ * assigned to app directories are still multiuser aware. */
+ attr->gid = AID_SDCARD_RW;
+ } else {
+ attr->gid = multiuser_get_uid(node->userid, fuse->gid);
+ }
+
+ int visible_mode = 0775 & ~fuse->mask;
+ if (node->perm == PERM_PRE_ROOT) {
+ /* Top of multi-user view should always be visible to ensure
+ * secondary users can traverse inside. */
+ visible_mode = 0711;
+ } else if (node->under_android) {
+ /* Block "other" access to Android directories, since only apps
+ * belonging to a specific user should be in there; we still
+ * leave +x open for the default view. */
+ if (fuse->gid == AID_SDCARD_RW) {
+ visible_mode = visible_mode & ~0006;
+ } else {
+ visible_mode = visible_mode & ~0007;
+ }
+ }
+ int owner_mode = s->st_mode & 0700;
+ int filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
+ attr->mode = (attr->mode & S_IFMT) | filtered_mode;
+}
+
+static int touch(char* path, mode_t mode) {
+ int fd = TEMP_FAILURE_RETRY(open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC,
+ mode));
+ if (fd == -1) {
+ if (errno == EEXIST) {
+ return 0;
+ } else {
+ PLOG(ERROR) << "open(" << path << ") failed";
+ return -1;
+ }
+ }
+ close(fd);
+ return 0;
+}
+
+static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
+ struct node *node) {
+ appid_t appid;
+
+ /* By default, each node inherits from its parent */
+ node->perm = PERM_INHERIT;
+ node->userid = parent->userid;
+ node->uid = parent->uid;
+ node->under_android = parent->under_android;
+
+ /* Derive custom permissions based on parent and current node */
+ switch (parent->perm) {
+ case PERM_INHERIT:
+ /* Already inherited above */
+ break;
+ case PERM_PRE_ROOT:
+ /* Legacy internal layout places users at top level */
+ node->perm = PERM_ROOT;
+ node->userid = strtoul(node->name, NULL, 10);
+ break;
+ case PERM_ROOT:
+ /* Assume masked off by default. */
+ if (!strcasecmp(node->name, "Android")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID;
+ node->under_android = true;
+ }
+ break;
+ case PERM_ANDROID:
+ if (!strcasecmp(node->name, "data")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID_DATA;
+ } else if (!strcasecmp(node->name, "obb")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID_OBB;
+ /* Single OBB directory is always shared */
+ node->graft_path = fuse->global->obb_path;
+ node->graft_pathlen = strlen(fuse->global->obb_path);
+ } else if (!strcasecmp(node->name, "media")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID_MEDIA;
+ }
+ break;
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_OBB:
+ case PERM_ANDROID_MEDIA:
+ const auto& iter = fuse->global->package_to_appid->find(node->name);
+ if (iter != fuse->global->package_to_appid->end()) {
+ appid = iter->second;
+ node->uid = multiuser_get_uid(parent->userid, appid);
+ }
+ break;
+ }
+}
+
+void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent) {
+ struct node *node;
+ for (node = parent->child; node; node = node->next) {
+ derive_permissions_locked(fuse, parent, node);
+ if (node->child) {
+ derive_permissions_recursive_locked(fuse, node);
+ }
+ }
+}
+
+/* Kernel has already enforced everything we returned through
+ * derive_permissions_locked(), so this is used to lock down access
+ * even further, such as enforcing that apps hold sdcard_rw. */
+static bool check_caller_access_to_name(struct fuse* fuse,
+ const struct fuse_in_header *hdr, const struct node* parent_node,
+ const char* name, int mode) {
+ /* Always block security-sensitive files at root */
+ if (parent_node && parent_node->perm == PERM_ROOT) {
+ if (!strcasecmp(name, "autorun.inf")
+ || !strcasecmp(name, ".android_secure")
+ || !strcasecmp(name, "android_secure")) {
+ return false;
+ }
+ }
+
+ /* Root always has access; access for any other UIDs should always
+ * be controlled through packages.list. */
+ if (hdr->uid == 0) {
+ return true;
+ }
+
+ /* No extra permissions to enforce */
+ return true;
+}
+
+static bool check_caller_access_to_node(struct fuse* fuse,
+ const struct fuse_in_header *hdr, const struct node* node, int mode) {
+ return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode);
+}
+
+struct node *create_node_locked(struct fuse* fuse,
+ struct node *parent, const char *name, const char* actual_name)
+{
+ struct node *node;
+ size_t namelen = strlen(name);
+
+ // Detect overflows in the inode counter. "4 billion nodes should be enough
+ // for everybody".
+ if (fuse->global->inode_ctr == 0) {
+ LOG(ERROR) << "No more inode numbers available";
+ return NULL;
+ }
+
+ node = static_cast<struct node*>(calloc(1, sizeof(struct node)));
+ if (!node) {
+ return NULL;
+ }
+ node->name = static_cast<char*>(malloc(namelen + 1));
+ if (!node->name) {
+ free(node);
+ return NULL;
+ }
+ memcpy(node->name, name, namelen + 1);
+ if (strcmp(name, actual_name)) {
+ node->actual_name = static_cast<char*>(malloc(namelen + 1));
+ if (!node->actual_name) {
+ free(node->name);
+ free(node);
+ return NULL;
+ }
+ memcpy(node->actual_name, actual_name, namelen + 1);
+ }
+ node->namelen = namelen;
+ node->nid = ptr_to_id(node);
+ node->ino = fuse->global->inode_ctr++;
+ node->gen = fuse->global->next_generation++;
+
+ node->deleted = false;
+
+ derive_permissions_locked(fuse, parent, node);
+ acquire_node_locked(node);
+ add_node_to_parent_locked(node, parent);
+ return node;
+}
+
+static int rename_node_locked(struct node *node, const char *name,
+ const char* actual_name)
+{
+ size_t namelen = strlen(name);
+ int need_actual_name = strcmp(name, actual_name);
+
+ /* make the storage bigger without actually changing the name
+ * in case an error occurs part way */
+ if (namelen > node->namelen) {
+ char* new_name = static_cast<char*>(realloc(node->name, namelen + 1));
+ if (!new_name) {
+ return -ENOMEM;
+ }
+ node->name = new_name;
+ if (need_actual_name && node->actual_name) {
+ char* new_actual_name = static_cast<char*>(realloc(node->actual_name, namelen + 1));
+ if (!new_actual_name) {
+ return -ENOMEM;
+ }
+ node->actual_name = new_actual_name;
+ }
+ }
+
+ /* update the name, taking care to allocate storage before overwriting the old name */
+ if (need_actual_name) {
+ if (!node->actual_name) {
+ node->actual_name = static_cast<char*>(malloc(namelen + 1));
+ if (!node->actual_name) {
+ return -ENOMEM;
+ }
+ }
+ memcpy(node->actual_name, actual_name, namelen + 1);
+ } else {
+ free(node->actual_name);
+ node->actual_name = NULL;
+ }
+ memcpy(node->name, name, namelen + 1);
+ node->namelen = namelen;
+ return 0;
+}
+
+static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid)
+{
+ if (nid == FUSE_ROOT_ID) {
+ return &fuse->global->root;
+ } else {
+ return static_cast<struct node*>(id_to_ptr(nid));
+ }
+}
+
+static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid,
+ char* buf, size_t bufsize)
+{
+ struct node* node = lookup_node_by_id_locked(fuse, nid);
+ if (node && get_node_path_locked(node, buf, bufsize) < 0) {
+ node = NULL;
+ }
+ return node;
+}
+
+static struct node *lookup_child_by_name_locked(struct node *node, const char *name)
+{
+ for (node = node->child; node; node = node->next) {
+ /* use exact string comparison, nodes that differ by case
+ * must be considered distinct even if they refer to the same
+ * underlying file as otherwise operations such as "mv x x"
+ * will not work because the source and target nodes are the same. */
+ if (!strcmp(name, node->name) && !node->deleted) {
+ return node;
+ }
+ }
+ return 0;
+}
+
+static struct node* acquire_or_create_child_locked(
+ struct fuse* fuse, struct node* parent,
+ const char* name, const char* actual_name)
+{
+ struct node* child = lookup_child_by_name_locked(parent, name);
+ if (child) {
+ acquire_node_locked(child);
+ } else {
+ child = create_node_locked(fuse, parent, name, actual_name);
+ }
+ return child;
+}
+
+static void fuse_status(struct fuse *fuse, __u64 unique, int err)
+{
+ struct fuse_out_header hdr;
+ hdr.len = sizeof(hdr);
+ hdr.error = err;
+ hdr.unique = unique;
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fuse->fd, &hdr, sizeof(hdr)));
+ if (ret == -1) {
+ PLOG(ERROR) << "*** STATUS FAILED ***";
+ } else if (static_cast<size_t>(ret) != sizeof(hdr)) {
+ LOG(ERROR) << "*** STATUS FAILED: written " << ret << " expected "
+ << sizeof(hdr) << " ***";
+ }
+}
+
+static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
+{
+ struct fuse_out_header hdr;
+ hdr.len = len + sizeof(hdr);
+ hdr.error = 0;
+ hdr.unique = unique;
+
+ struct iovec vec[2];
+ vec[0].iov_base = &hdr;
+ vec[0].iov_len = sizeof(hdr);
+ vec[1].iov_base = data;
+ vec[1].iov_len = len;
+
+ ssize_t ret = TEMP_FAILURE_RETRY(writev(fuse->fd, vec, 2));
+ if (ret == -1) {
+ PLOG(ERROR) << "*** REPLY FAILED ***";
+ } else if (static_cast<size_t>(ret) != sizeof(hdr) + len) {
+ LOG(ERROR) << "*** REPLY FAILED: written " << ret << " expected "
+ << sizeof(hdr) + len << " ***";
+ }
+}
+
+static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
+ struct node* parent, const char* name, const char* actual_name,
+ const char* path)
+{
+ struct node* node;
+ struct fuse_entry_out out;
+ struct stat s;
+
+ if (lstat(path, &s) == -1) {
+ return -errno;
+ }
+
+ pthread_mutex_lock(&fuse->global->lock);
+ node = acquire_or_create_child_locked(fuse, parent, name, actual_name);
+ if (!node) {
+ pthread_mutex_unlock(&fuse->global->lock);
+ return -ENOMEM;
+ }
+ memset(&out, 0, sizeof(out));
+ attr_from_stat(fuse, &out.attr, &s, node);
+ out.attr_valid = 10;
+ out.entry_valid = 10;
+ out.nodeid = node->nid;
+ out.generation = node->gen;
+ pthread_mutex_unlock(&fuse->global->lock);
+ fuse_reply(fuse, unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* node,
+ const char* path)
+{
+ struct fuse_attr_out out;
+ struct stat s;
+
+ if (lstat(path, &s) == -1) {
+ return -errno;
+ }
+ memset(&out, 0, sizeof(out));
+ attr_from_stat(fuse, &out.attr, &s, node);
+ out.attr_valid = 10;
+ fuse_reply(fuse, unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static void fuse_notify_delete(struct fuse* fuse, const __u64 parent,
+ const __u64 child, const char* name) {
+ struct fuse_out_header hdr;
+ struct fuse_notify_delete_out data;
+ size_t namelen = strlen(name);
+ hdr.len = sizeof(hdr) + sizeof(data) + namelen + 1;
+ hdr.error = FUSE_NOTIFY_DELETE;
+ hdr.unique = 0;
+
+ data.parent = parent;
+ data.child = child;
+ data.namelen = namelen;
+ data.padding = 0;
+
+ struct iovec vec[3];
+ vec[0].iov_base = &hdr;
+ vec[0].iov_len = sizeof(hdr);
+ vec[1].iov_base = &data;
+ vec[1].iov_len = sizeof(data);
+ vec[2].iov_base = (void*) name;
+ vec[2].iov_len = namelen + 1;
+
+ ssize_t ret = TEMP_FAILURE_RETRY(writev(fuse->fd, vec, 3));
+ /* Ignore ENOENT, since other views may not have seen the entry */
+ if (ret == -1) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "*** NOTIFY FAILED ***";
+ }
+ } else if (static_cast<size_t>(ret) != sizeof(hdr) + sizeof(data) + namelen + 1) {
+ LOG(ERROR) << "*** NOTIFY FAILED: written " << ret << " expected "
+ << sizeof(hdr) + sizeof(data) + namelen + 1 << " ***";
+ }
+}
+
+static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const char* name)
+{
+ struct node* parent_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+ const char* actual_name;
+
+ pthread_mutex_lock(&fuse->global->lock);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ DLOG(INFO) << "[" << handler->token << "] LOOKUP " << name << " @ " << hdr->nodeid
+ << " (" << (parent_node ? parent_node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!parent_node || !(actual_name = find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1))) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK)) {
+ return -EACCES;
+ }
+
+ return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
+}
+
+static int handle_forget(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const struct fuse_forget_in *req)
+{
+ struct node* node;
+
+ pthread_mutex_lock(&fuse->global->lock);
+ node = lookup_node_by_id_locked(fuse, hdr->nodeid);
+ DLOG(INFO) << "[" << handler->token << "] FORGET #" << req->nlookup
+ << " @ " << std::hex << hdr->nodeid
+ << " (" << (node ? node->name : "?") << ")";
+ if (node) {
+ __u64 n = req->nlookup;
+ while (n) {
+ n--;
+ release_node_locked(node);
+ }
+ }
+ pthread_mutex_unlock(&fuse->global->lock);
+ return NO_STATUS; /* no reply */
+}
+
+static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const struct fuse_getattr_in *req)
+{
+ struct node* node;
+ char path[PATH_MAX];
+
+ pthread_mutex_lock(&fuse->global->lock);
+ node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
+ DLOG(INFO) << "[" << handler->token << "] GETATTR flags=" << req->getattr_flags
+ << " fh=" << std::hex << req->fh << " @ " << hdr->nodeid << std::dec
+ << " (" << (node ? node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!node) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
+ return -EACCES;
+ }
+
+ return fuse_reply_attr(fuse, hdr->unique, node, path);
+}
+
+static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
+{
+ struct node* node;
+ char path[PATH_MAX];
+ struct timespec times[2];
+
+ pthread_mutex_lock(&fuse->global->lock);
+ node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
+ DLOG(INFO) << "[" << handler->token << "] SETATTR fh=" << std::hex << req->fh
+ << " valid=" << std::hex << req->valid << " @ " << hdr->nodeid << std::dec
+ << " (" << (node ? node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!node) {
+ return -ENOENT;
+ }
+
+ if (!(req->valid & FATTR_FH) &&
+ !check_caller_access_to_node(fuse, hdr, node, W_OK)) {
+ return -EACCES;
+ }
+
+ /* XXX: incomplete implementation on purpose.
+ * chmod/chown should NEVER be implemented.*/
+
+ if ((req->valid & FATTR_SIZE) && TEMP_FAILURE_RETRY(truncate64(path, req->size)) == -1) {
+ return -errno;
+ }
+
+ /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW
+ * are both set, then set it to the current time. Else, set it to the
+ * time specified in the request. Same goes for mtime. Use utimensat(2)
+ * as it allows ATIME and MTIME to be changed independently, and has
+ * nanosecond resolution which fuse also has.
+ */
+ if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
+ times[0].tv_nsec = UTIME_OMIT;
+ times[1].tv_nsec = UTIME_OMIT;
+ if (req->valid & FATTR_ATIME) {
+ if (req->valid & FATTR_ATIME_NOW) {
+ times[0].tv_nsec = UTIME_NOW;
+ } else {
+ times[0].tv_sec = req->atime;
+ times[0].tv_nsec = req->atimensec;
+ }
+ }
+ if (req->valid & FATTR_MTIME) {
+ if (req->valid & FATTR_MTIME_NOW) {
+ times[1].tv_nsec = UTIME_NOW;
+ } else {
+ times[1].tv_sec = req->mtime;
+ times[1].tv_nsec = req->mtimensec;
+ }
+ }
+ DLOG(INFO) << "[" << handler->token << "] Calling utimensat on " << path
+ << " with atime " << times[0].tv_sec << ", mtime=" << times[1].tv_sec;
+ if (utimensat(-1, path, times, 0) < 0) {
+ return -errno;
+ }
+ }
+ return fuse_reply_attr(fuse, hdr->unique, node, path);
+}
+
+static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
+{
+ struct node* parent_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+ const char* actual_name;
+
+ pthread_mutex_lock(&fuse->global->lock);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ DLOG(INFO) << "[" << handler->token << "] MKNOD " << name << " 0" << std::oct << req->mode
+ << " @ " << std::hex << hdr->nodeid
+ << " (" << (parent_node ? parent_node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!parent_node || !(actual_name = find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1))) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
+ return -EACCES;
+ }
+ __u32 mode = (req->mode & (~0777)) | 0664;
+ if (mknod(child_path, mode, req->rdev) == -1) {
+ return -errno;
+ }
+ return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
+}
+
+static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
+{
+ struct node* parent_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+ const char* actual_name;
+
+ pthread_mutex_lock(&fuse->global->lock);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ DLOG(INFO) << "[" << handler->token << "] MKDIR " << name << " 0" << std::oct << req->mode
+ << " @ " << std::hex << hdr->nodeid
+ << " (" << (parent_node ? parent_node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!parent_node || !(actual_name = find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1))) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
+ return -EACCES;
+ }
+ __u32 mode = (req->mode & (~0777)) | 0775;
+ if (mkdir(child_path, mode) == -1) {
+ return -errno;
+ }
+
+ /* When creating /Android/data and /Android/obb, mark them as .nomedia */
+ if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "data")) {
+ char nomedia[PATH_MAX];
+ snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path);
+ if (touch(nomedia, 0664) != 0) {
+ PLOG(ERROR) << "touch(" << nomedia << ") failed";
+ return -ENOENT;
+ }
+ }
+ if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) {
+ char nomedia[PATH_MAX];
+ snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->global->obb_path);
+ if (touch(nomedia, 0664) != 0) {
+ PLOG(ERROR) << "touch(" << nomedia << ") failed";
+ return -ENOENT;
+ }
+ }
+
+ return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
+}
+
+static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const char* name)
+{
+ struct node* parent_node;
+ struct node* child_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+
+ pthread_mutex_lock(&fuse->global->lock);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ DLOG(INFO) << "[" << handler->token << "] UNLINK " << name << " @ " << std::hex << hdr->nodeid
+ << " (" << (parent_node ? parent_node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!parent_node || !find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1)) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
+ return -EACCES;
+ }
+ if (unlink(child_path) == -1) {
+ return -errno;
+ }
+ pthread_mutex_lock(&fuse->global->lock);
+ child_node = lookup_child_by_name_locked(parent_node, name);
+ if (child_node) {
+ child_node->deleted = true;
+ }
+ pthread_mutex_unlock(&fuse->global->lock);
+ if (parent_node && child_node) {
+ /* Tell all other views that node is gone */
+ DLOG(INFO) << "[" << handler->token << "] fuse_notify_delete"
+ << " parent=" << std::hex << parent_node->nid
+ << ", child=" << std::hex << child_node->nid << std::dec
+ << ", name=" << name;
+ if (fuse != fuse->global->fuse_default) {
+ fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name);
+ }
+ if (fuse != fuse->global->fuse_read) {
+ fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name);
+ }
+ if (fuse != fuse->global->fuse_write) {
+ fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name);
+ }
+ }
+ return 0;
+}
+
+static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const char* name)
+{
+ struct node* child_node;
+ struct node* parent_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+
+ pthread_mutex_lock(&fuse->global->lock);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ DLOG(INFO) << "[" << handler->token << "] UNLINK " << name << " @ " << std::hex << hdr->nodeid
+ << " (" << (parent_node ? parent_node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!parent_node || !find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1)) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
+ return -EACCES;
+ }
+ if (rmdir(child_path) == -1) {
+ return -errno;
+ }
+ pthread_mutex_lock(&fuse->global->lock);
+ child_node = lookup_child_by_name_locked(parent_node, name);
+ if (child_node) {
+ child_node->deleted = true;
+ }
+ pthread_mutex_unlock(&fuse->global->lock);
+ if (parent_node && child_node) {
+ /* Tell all other views that node is gone */
+ DLOG(INFO) << "[" << handler->token << "] fuse_notify_delete"
+ << " parent=" << std::hex << parent_node->nid
+ << ", child=" << std::hex << child_node->nid << std::dec
+ << ", name=" << name;
+ if (fuse != fuse->global->fuse_default) {
+ fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name);
+ }
+ if (fuse != fuse->global->fuse_read) {
+ fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name);
+ }
+ if (fuse != fuse->global->fuse_write) {
+ fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name);
+ }
+ }
+ return 0;
+}
+
+static int handle_rename(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
+ const char* old_name, const char* new_name)
+{
+ struct node* old_parent_node;
+ struct node* new_parent_node;
+ struct node* child_node;
+ char old_parent_path[PATH_MAX];
+ char new_parent_path[PATH_MAX];
+ char old_child_path[PATH_MAX];
+ char new_child_path[PATH_MAX];
+ const char* new_actual_name;
+ int search;
+ int res;
+
+ pthread_mutex_lock(&fuse->global->lock);
+ old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ old_parent_path, sizeof(old_parent_path));
+ new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
+ new_parent_path, sizeof(new_parent_path));
+ DLOG(INFO) << "[" << handler->token << "] RENAME " << old_name << "->" << new_name
+ << " @ " << std::hex << hdr->nodeid
+ << " (" << (old_parent_node ? old_parent_node->name : "?") << ") -> "
+ << std::hex << req->newdir
+ << " (" << (new_parent_node ? new_parent_node->name : "?") << ")";
+ if (!old_parent_node || !new_parent_node) {
+ res = -ENOENT;
+ goto lookup_error;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK)) {
+ res = -EACCES;
+ goto lookup_error;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK)) {
+ res = -EACCES;
+ goto lookup_error;
+ }
+ child_node = lookup_child_by_name_locked(old_parent_node, old_name);
+ if (!child_node || get_node_path_locked(child_node,
+ old_child_path, sizeof(old_child_path)) < 0) {
+ res = -ENOENT;
+ goto lookup_error;
+ }
+ acquire_node_locked(child_node);
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ /* Special case for renaming a file where destination is same path
+ * differing only by case. In this case we don't want to look for a case
+ * insensitive match. This allows commands like "mv foo FOO" to work as expected.
+ */
+ search = old_parent_node != new_parent_node
+ || strcasecmp(old_name, new_name);
+ if (!(new_actual_name = find_file_within(new_parent_path, new_name,
+ new_child_path, sizeof(new_child_path), search))) {
+ res = -ENOENT;
+ goto io_error;
+ }
+
+ DLOG(INFO) << "[" << handler->token << "] RENAME " << old_child_path << "->" << new_child_path;
+ res = rename(old_child_path, new_child_path);
+ if (res == -1) {
+ res = -errno;
+ goto io_error;
+ }
+
+ pthread_mutex_lock(&fuse->global->lock);
+ res = rename_node_locked(child_node, new_name, new_actual_name);
+ if (!res) {
+ remove_node_from_parent_locked(child_node);
+ derive_permissions_locked(fuse, new_parent_node, child_node);
+ derive_permissions_recursive_locked(fuse, child_node);
+ add_node_to_parent_locked(child_node, new_parent_node);
+ }
+ goto done;
+
+io_error:
+ pthread_mutex_lock(&fuse->global->lock);
+done:
+ release_node_locked(child_node);
+lookup_error:
+ pthread_mutex_unlock(&fuse->global->lock);
+ return res;
+}
+
+static int open_flags_to_access_mode(int open_flags) {
+ if ((open_flags & O_ACCMODE) == O_RDONLY) {
+ return R_OK;
+ } else if ((open_flags & O_ACCMODE) == O_WRONLY) {
+ return W_OK;
+ } else {
+ /* Probably O_RDRW, but treat as default to be safe */
+ return R_OK | W_OK;
+ }
+}
+
+static int handle_open(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_open_in* req)
+{
+ struct node* node;
+ char path[PATH_MAX];
+ struct fuse_open_out out;
+ struct handle *h;
+
+ pthread_mutex_lock(&fuse->global->lock);
+ node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
+ DLOG(INFO) << "[" << handler->token << "] OPEN 0" << std::oct << req->flags
+ << " @ " << std::hex << hdr->nodeid << std::dec
+ << " (" << (node ? node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!node) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_node(fuse, hdr, node,
+ open_flags_to_access_mode(req->flags))) {
+ return -EACCES;
+ }
+ h = static_cast<struct handle*>(malloc(sizeof(*h)));
+ if (!h) {
+ return -ENOMEM;
+ }
+ DLOG(INFO) << "[" << handler->token << "] OPEN " << path;
+ h->fd = TEMP_FAILURE_RETRY(open(path, req->flags));
+ if (h->fd == -1) {
+ free(h);
+ return -errno;
+ }
+ out.fh = ptr_to_id(h);
+ out.open_flags = 0;
+
+#ifdef FUSE_SHORTCIRCUIT
+ out.lower_fd = h->fd;
+#else
+ out.padding = 0;
+#endif
+
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_read(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_read_in* req)
+{
+ struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
+ __u64 unique = hdr->unique;
+ __u32 size = req->size;
+ __u64 offset = req->offset;
+ int res;
+ __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGE_SIZE) & ~((uintptr_t)PAGE_SIZE-1));
+
+ /* Don't access any other fields of hdr or req beyond this point, the read buffer
+ * overlaps the request buffer and will clobber data in the request. This
+ * saves us 128KB per request handler thread at the cost of this scary comment. */
+
+ DLOG(INFO) << "[" << handler->token << "] READ " << std::hex << h << std::dec
+ << "(" << h->fd << ") " << size << "@" << offset;
+ if (size > MAX_READ) {
+ return -EINVAL;
+ }
+ res = TEMP_FAILURE_RETRY(pread64(h->fd, read_buffer, size, offset));
+ if (res == -1) {
+ return -errno;
+ }
+ fuse_reply(fuse, unique, read_buffer, res);
+ return NO_STATUS;
+}
+
+static int handle_write(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_write_in* req,
+ const void* buffer)
+{
+ struct fuse_write_out out;
+ struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
+ int res;
+ __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGE_SIZE)));
+
+ if (req->flags & O_DIRECT) {
+ memcpy(aligned_buffer, buffer, req->size);
+ buffer = (const __u8*) aligned_buffer;
+ }
+
+ DLOG(INFO) << "[" << handler->token << "] WRITE " << std::hex << h << std::dec
+ << "(" << h->fd << ") " << req->size << "@" << req->offset;
+ res = TEMP_FAILURE_RETRY(pwrite64(h->fd, buffer, req->size, req->offset));
+ if (res == -1) {
+ return -errno;
+ }
+ out.size = res;
+ out.padding = 0;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr)
+{
+ char path[PATH_MAX];
+ struct statfs stat;
+ struct fuse_statfs_out out;
+ int res;
+
+ pthread_mutex_lock(&fuse->global->lock);
+ DLOG(INFO) << "[" << handler->token << "] STATFS";
+ res = get_node_path_locked(&fuse->global->root, path, sizeof(path));
+ pthread_mutex_unlock(&fuse->global->lock);
+ if (res < 0) {
+ return -ENOENT;
+ }
+ if (TEMP_FAILURE_RETRY(statfs(fuse->global->root.name, &stat)) == -1) {
+ return -errno;
+ }
+ memset(&out, 0, sizeof(out));
+ out.st.blocks = stat.f_blocks;
+ out.st.bfree = stat.f_bfree;
+ out.st.bavail = stat.f_bavail;
+ out.st.files = stat.f_files;
+ out.st.ffree = stat.f_ffree;
+ out.st.bsize = stat.f_bsize;
+ out.st.namelen = stat.f_namelen;
+ out.st.frsize = stat.f_frsize;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_release(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_release_in* req)
+{
+ struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
+
+ DLOG(INFO) << "[" << handler->token << "] RELEASE " << std::hex << h << std::dec
+ << "(" << h->fd << ")";
+ close(h->fd);
+ free(h);
+ return 0;
+}
+
+static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_fsync_in* req)
+{
+ bool is_dir = (hdr->opcode == FUSE_FSYNCDIR);
+ bool is_data_sync = req->fsync_flags & 1;
+
+ int fd = -1;
+ if (is_dir) {
+ struct dirhandle *dh = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
+ fd = dirfd(dh->d);
+ } else {
+ struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
+ fd = h->fd;
+ }
+
+ DLOG(INFO) << "[" << handler->token << "] " << (is_dir ? "FSYNCDIR" : "FSYNC") << " "
+ << std::hex << req->fh << std::dec << "(" << fd << ") is_data_sync=" << is_data_sync;
+ int res = is_data_sync ? fdatasync(fd) : fsync(fd);
+ if (res == -1) {
+ return -errno;
+ }
+ return 0;
+}
+
+static int handle_flush(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr)
+{
+ DLOG(INFO) << "[" << handler->token << "] FLUSH";
+ return 0;
+}
+
+static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_open_in* req)
+{
+ struct node* node;
+ char path[PATH_MAX];
+ struct fuse_open_out out;
+ struct dirhandle *h;
+
+ pthread_mutex_lock(&fuse->global->lock);
+ node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
+ DLOG(INFO) << "[" << handler->token << "] OPENDIR @ " << std::hex << hdr->nodeid
+ << " (" << (node ? node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!node) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
+ return -EACCES;
+ }
+ h = static_cast<struct dirhandle*>(malloc(sizeof(*h)));
+ if (!h) {
+ return -ENOMEM;
+ }
+ DLOG(INFO) << "[" << handler->token << "] OPENDIR " << path;
+ h->d = opendir(path);
+ if (!h->d) {
+ free(h);
+ return -errno;
+ }
+ out.fh = ptr_to_id(h);
+ out.open_flags = 0;
+
+#ifdef FUSE_SHORTCIRCUIT
+ out.lower_fd = -1;
+#else
+ out.padding = 0;
+#endif
+
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_read_in* req)
+{
+ char buffer[8192];
+ struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
+ struct dirent *de;
+ struct dirhandle *h = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
+
+ DLOG(INFO) << "[" << handler->token << "] READDIR " << h;
+ if (req->offset == 0) {
+ /* rewinddir() might have been called above us, so rewind here too */
+ DLOG(INFO) << "[" << handler->token << "] calling rewinddir()";
+ rewinddir(h->d);
+ }
+ de = readdir(h->d);
+ if (!de) {
+ return 0;
+ }
+ fde->ino = FUSE_UNKNOWN_INO;
+ /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
+ fde->off = req->offset + 1;
+ fde->type = de->d_type;
+ fde->namelen = strlen(de->d_name);
+ memcpy(fde->name, de->d_name, fde->namelen + 1);
+ fuse_reply(fuse, hdr->unique, fde,
+ FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
+ return NO_STATUS;
+}
+
+static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_release_in* req)
+{
+ struct dirhandle *h = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
+
+ DLOG(INFO) << "[" << handler->token << "] RELEASEDIR " << h;
+ closedir(h->d);
+ free(h);
+ return 0;
+}
+
+static int handle_init(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_init_in* req)
+{
+ struct fuse_init_out out;
+ size_t fuse_struct_size;
+
+ DLOG(INFO) << "[" << handler->token << "] INIT ver=" << req->major << "." << req->minor
+ << " maxread=" << req->max_readahead << " flags=" << std::hex << req->flags;
+
+ /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
+ * defined (fuse version 7.6). The structure is the same from 7.6 through
+ * 7.22. Beginning with 7.23, the structure increased in size and added
+ * new parameters.
+ */
+ if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
+ LOG(ERROR) << "Fuse kernel version mismatch: Kernel version "
+ << req->major << "." << req->minor
+ << ", Expected at least " << FUSE_KERNEL_VERSION << ".6";
+ return -1;
+ }
+
+ /* We limit ourselves to 15 because we don't handle BATCH_FORGET yet */
+ out.minor = MIN(req->minor, 15);
+ fuse_struct_size = sizeof(out);
+#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
+ /* FUSE_KERNEL_VERSION >= 23. */
+
+ /* Since we return minor version 15, the kernel does not accept the latest
+ * fuse_init_out size. We need to use FUSE_COMPAT_22_INIT_OUT_SIZE always.*/
+ fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
+#endif
+
+ out.major = FUSE_KERNEL_VERSION;
+ out.max_readahead = req->max_readahead;
+ out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
+
+#ifdef FUSE_SHORTCIRCUIT
+ out.flags |= FUSE_SHORTCIRCUIT;
+#endif
+
+ out.max_background = 32;
+ out.congestion_threshold = 32;
+ out.max_write = MAX_WRITE;
+ fuse_reply(fuse, hdr->unique, &out, fuse_struct_size);
+ return NO_STATUS;
+}
+
+static int handle_canonical_path(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr)
+{
+ struct node* node;
+ char path[PATH_MAX];
+ int len;
+
+ pthread_mutex_lock(&fuse->global->lock);
+ node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ path, sizeof(path));
+ DLOG(INFO) << "[" << handler->token << "] CANONICAL_PATH @ " << std::hex << hdr->nodeid
+ << std::dec << " (" << (node ? node->name : "?") << ")";
+ pthread_mutex_unlock(&fuse->global->lock);
+
+ if (!node) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
+ return -EACCES;
+ }
+ len = strlen(path);
+ if (len + 1 > PATH_MAX)
+ len = PATH_MAX - 1;
+ path[PATH_MAX - 1] = 0;
+ fuse_reply(fuse, hdr->unique, path, len + 1);
+ return NO_STATUS;
+}
+
+static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const void *data, size_t data_len)
+{
+ switch (hdr->opcode) {
+ case FUSE_LOOKUP: { /* bytez[] -> entry_out */
+ const char *name = static_cast<const char*>(data);
+ return handle_lookup(fuse, handler, hdr, name);
+ }
+
+ case FUSE_FORGET: {
+ const struct fuse_forget_in *req = static_cast<const struct fuse_forget_in*>(data);
+ return handle_forget(fuse, handler, hdr, req);
+ }
+
+ case FUSE_GETATTR: { /* getattr_in -> attr_out */
+ const struct fuse_getattr_in *req = static_cast<const struct fuse_getattr_in*>(data);
+ return handle_getattr(fuse, handler, hdr, req);
+ }
+
+ case FUSE_SETATTR: { /* setattr_in -> attr_out */
+ const struct fuse_setattr_in *req = static_cast<const struct fuse_setattr_in*>(data);
+ return handle_setattr(fuse, handler, hdr, req);
+ }
+
+// case FUSE_READLINK:
+// case FUSE_SYMLINK:
+ case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
+ const struct fuse_mknod_in *req = static_cast<const struct fuse_mknod_in*>(data);
+ const char *name = ((const char*) data) + sizeof(*req);
+ return handle_mknod(fuse, handler, hdr, req, name);
+ }
+
+ case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
+ const struct fuse_mkdir_in *req = static_cast<const struct fuse_mkdir_in*>(data);
+ const char *name = ((const char*) data) + sizeof(*req);
+ return handle_mkdir(fuse, handler, hdr, req, name);
+ }
+
+ case FUSE_UNLINK: { /* bytez[] -> */
+ const char *name = static_cast<const char*>(data);
+ return handle_unlink(fuse, handler, hdr, name);
+ }
+
+ case FUSE_RMDIR: { /* bytez[] -> */
+ const char *name = static_cast<const char*>(data);
+ return handle_rmdir(fuse, handler, hdr, name);
+ }
+
+ case FUSE_RENAME: { /* rename_in, oldname, newname -> */
+ const struct fuse_rename_in *req = static_cast<const struct fuse_rename_in*>(data);
+ const char *old_name = ((const char*) data) + sizeof(*req);
+ const char *new_name = old_name + strlen(old_name) + 1;
+ return handle_rename(fuse, handler, hdr, req, old_name, new_name);
+ }
+
+// case FUSE_LINK:
+ case FUSE_OPEN: { /* open_in -> open_out */
+ const struct fuse_open_in *req = static_cast<const struct fuse_open_in*>(data);
+ return handle_open(fuse, handler, hdr, req);
+ }
+
+ case FUSE_READ: { /* read_in -> byte[] */
+ const struct fuse_read_in *req = static_cast<const struct fuse_read_in*>(data);
+ return handle_read(fuse, handler, hdr, req);
+ }
+
+ case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
+ const struct fuse_write_in *req = static_cast<const struct fuse_write_in*>(data);
+ const void* buffer = (const __u8*)data + sizeof(*req);
+ return handle_write(fuse, handler, hdr, req, buffer);
+ }
+
+ case FUSE_STATFS: { /* getattr_in -> attr_out */
+ return handle_statfs(fuse, handler, hdr);
+ }
+
+ case FUSE_RELEASE: { /* release_in -> */
+ const struct fuse_release_in *req = static_cast<const struct fuse_release_in*>(data);
+ return handle_release(fuse, handler, hdr, req);
+ }
+
+ case FUSE_FSYNC:
+ case FUSE_FSYNCDIR: {
+ const struct fuse_fsync_in *req = static_cast<const struct fuse_fsync_in*>(data);
+ return handle_fsync(fuse, handler, hdr, req);
+ }
+
+// case FUSE_SETXATTR:
+// case FUSE_GETXATTR:
+// case FUSE_LISTXATTR:
+// case FUSE_REMOVEXATTR:
+ case FUSE_FLUSH: {
+ return handle_flush(fuse, handler, hdr);
+ }
+
+ case FUSE_OPENDIR: { /* open_in -> open_out */
+ const struct fuse_open_in *req = static_cast<const struct fuse_open_in*>(data);
+ return handle_opendir(fuse, handler, hdr, req);
+ }
+
+ case FUSE_READDIR: {
+ const struct fuse_read_in *req = static_cast<const struct fuse_read_in*>(data);
+ return handle_readdir(fuse, handler, hdr, req);
+ }
+
+ case FUSE_RELEASEDIR: { /* release_in -> */
+ const struct fuse_release_in *req = static_cast<const struct fuse_release_in*>(data);
+ return handle_releasedir(fuse, handler, hdr, req);
+ }
+
+ case FUSE_INIT: { /* init_in -> init_out */
+ const struct fuse_init_in *req = static_cast<const struct fuse_init_in*>(data);
+ return handle_init(fuse, handler, hdr, req);
+ }
+
+ case FUSE_CANONICAL_PATH: { /* nodeid -> bytez[] */
+ return handle_canonical_path(fuse, handler, hdr);
+ }
+
+ default: {
+ DLOG(INFO) << "[" << handler->token << "] NOTIMPL op=" << hdr->opcode
+ << "uniq=" << std::hex << hdr->unique << "nid=" << hdr->nodeid << std::dec;
+ return -ENOSYS;
+ }
+ }
+}
+
+void handle_fuse_requests(struct fuse_handler* handler)
+{
+ struct fuse* fuse = handler->fuse;
+ for (;;) {
+ ssize_t len = TEMP_FAILURE_RETRY(read(fuse->fd,
+ handler->request_buffer, sizeof(handler->request_buffer)));
+ if (len == -1) {
+ if (errno == ENODEV) {
+ LOG(ERROR) << "[" << handler->token << "] someone stole our marbles!";
+ exit(2);
+ }
+ PLOG(ERROR) << "[" << handler->token << "] handle_fuse_requests";
+ continue;
+ }
+
+ if (static_cast<size_t>(len) < sizeof(struct fuse_in_header)) {
+ LOG(ERROR) << "[" << handler->token << "] request too short: len=" << len;
+ continue;
+ }
+
+ const struct fuse_in_header* hdr =
+ reinterpret_cast<const struct fuse_in_header*>(handler->request_buffer);
+ if (hdr->len != static_cast<size_t>(len)) {
+ LOG(ERROR) << "[" << handler->token << "] malformed header: len=" << len
+ << ", hdr->len=" << hdr->len;
+ continue;
+ }
+
+ const void *data = handler->request_buffer + sizeof(struct fuse_in_header);
+ size_t data_len = len - sizeof(struct fuse_in_header);
+ __u64 unique = hdr->unique;
+ int res = handle_fuse_request(fuse, handler, hdr, data, data_len);
+
+ /* We do not access the request again after this point because the underlying
+ * buffer storage may have been reused while processing the request. */
+
+ if (res != NO_STATUS) {
+ if (res) {
+ DLOG(INFO) << "[" << handler->token << "] ERROR " << res;
+ }
+ fuse_status(fuse, unique, res);
+ }
+ }
+}
diff --git a/sdcard/fuse.h b/sdcard/fuse.h
new file mode 100644
index 0000000..9ccd21d
--- /dev/null
+++ b/sdcard/fuse.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FUSE_H_
+#define FUSE_H_
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fuse.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+
+#include <android-base/logging.h>
+#include <cutils/fs.h>
+#include <cutils/multiuser.h>
+#include <packagelistparser/packagelistparser.h>
+
+#include <private/android_filesystem_config.h>
+
+#define FUSE_TRACE 0
+
+#if FUSE_TRACE
+static constexpr bool kEnableDLog = true;
+#else // FUSE_TRACE == 0
+static constexpr bool kEnableDLog = false;
+#endif
+
+// Use same strategy as DCHECK().
+#define DLOG(x) \
+ if (kEnableDLog) LOG(x)
+
+/* Maximum number of bytes to write in one request. */
+#define MAX_WRITE (256 * 1024)
+
+/* Maximum number of bytes to read in one request. */
+#define MAX_READ (128 * 1024)
+
+/* Largest possible request.
+ * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
+ * the largest possible data payload. */
+#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
+
+namespace {
+struct CaseInsensitiveCompare {
+ bool operator()(const std::string& lhs, const std::string& rhs) const {
+ return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
+ }
+};
+}
+
+using AppIdMap = std::map<std::string, appid_t, CaseInsensitiveCompare>;
+
+/* Permission mode for a specific node. Controls how file permissions
+ * are derived for children nodes. */
+typedef enum {
+ /* Nothing special; this node should just inherit from its parent. */
+ PERM_INHERIT,
+ /* This node is one level above a normal root; used for legacy layouts
+ * which use the first level to represent user_id. */
+ PERM_PRE_ROOT,
+ /* This node is "/" */
+ PERM_ROOT,
+ /* This node is "/Android" */
+ PERM_ANDROID,
+ /* This node is "/Android/data" */
+ PERM_ANDROID_DATA,
+ /* This node is "/Android/obb" */
+ PERM_ANDROID_OBB,
+ /* This node is "/Android/media" */
+ PERM_ANDROID_MEDIA,
+} perm_t;
+
+struct handle {
+ int fd;
+};
+
+struct dirhandle {
+ DIR *d;
+};
+
+struct node {
+ __u32 refcount;
+ __u64 nid;
+ __u64 gen;
+ /*
+ * The inode number for this FUSE node. Note that this isn't stable across
+ * multiple invocations of the FUSE daemon.
+ */
+ __u32 ino;
+
+ /* State derived based on current position in hierarchy. */
+ perm_t perm;
+ userid_t userid;
+ uid_t uid;
+ bool under_android;
+
+ struct node *next; /* per-dir sibling list */
+ struct node *child; /* first contained file by this dir */
+ struct node *parent; /* containing directory */
+
+ size_t namelen;
+ char *name;
+ /* If non-null, this is the real name of the file in the underlying storage.
+ * This may differ from the field "name" only by case.
+ * strlen(actual_name) will always equal strlen(name), so it is safe to use
+ * namelen for both fields.
+ */
+ char *actual_name;
+
+ /* If non-null, an exact underlying path that should be grafted into this
+ * position. Used to support things like OBB. */
+ char* graft_path;
+ size_t graft_pathlen;
+
+ bool deleted;
+};
+
+/* Global data for all FUSE mounts */
+struct fuse_global {
+ pthread_mutex_t lock;
+
+ uid_t uid;
+ gid_t gid;
+ bool multi_user;
+
+ char source_path[PATH_MAX];
+ char obb_path[PATH_MAX];
+
+ AppIdMap* package_to_appid;
+
+ __u64 next_generation;
+ struct node root;
+
+ /* Used to allocate unique inode numbers for fuse nodes. We use
+ * a simple counter based scheme where inode numbers from deleted
+ * nodes aren't reused. Note that inode allocations are not stable
+ * across multiple invocation of the sdcard daemon, but that shouldn't
+ * be a huge problem in practice.
+ *
+ * Note that we restrict inodes to 32 bit unsigned integers to prevent
+ * truncation on 32 bit processes when unsigned long long stat.st_ino is
+ * assigned to an unsigned long ino_t type in an LP32 process.
+ *
+ * Also note that fuse_attr and fuse_dirent inode values are 64 bits wide
+ * on both LP32 and LP64, but the fuse kernel code doesn't squash 64 bit
+ * inode numbers into 32 bit values on 64 bit kernels (see fuse_squash_ino
+ * in fs/fuse/inode.c).
+ *
+ * Accesses must be guarded by |lock|.
+ */
+ __u32 inode_ctr;
+
+ struct fuse* fuse_default;
+ struct fuse* fuse_read;
+ struct fuse* fuse_write;
+};
+
+/* Single FUSE mount */
+struct fuse {
+ struct fuse_global* global;
+
+ char dest_path[PATH_MAX];
+
+ int fd;
+
+ gid_t gid;
+ mode_t mask;
+};
+
+/* Private data used by a single FUSE handler */
+struct fuse_handler {
+ struct fuse* fuse;
+ int token;
+
+ /* To save memory, we never use the contents of the request buffer and the read
+ * buffer at the same time. This allows us to share the underlying storage. */
+ union {
+ __u8 request_buffer[MAX_REQUEST_SIZE];
+ __u8 read_buffer[MAX_READ + PAGE_SIZE];
+ };
+};
+
+void handle_fuse_requests(struct fuse_handler* handler);
+void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent);
+
+#endif /* FUSE_H_ */
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
deleted file mode 100644
index 5b657bb..0000000
--- a/sdcard/sdcard.c
+++ /dev/null
@@ -1,2023 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "sdcard"
-
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <linux/fuse.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/inotify.h>
-#include <sys/mount.h>
-#include <sys/param.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/time.h>
-#include <sys/uio.h>
-#include <unistd.h>
-
-#include <cutils/fs.h>
-#include <cutils/hashmap.h>
-#include <cutils/log.h>
-#include <cutils/multiuser.h>
-
-#include <private/android_filesystem_config.h>
-
-/* README
- *
- * What is this?
- *
- * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
- * directory permissions (all files are given fixed owner, group, and
- * permissions at creation, owner, group, and permissions are not
- * changeable, symlinks and hardlinks are not createable, etc.
- *
- * See usage() for command line options.
- *
- * It must be run as root, but will drop to requested UID/GID as soon as it
- * mounts a filesystem. It will refuse to run if requested UID/GID are zero.
- *
- * Things I believe to be true:
- *
- * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
- * CREAT) must bump that node's refcount
- * - don't forget that FORGET can forget multiple references (req->nlookup)
- * - if an op that returns a fuse_entry fails writing the reply to the
- * kernel, you must rollback the refcount to reflect the reference the
- * kernel did not actually acquire
- *
- * This daemon can also derive custom filesystem permissions based on directory
- * structure when requested. These custom permissions support several features:
- *
- * - Apps can access their own files in /Android/data/com.example/ without
- * requiring any additional GIDs.
- * - Separate permissions for protecting directories like Pictures and Music.
- * - Multi-user separation on the same physical device.
- *
- * The derived permissions look like this:
- *
- * rwxrwx--x root:sdcard_rw /
- * rwxrwx--- root:sdcard_pics /Pictures
- * rwxrwx--- root:sdcard_av /Music
- *
- * rwxrwx--x root:sdcard_rw /Android
- * rwxrwx--x root:sdcard_rw /Android/data
- * rwxrwx--- u0_a12:sdcard_rw /Android/data/com.example
- * rwxrwx--x root:sdcard_rw /Android/obb/
- * rwxrwx--- u0_a12:sdcard_rw /Android/obb/com.example
- *
- * rwxrwx--- root:sdcard_all /Android/user
- * rwxrwx--x root:sdcard_rw /Android/user/10
- * rwxrwx--- u10_a12:sdcard_rw /Android/user/10/Android/data/com.example
- */
-
-#define FUSE_TRACE 0
-
-#if FUSE_TRACE
-#define TRACE(x...) ALOGD(x)
-#else
-#define TRACE(x...) do {} while (0)
-#endif
-
-#define ERROR(x...) ALOGE(x)
-
-#define FUSE_UNKNOWN_INO 0xffffffff
-
-/* Maximum number of bytes to write in one request. */
-#define MAX_WRITE (256 * 1024)
-
-/* Maximum number of bytes to read in one request. */
-#define MAX_READ (128 * 1024)
-
-/* Largest possible request.
- * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
- * the largest possible data payload. */
-#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
-
-/* Default number of threads. */
-#define DEFAULT_NUM_THREADS 2
-
-/* Pseudo-error constant used to indicate that no fuse status is needed
- * or that a reply has already been written. */
-#define NO_STATUS 1
-
-/* Path to system-provided mapping of package name to appIds */
-static const char* const kPackagesListFile = "/data/system/packages.list";
-
-/* Supplementary groups to execute with */
-static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
-
-/* Permission mode for a specific node. Controls how file permissions
- * are derived for children nodes. */
-typedef enum {
- /* Nothing special; this node should just inherit from its parent. */
- PERM_INHERIT,
- /* This node is one level above a normal root; used for legacy layouts
- * which use the first level to represent user_id. */
- PERM_LEGACY_PRE_ROOT,
- /* This node is "/" */
- PERM_ROOT,
- /* This node is "/Android" */
- PERM_ANDROID,
- /* This node is "/Android/data" */
- PERM_ANDROID_DATA,
- /* This node is "/Android/obb" */
- PERM_ANDROID_OBB,
- /* This node is "/Android/media" */
- PERM_ANDROID_MEDIA,
- /* This node is "/Android/user" */
- PERM_ANDROID_USER,
-} perm_t;
-
-/* Permissions structure to derive */
-typedef enum {
- DERIVE_NONE,
- DERIVE_LEGACY,
- DERIVE_UNIFIED,
-} derive_t;
-
-struct handle {
- int fd;
-};
-
-struct dirhandle {
- DIR *d;
-};
-
-struct node {
- __u32 refcount;
- __u64 nid;
- __u64 gen;
- /*
- * The inode number for this FUSE node. Note that this isn't stable across
- * multiple invocations of the FUSE daemon.
- */
- __u32 ino;
-
- /* State derived based on current position in hierarchy. */
- perm_t perm;
- userid_t userid;
- uid_t uid;
- gid_t gid;
- mode_t mode;
-
- struct node *next; /* per-dir sibling list */
- struct node *child; /* first contained file by this dir */
- struct node *parent; /* containing directory */
-
- size_t namelen;
- char *name;
- /* If non-null, this is the real name of the file in the underlying storage.
- * This may differ from the field "name" only by case.
- * strlen(actual_name) will always equal strlen(name), so it is safe to use
- * namelen for both fields.
- */
- char *actual_name;
-
- /* If non-null, an exact underlying path that should be grafted into this
- * position. Used to support things like OBB. */
- char* graft_path;
- size_t graft_pathlen;
-
- bool deleted;
-};
-
-static int str_hash(void *key) {
- return hashmapHash(key, strlen(key));
-}
-
-/** Test if two string keys are equal ignoring case */
-static bool str_icase_equals(void *keyA, void *keyB) {
- return strcasecmp(keyA, keyB) == 0;
-}
-
-static int int_hash(void *key) {
- return (int) (uintptr_t) key;
-}
-
-static bool int_equals(void *keyA, void *keyB) {
- return keyA == keyB;
-}
-
-/* Global data structure shared by all fuse handlers. */
-struct fuse {
- pthread_mutex_t lock;
-
- __u64 next_generation;
- int fd;
- derive_t derive;
- bool split_perms;
- gid_t write_gid;
- struct node root;
- char obbpath[PATH_MAX];
-
- /* Used to allocate unique inode numbers for fuse nodes. We use
- * a simple counter based scheme where inode numbers from deleted
- * nodes aren't reused. Note that inode allocations are not stable
- * across multiple invocation of the sdcard daemon, but that shouldn't
- * be a huge problem in practice.
- *
- * Note that we restrict inodes to 32 bit unsigned integers to prevent
- * truncation on 32 bit processes when unsigned long long stat.st_ino is
- * assigned to an unsigned long ino_t type in an LP32 process.
- *
- * Also note that fuse_attr and fuse_dirent inode values are 64 bits wide
- * on both LP32 and LP64, but the fuse kernel code doesn't squash 64 bit
- * inode numbers into 32 bit values on 64 bit kernels (see fuse_squash_ino
- * in fs/fuse/inode.c).
- *
- * Accesses must be guarded by |lock|.
- */
- __u32 inode_ctr;
-
- Hashmap* package_to_appid;
- Hashmap* appid_with_rw;
-};
-
-/* Private data used by a single fuse handler. */
-struct fuse_handler {
- struct fuse* fuse;
- int token;
-
- /* To save memory, we never use the contents of the request buffer and the read
- * buffer at the same time. This allows us to share the underlying storage. */
- union {
- __u8 request_buffer[MAX_REQUEST_SIZE];
- __u8 read_buffer[MAX_READ + PAGE_SIZE];
- };
-};
-
-static inline void *id_to_ptr(__u64 nid)
-{
- return (void *) (uintptr_t) nid;
-}
-
-static inline __u64 ptr_to_id(void *ptr)
-{
- return (__u64) (uintptr_t) ptr;
-}
-
-static void acquire_node_locked(struct node* node)
-{
- node->refcount++;
- TRACE("ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
-}
-
-static void remove_node_from_parent_locked(struct node* node);
-
-static void release_node_locked(struct node* node)
-{
- TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
- if (node->refcount > 0) {
- node->refcount--;
- if (!node->refcount) {
- TRACE("DESTROY %p (%s)\n", node, node->name);
- remove_node_from_parent_locked(node);
-
- /* TODO: remove debugging - poison memory */
- memset(node->name, 0xef, node->namelen);
- free(node->name);
- free(node->actual_name);
- memset(node, 0xfc, sizeof(*node));
- free(node);
- }
- } else {
- ERROR("Zero refcnt %p\n", node);
- }
-}
-
-static void add_node_to_parent_locked(struct node *node, struct node *parent) {
- node->parent = parent;
- node->next = parent->child;
- parent->child = node;
- acquire_node_locked(parent);
-}
-
-static void remove_node_from_parent_locked(struct node* node)
-{
- if (node->parent) {
- if (node->parent->child == node) {
- node->parent->child = node->parent->child->next;
- } else {
- struct node *node2;
- node2 = node->parent->child;
- while (node2->next != node)
- node2 = node2->next;
- node2->next = node->next;
- }
- release_node_locked(node->parent);
- node->parent = NULL;
- node->next = NULL;
- }
-}
-
-/* Gets the absolute path to a node into the provided buffer.
- *
- * Populates 'buf' with the path and returns the length of the path on success,
- * or returns -1 if the path is too long for the provided buffer.
- */
-static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) {
- const char* name;
- size_t namelen;
- if (node->graft_path) {
- name = node->graft_path;
- namelen = node->graft_pathlen;
- } else if (node->actual_name) {
- name = node->actual_name;
- namelen = node->namelen;
- } else {
- name = node->name;
- namelen = node->namelen;
- }
-
- if (bufsize < namelen + 1) {
- return -1;
- }
-
- ssize_t pathlen = 0;
- if (node->parent && node->graft_path == NULL) {
- pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2);
- if (pathlen < 0) {
- return -1;
- }
- buf[pathlen++] = '/';
- }
-
- memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */
- return pathlen + namelen;
-}
-
-/* Finds the absolute path of a file within a given directory.
- * Performs a case-insensitive search for the file and sets the buffer to the path
- * of the first matching file. If 'search' is zero or if no match is found, sets
- * the buffer to the path that the file would have, assuming the name were case-sensitive.
- *
- * Populates 'buf' with the path and returns the actual name (within 'buf') on success,
- * or returns NULL if the path is too long for the provided buffer.
- */
-static char* find_file_within(const char* path, const char* name,
- char* buf, size_t bufsize, int search)
-{
- size_t pathlen = strlen(path);
- size_t namelen = strlen(name);
- size_t childlen = pathlen + namelen + 1;
- char* actual;
-
- if (bufsize <= childlen) {
- return NULL;
- }
-
- memcpy(buf, path, pathlen);
- buf[pathlen] = '/';
- actual = buf + pathlen + 1;
- memcpy(actual, name, namelen + 1);
-
- if (search && access(buf, F_OK)) {
- struct dirent* entry;
- DIR* dir = opendir(path);
- if (!dir) {
- ERROR("opendir %s failed: %s\n", path, strerror(errno));
- return actual;
- }
- while ((entry = readdir(dir))) {
- if (!strcasecmp(entry->d_name, name)) {
- /* we have a match - replace the name, don't need to copy the null again */
- memcpy(actual, entry->d_name, namelen);
- break;
- }
- }
- closedir(dir);
- }
- return actual;
-}
-
-static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const struct node* node)
-{
- attr->ino = node->ino;
- attr->size = s->st_size;
- attr->blocks = s->st_blocks;
- attr->atime = s->st_atim.tv_sec;
- attr->mtime = s->st_mtim.tv_sec;
- attr->ctime = s->st_ctim.tv_sec;
- attr->atimensec = s->st_atim.tv_nsec;
- attr->mtimensec = s->st_mtim.tv_nsec;
- attr->ctimensec = s->st_ctim.tv_nsec;
- attr->mode = s->st_mode;
- attr->nlink = s->st_nlink;
-
- attr->uid = node->uid;
- attr->gid = node->gid;
-
- /* Filter requested mode based on underlying file, and
- * pass through file type. */
- int owner_mode = s->st_mode & 0700;
- int filtered_mode = node->mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
- attr->mode = (attr->mode & S_IFMT) | filtered_mode;
-}
-
-static int touch(char* path, mode_t mode) {
- int fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, mode);
- if (fd == -1) {
- if (errno == EEXIST) {
- return 0;
- } else {
- ERROR("Failed to open(%s): %s\n", path, strerror(errno));
- return -1;
- }
- }
- close(fd);
- return 0;
-}
-
-static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
- struct node *node) {
- appid_t appid;
-
- /* By default, each node inherits from its parent */
- node->perm = PERM_INHERIT;
- node->userid = parent->userid;
- node->uid = parent->uid;
- node->gid = parent->gid;
- node->mode = parent->mode;
-
- if (fuse->derive == DERIVE_NONE) {
- return;
- }
-
- /* Derive custom permissions based on parent and current node */
- switch (parent->perm) {
- case PERM_INHERIT:
- /* Already inherited above */
- break;
- case PERM_LEGACY_PRE_ROOT:
- /* Legacy internal layout places users at top level */
- node->perm = PERM_ROOT;
- node->userid = strtoul(node->name, NULL, 10);
- break;
- case PERM_ROOT:
- /* Assume masked off by default. */
- node->mode = 0770;
- if (!strcasecmp(node->name, "Android")) {
- /* App-specific directories inside; let anyone traverse */
- node->perm = PERM_ANDROID;
- node->mode = 0771;
- } else if (fuse->split_perms) {
- if (!strcasecmp(node->name, "DCIM")
- || !strcasecmp(node->name, "Pictures")) {
- node->gid = AID_SDCARD_PICS;
- } else if (!strcasecmp(node->name, "Alarms")
- || !strcasecmp(node->name, "Movies")
- || !strcasecmp(node->name, "Music")
- || !strcasecmp(node->name, "Notifications")
- || !strcasecmp(node->name, "Podcasts")
- || !strcasecmp(node->name, "Ringtones")) {
- node->gid = AID_SDCARD_AV;
- }
- }
- break;
- case PERM_ANDROID:
- if (!strcasecmp(node->name, "data")) {
- /* App-specific directories inside; let anyone traverse */
- node->perm = PERM_ANDROID_DATA;
- node->mode = 0771;
- } else if (!strcasecmp(node->name, "obb")) {
- /* App-specific directories inside; let anyone traverse */
- node->perm = PERM_ANDROID_OBB;
- node->mode = 0771;
- /* Single OBB directory is always shared */
- node->graft_path = fuse->obbpath;
- node->graft_pathlen = strlen(fuse->obbpath);
- } else if (!strcasecmp(node->name, "media")) {
- /* App-specific directories inside; let anyone traverse */
- node->perm = PERM_ANDROID_MEDIA;
- node->mode = 0771;
- } else if (!strcasecmp(node->name, "user")) {
- /* User directories must only be accessible to system, protected
- * by sdcard_all. Zygote will bind mount the appropriate user-
- * specific path. */
- node->perm = PERM_ANDROID_USER;
- node->gid = AID_SDCARD_ALL;
- node->mode = 0770;
- }
- break;
- case PERM_ANDROID_DATA:
- case PERM_ANDROID_OBB:
- case PERM_ANDROID_MEDIA:
- appid = (appid_t) (uintptr_t) hashmapGet(fuse->package_to_appid, node->name);
- if (appid != 0) {
- node->uid = multiuser_get_uid(parent->userid, appid);
- }
- node->mode = 0770;
- break;
- case PERM_ANDROID_USER:
- /* Root of a secondary user */
- node->perm = PERM_ROOT;
- node->userid = strtoul(node->name, NULL, 10);
- node->gid = AID_SDCARD_R;
- node->mode = 0771;
- break;
- }
-}
-
-/* Return if the calling UID holds sdcard_rw. */
-static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) {
- /* No additional permissions enforcement */
- if (fuse->derive == DERIVE_NONE) {
- return true;
- }
-
- appid_t appid = multiuser_get_app_id(hdr->uid);
- return hashmapContainsKey(fuse->appid_with_rw, (void*) (uintptr_t) appid);
-}
-
-/* Kernel has already enforced everything we returned through
- * derive_permissions_locked(), so this is used to lock down access
- * even further, such as enforcing that apps hold sdcard_rw. */
-static bool check_caller_access_to_name(struct fuse* fuse,
- const struct fuse_in_header *hdr, const struct node* parent_node,
- const char* name, int mode, bool has_rw) {
- /* Always block security-sensitive files at root */
- if (parent_node && parent_node->perm == PERM_ROOT) {
- if (!strcasecmp(name, "autorun.inf")
- || !strcasecmp(name, ".android_secure")
- || !strcasecmp(name, "android_secure")) {
- return false;
- }
- }
-
- /* No additional permissions enforcement */
- if (fuse->derive == DERIVE_NONE) {
- return true;
- }
-
- /* Root always has access; access for any other UIDs should always
- * be controlled through packages.list. */
- if (hdr->uid == 0) {
- return true;
- }
-
- /* If asking to write, verify that caller either owns the
- * parent or holds sdcard_rw. */
- if (mode & W_OK) {
- if (parent_node && hdr->uid == parent_node->uid) {
- return true;
- }
-
- return has_rw;
- }
-
- /* No extra permissions to enforce */
- return true;
-}
-
-static bool check_caller_access_to_node(struct fuse* fuse,
- const struct fuse_in_header *hdr, const struct node* node, int mode, bool has_rw) {
- return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode, has_rw);
-}
-
-struct node *create_node_locked(struct fuse* fuse,
- struct node *parent, const char *name, const char* actual_name)
-{
- struct node *node;
- size_t namelen = strlen(name);
-
- // Detect overflows in the inode counter. "4 billion nodes should be enough
- // for everybody".
- if (fuse->inode_ctr == 0) {
- ERROR("No more inode numbers available");
- return NULL;
- }
-
- node = calloc(1, sizeof(struct node));
- if (!node) {
- return NULL;
- }
- node->name = malloc(namelen + 1);
- if (!node->name) {
- free(node);
- return NULL;
- }
- memcpy(node->name, name, namelen + 1);
- if (strcmp(name, actual_name)) {
- node->actual_name = malloc(namelen + 1);
- if (!node->actual_name) {
- free(node->name);
- free(node);
- return NULL;
- }
- memcpy(node->actual_name, actual_name, namelen + 1);
- }
- node->namelen = namelen;
- node->nid = ptr_to_id(node);
- node->ino = fuse->inode_ctr++;
- node->gen = fuse->next_generation++;
-
- node->deleted = false;
-
- derive_permissions_locked(fuse, parent, node);
- acquire_node_locked(node);
- add_node_to_parent_locked(node, parent);
- return node;
-}
-
-static int rename_node_locked(struct node *node, const char *name,
- const char* actual_name)
-{
- size_t namelen = strlen(name);
- int need_actual_name = strcmp(name, actual_name);
-
- /* make the storage bigger without actually changing the name
- * in case an error occurs part way */
- if (namelen > node->namelen) {
- char* new_name = realloc(node->name, namelen + 1);
- if (!new_name) {
- return -ENOMEM;
- }
- node->name = new_name;
- if (need_actual_name && node->actual_name) {
- char* new_actual_name = realloc(node->actual_name, namelen + 1);
- if (!new_actual_name) {
- return -ENOMEM;
- }
- node->actual_name = new_actual_name;
- }
- }
-
- /* update the name, taking care to allocate storage before overwriting the old name */
- if (need_actual_name) {
- if (!node->actual_name) {
- node->actual_name = malloc(namelen + 1);
- if (!node->actual_name) {
- return -ENOMEM;
- }
- }
- memcpy(node->actual_name, actual_name, namelen + 1);
- } else {
- free(node->actual_name);
- node->actual_name = NULL;
- }
- memcpy(node->name, name, namelen + 1);
- node->namelen = namelen;
- return 0;
-}
-
-static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid)
-{
- if (nid == FUSE_ROOT_ID) {
- return &fuse->root;
- } else {
- return id_to_ptr(nid);
- }
-}
-
-static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid,
- char* buf, size_t bufsize)
-{
- struct node* node = lookup_node_by_id_locked(fuse, nid);
- if (node && get_node_path_locked(node, buf, bufsize) < 0) {
- node = NULL;
- }
- return node;
-}
-
-static struct node *lookup_child_by_name_locked(struct node *node, const char *name)
-{
- for (node = node->child; node; node = node->next) {
- /* use exact string comparison, nodes that differ by case
- * must be considered distinct even if they refer to the same
- * underlying file as otherwise operations such as "mv x x"
- * will not work because the source and target nodes are the same. */
- if (!strcmp(name, node->name) && !node->deleted) {
- return node;
- }
- }
- return 0;
-}
-
-static struct node* acquire_or_create_child_locked(
- struct fuse* fuse, struct node* parent,
- const char* name, const char* actual_name)
-{
- struct node* child = lookup_child_by_name_locked(parent, name);
- if (child) {
- acquire_node_locked(child);
- } else {
- child = create_node_locked(fuse, parent, name, actual_name);
- }
- return child;
-}
-
-static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
- gid_t write_gid, derive_t derive, bool split_perms) {
- pthread_mutex_init(&fuse->lock, NULL);
-
- fuse->fd = fd;
- fuse->next_generation = 0;
- fuse->derive = derive;
- fuse->split_perms = split_perms;
- fuse->write_gid = write_gid;
- fuse->inode_ctr = 1;
-
- memset(&fuse->root, 0, sizeof(fuse->root));
- fuse->root.nid = FUSE_ROOT_ID; /* 1 */
- fuse->root.refcount = 2;
- fuse->root.namelen = strlen(source_path);
- fuse->root.name = strdup(source_path);
- fuse->root.userid = 0;
- fuse->root.uid = AID_ROOT;
-
- /* Set up root node for various modes of operation */
- switch (derive) {
- case DERIVE_NONE:
- /* Traditional behavior that treats entire device as being accessible
- * to sdcard_rw, and no permissions are derived. */
- fuse->root.perm = PERM_ROOT;
- fuse->root.mode = 0775;
- fuse->root.gid = AID_SDCARD_RW;
- break;
- case DERIVE_LEGACY:
- /* Legacy behavior used to support internal multiuser layout which
- * places user_id at the top directory level, with the actual roots
- * just below that. Shared OBB path is also at top level. */
- fuse->root.perm = PERM_LEGACY_PRE_ROOT;
- fuse->root.mode = 0771;
- fuse->root.gid = AID_SDCARD_R;
- fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
- fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
- snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
- fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid());
- break;
- case DERIVE_UNIFIED:
- /* Unified multiuser layout which places secondary user_id under
- * /Android/user and shared OBB path under /Android/obb. */
- fuse->root.perm = PERM_ROOT;
- fuse->root.mode = 0771;
- fuse->root.gid = AID_SDCARD_R;
- fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
- fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
- snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path);
- break;
- }
-}
-
-static void fuse_status(struct fuse *fuse, __u64 unique, int err)
-{
- struct fuse_out_header hdr;
- hdr.len = sizeof(hdr);
- hdr.error = err;
- hdr.unique = unique;
- write(fuse->fd, &hdr, sizeof(hdr));
-}
-
-static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
-{
- struct fuse_out_header hdr;
- struct iovec vec[2];
- int res;
-
- hdr.len = len + sizeof(hdr);
- hdr.error = 0;
- hdr.unique = unique;
-
- vec[0].iov_base = &hdr;
- vec[0].iov_len = sizeof(hdr);
- vec[1].iov_base = data;
- vec[1].iov_len = len;
-
- res = writev(fuse->fd, vec, 2);
- if (res < 0) {
- ERROR("*** REPLY FAILED *** %d\n", errno);
- }
-}
-
-static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
- struct node* parent, const char* name, const char* actual_name,
- const char* path)
-{
- struct node* node;
- struct fuse_entry_out out;
- struct stat s;
-
- if (lstat(path, &s) < 0) {
- return -errno;
- }
-
- pthread_mutex_lock(&fuse->lock);
- node = acquire_or_create_child_locked(fuse, parent, name, actual_name);
- if (!node) {
- pthread_mutex_unlock(&fuse->lock);
- return -ENOMEM;
- }
- memset(&out, 0, sizeof(out));
- attr_from_stat(&out.attr, &s, node);
- out.attr_valid = 10;
- out.entry_valid = 10;
- out.nodeid = node->nid;
- out.generation = node->gen;
- pthread_mutex_unlock(&fuse->lock);
- fuse_reply(fuse, unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* node,
- const char* path)
-{
- struct fuse_attr_out out;
- struct stat s;
-
- if (lstat(path, &s) < 0) {
- return -errno;
- }
- memset(&out, 0, sizeof(out));
- attr_from_stat(&out.attr, &s, node);
- out.attr_valid = 10;
- fuse_reply(fuse, unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const char* name)
-{
- struct node* parent_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
- const char* actual_name;
-
- pthread_mutex_lock(&fuse->lock);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- TRACE("[%d] LOOKUP %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid,
- parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
-
- if (!parent_node || !(actual_name = find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1))) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK, false)) {
- return -EACCES;
- }
-
- return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
-}
-
-static int handle_forget(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const struct fuse_forget_in *req)
-{
- struct node* node;
-
- pthread_mutex_lock(&fuse->lock);
- node = lookup_node_by_id_locked(fuse, hdr->nodeid);
- TRACE("[%d] FORGET #%"PRIu64" @ %"PRIx64" (%s)\n", handler->token, req->nlookup,
- hdr->nodeid, node ? node->name : "?");
- if (node) {
- __u64 n = req->nlookup;
- while (n--) {
- release_node_locked(node);
- }
- }
- pthread_mutex_unlock(&fuse->lock);
- return NO_STATUS; /* no reply */
-}
-
-static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const struct fuse_getattr_in *req)
-{
- struct node* node;
- char path[PATH_MAX];
-
- pthread_mutex_lock(&fuse->lock);
- node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
- TRACE("[%d] GETATTR flags=%x fh=%"PRIx64" @ %"PRIx64" (%s)\n", handler->token,
- req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
-
- if (!node) {
- return -ENOENT;
- }
- if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) {
- return -EACCES;
- }
-
- return fuse_reply_attr(fuse, hdr->unique, node, path);
-}
-
-static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
-{
- bool has_rw;
- struct node* node;
- char path[PATH_MAX];
- struct timespec times[2];
-
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
- node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
- TRACE("[%d] SETATTR fh=%"PRIx64" valid=%x @ %"PRIx64" (%s)\n", handler->token,
- req->fh, req->valid, hdr->nodeid, node ? node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
-
- if (!node) {
- return -ENOENT;
- }
-
- if (!(req->valid & FATTR_FH) &&
- !check_caller_access_to_node(fuse, hdr, node, W_OK, has_rw)) {
- return -EACCES;
- }
-
- /* XXX: incomplete implementation on purpose.
- * chmod/chown should NEVER be implemented.*/
-
- if ((req->valid & FATTR_SIZE) && truncate64(path, req->size) < 0) {
- return -errno;
- }
-
- /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW
- * are both set, then set it to the current time. Else, set it to the
- * time specified in the request. Same goes for mtime. Use utimensat(2)
- * as it allows ATIME and MTIME to be changed independently, and has
- * nanosecond resolution which fuse also has.
- */
- if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
- times[0].tv_nsec = UTIME_OMIT;
- times[1].tv_nsec = UTIME_OMIT;
- if (req->valid & FATTR_ATIME) {
- if (req->valid & FATTR_ATIME_NOW) {
- times[0].tv_nsec = UTIME_NOW;
- } else {
- times[0].tv_sec = req->atime;
- times[0].tv_nsec = req->atimensec;
- }
- }
- if (req->valid & FATTR_MTIME) {
- if (req->valid & FATTR_MTIME_NOW) {
- times[1].tv_nsec = UTIME_NOW;
- } else {
- times[1].tv_sec = req->mtime;
- times[1].tv_nsec = req->mtimensec;
- }
- }
- TRACE("[%d] Calling utimensat on %s with atime %ld, mtime=%ld\n",
- handler->token, path, times[0].tv_sec, times[1].tv_sec);
- if (utimensat(-1, path, times, 0) < 0) {
- return -errno;
- }
- }
- return fuse_reply_attr(fuse, hdr->unique, node, path);
-}
-
-static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
-{
- bool has_rw;
- struct node* parent_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
- const char* actual_name;
-
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- TRACE("[%d] MKNOD %s 0%o @ %"PRIx64" (%s)\n", handler->token,
- name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
-
- if (!parent_node || !(actual_name = find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1))) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
- return -EACCES;
- }
- __u32 mode = (req->mode & (~0777)) | 0664;
- if (mknod(child_path, mode, req->rdev) < 0) {
- return -errno;
- }
- return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
-}
-
-static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
-{
- bool has_rw;
- struct node* parent_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
- const char* actual_name;
-
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- TRACE("[%d] MKDIR %s 0%o @ %"PRIx64" (%s)\n", handler->token,
- name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
-
- if (!parent_node || !(actual_name = find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1))) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
- return -EACCES;
- }
- __u32 mode = (req->mode & (~0777)) | 0775;
- if (mkdir(child_path, mode) < 0) {
- return -errno;
- }
-
- /* When creating /Android/data and /Android/obb, mark them as .nomedia */
- if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "data")) {
- char nomedia[PATH_MAX];
- snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path);
- if (touch(nomedia, 0664) != 0) {
- ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
- return -ENOENT;
- }
- }
- if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) {
- char nomedia[PATH_MAX];
- snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obbpath);
- if (touch(nomedia, 0664) != 0) {
- ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
- return -ENOENT;
- }
- }
-
- return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
-}
-
-static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const char* name)
-{
- bool has_rw;
- struct node* parent_node;
- struct node* child_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
-
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- TRACE("[%d] UNLINK %s @ %"PRIx64" (%s)\n", handler->token,
- name, hdr->nodeid, parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
-
- if (!parent_node || !find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1)) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
- return -EACCES;
- }
- if (unlink(child_path) < 0) {
- return -errno;
- }
- pthread_mutex_lock(&fuse->lock);
- child_node = lookup_child_by_name_locked(parent_node, name);
- if (child_node) {
- child_node->deleted = true;
- }
- pthread_mutex_unlock(&fuse->lock);
- return 0;
-}
-
-static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const char* name)
-{
- bool has_rw;
- struct node* child_node;
- struct node* parent_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
-
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- TRACE("[%d] RMDIR %s @ %"PRIx64" (%s)\n", handler->token,
- name, hdr->nodeid, parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
-
- if (!parent_node || !find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1)) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
- return -EACCES;
- }
- if (rmdir(child_path) < 0) {
- return -errno;
- }
- pthread_mutex_lock(&fuse->lock);
- child_node = lookup_child_by_name_locked(parent_node, name);
- if (child_node) {
- child_node->deleted = true;
- }
- pthread_mutex_unlock(&fuse->lock);
- return 0;
-}
-
-static int handle_rename(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
- const char* old_name, const char* new_name)
-{
- bool has_rw;
- struct node* old_parent_node;
- struct node* new_parent_node;
- struct node* child_node;
- char old_parent_path[PATH_MAX];
- char new_parent_path[PATH_MAX];
- char old_child_path[PATH_MAX];
- char new_child_path[PATH_MAX];
- const char* new_actual_name;
- int res;
-
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
- old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- old_parent_path, sizeof(old_parent_path));
- new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
- new_parent_path, sizeof(new_parent_path));
- TRACE("[%d] RENAME %s->%s @ %"PRIx64" (%s) -> %"PRIx64" (%s)\n", handler->token,
- old_name, new_name,
- hdr->nodeid, old_parent_node ? old_parent_node->name : "?",
- req->newdir, new_parent_node ? new_parent_node->name : "?");
- if (!old_parent_node || !new_parent_node) {
- res = -ENOENT;
- goto lookup_error;
- }
- if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK, has_rw)) {
- res = -EACCES;
- goto lookup_error;
- }
- if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK, has_rw)) {
- res = -EACCES;
- goto lookup_error;
- }
- child_node = lookup_child_by_name_locked(old_parent_node, old_name);
- if (!child_node || get_node_path_locked(child_node,
- old_child_path, sizeof(old_child_path)) < 0) {
- res = -ENOENT;
- goto lookup_error;
- }
- acquire_node_locked(child_node);
- pthread_mutex_unlock(&fuse->lock);
-
- /* Special case for renaming a file where destination is same path
- * differing only by case. In this case we don't want to look for a case
- * insensitive match. This allows commands like "mv foo FOO" to work as expected.
- */
- int search = old_parent_node != new_parent_node
- || strcasecmp(old_name, new_name);
- if (!(new_actual_name = find_file_within(new_parent_path, new_name,
- new_child_path, sizeof(new_child_path), search))) {
- res = -ENOENT;
- goto io_error;
- }
-
- TRACE("[%d] RENAME %s->%s\n", handler->token, old_child_path, new_child_path);
- res = rename(old_child_path, new_child_path);
- if (res < 0) {
- res = -errno;
- goto io_error;
- }
-
- pthread_mutex_lock(&fuse->lock);
- res = rename_node_locked(child_node, new_name, new_actual_name);
- if (!res) {
- remove_node_from_parent_locked(child_node);
- add_node_to_parent_locked(child_node, new_parent_node);
- }
- goto done;
-
-io_error:
- pthread_mutex_lock(&fuse->lock);
-done:
- release_node_locked(child_node);
-lookup_error:
- pthread_mutex_unlock(&fuse->lock);
- return res;
-}
-
-static int open_flags_to_access_mode(int open_flags) {
- if ((open_flags & O_ACCMODE) == O_RDONLY) {
- return R_OK;
- } else if ((open_flags & O_ACCMODE) == O_WRONLY) {
- return W_OK;
- } else {
- /* Probably O_RDRW, but treat as default to be safe */
- return R_OK | W_OK;
- }
-}
-
-static int handle_open(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_open_in* req)
-{
- bool has_rw;
- struct node* node;
- char path[PATH_MAX];
- struct fuse_open_out out;
- struct handle *h;
-
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
- node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
- TRACE("[%d] OPEN 0%o @ %"PRIx64" (%s)\n", handler->token,
- req->flags, hdr->nodeid, node ? node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
-
- if (!node) {
- return -ENOENT;
- }
- if (!check_caller_access_to_node(fuse, hdr, node,
- open_flags_to_access_mode(req->flags), has_rw)) {
- return -EACCES;
- }
- h = malloc(sizeof(*h));
- if (!h) {
- return -ENOMEM;
- }
- TRACE("[%d] OPEN %s\n", handler->token, path);
- h->fd = open(path, req->flags);
- if (h->fd < 0) {
- free(h);
- return -errno;
- }
- out.fh = ptr_to_id(h);
- out.open_flags = 0;
- out.padding = 0;
- fuse_reply(fuse, hdr->unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int handle_read(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_read_in* req)
-{
- struct handle *h = id_to_ptr(req->fh);
- __u64 unique = hdr->unique;
- __u32 size = req->size;
- __u64 offset = req->offset;
- int res;
- __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGE_SIZE) & ~((uintptr_t)PAGE_SIZE-1));
-
- /* Don't access any other fields of hdr or req beyond this point, the read buffer
- * overlaps the request buffer and will clobber data in the request. This
- * saves us 128KB per request handler thread at the cost of this scary comment. */
-
- TRACE("[%d] READ %p(%d) %u@%"PRIu64"\n", handler->token,
- h, h->fd, size, (uint64_t) offset);
- if (size > MAX_READ) {
- return -EINVAL;
- }
- res = pread64(h->fd, read_buffer, size, offset);
- if (res < 0) {
- return -errno;
- }
- fuse_reply(fuse, unique, read_buffer, res);
- return NO_STATUS;
-}
-
-static int handle_write(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_write_in* req,
- const void* buffer)
-{
- struct fuse_write_out out;
- struct handle *h = id_to_ptr(req->fh);
- int res;
- __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGE_SIZE)));
-
- if (req->flags & O_DIRECT) {
- memcpy(aligned_buffer, buffer, req->size);
- buffer = (const __u8*) aligned_buffer;
- }
-
- TRACE("[%d] WRITE %p(%d) %u@%"PRIu64"\n", handler->token,
- h, h->fd, req->size, req->offset);
- res = pwrite64(h->fd, buffer, req->size, req->offset);
- if (res < 0) {
- return -errno;
- }
- out.size = res;
- out.padding = 0;
- fuse_reply(fuse, hdr->unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr)
-{
- char path[PATH_MAX];
- struct statfs stat;
- struct fuse_statfs_out out;
- int res;
-
- pthread_mutex_lock(&fuse->lock);
- TRACE("[%d] STATFS\n", handler->token);
- res = get_node_path_locked(&fuse->root, path, sizeof(path));
- pthread_mutex_unlock(&fuse->lock);
- if (res < 0) {
- return -ENOENT;
- }
- if (statfs(fuse->root.name, &stat) < 0) {
- return -errno;
- }
- memset(&out, 0, sizeof(out));
- out.st.blocks = stat.f_blocks;
- out.st.bfree = stat.f_bfree;
- out.st.bavail = stat.f_bavail;
- out.st.files = stat.f_files;
- out.st.ffree = stat.f_ffree;
- out.st.bsize = stat.f_bsize;
- out.st.namelen = stat.f_namelen;
- out.st.frsize = stat.f_frsize;
- fuse_reply(fuse, hdr->unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int handle_release(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_release_in* req)
-{
- struct handle *h = id_to_ptr(req->fh);
-
- TRACE("[%d] RELEASE %p(%d)\n", handler->token, h, h->fd);
- close(h->fd);
- free(h);
- return 0;
-}
-
-static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_fsync_in* req)
-{
- bool is_dir = (hdr->opcode == FUSE_FSYNCDIR);
- bool is_data_sync = req->fsync_flags & 1;
-
- int fd = -1;
- if (is_dir) {
- struct dirhandle *dh = id_to_ptr(req->fh);
- fd = dirfd(dh->d);
- } else {
- struct handle *h = id_to_ptr(req->fh);
- fd = h->fd;
- }
-
- TRACE("[%d] %s %p(%d) is_data_sync=%d\n", handler->token,
- is_dir ? "FSYNCDIR" : "FSYNC",
- id_to_ptr(req->fh), fd, is_data_sync);
- int res = is_data_sync ? fdatasync(fd) : fsync(fd);
- if (res == -1) {
- return -errno;
- }
- return 0;
-}
-
-static int handle_flush(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr)
-{
- TRACE("[%d] FLUSH\n", handler->token);
- return 0;
-}
-
-static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_open_in* req)
-{
- struct node* node;
- char path[PATH_MAX];
- struct fuse_open_out out;
- struct dirhandle *h;
-
- pthread_mutex_lock(&fuse->lock);
- node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
- TRACE("[%d] OPENDIR @ %"PRIx64" (%s)\n", handler->token,
- hdr->nodeid, node ? node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
-
- if (!node) {
- return -ENOENT;
- }
- if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) {
- return -EACCES;
- }
- h = malloc(sizeof(*h));
- if (!h) {
- return -ENOMEM;
- }
- TRACE("[%d] OPENDIR %s\n", handler->token, path);
- h->d = opendir(path);
- if (!h->d) {
- free(h);
- return -errno;
- }
- out.fh = ptr_to_id(h);
- out.open_flags = 0;
- out.padding = 0;
- fuse_reply(fuse, hdr->unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_read_in* req)
-{
- char buffer[8192];
- struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
- struct dirent *de;
- struct dirhandle *h = id_to_ptr(req->fh);
-
- TRACE("[%d] READDIR %p\n", handler->token, h);
- if (req->offset == 0) {
- /* rewinddir() might have been called above us, so rewind here too */
- TRACE("[%d] calling rewinddir()\n", handler->token);
- rewinddir(h->d);
- }
- de = readdir(h->d);
- if (!de) {
- return 0;
- }
- fde->ino = FUSE_UNKNOWN_INO;
- /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
- fde->off = req->offset + 1;
- fde->type = de->d_type;
- fde->namelen = strlen(de->d_name);
- memcpy(fde->name, de->d_name, fde->namelen + 1);
- fuse_reply(fuse, hdr->unique, fde,
- FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
- return NO_STATUS;
-}
-
-static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_release_in* req)
-{
- struct dirhandle *h = id_to_ptr(req->fh);
-
- TRACE("[%d] RELEASEDIR %p\n", handler->token, h);
- closedir(h->d);
- free(h);
- return 0;
-}
-
-static int handle_init(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_init_in* req)
-{
- struct fuse_init_out out;
- size_t fuse_struct_size;
-
- TRACE("[%d] INIT ver=%d.%d maxread=%d flags=%x\n",
- handler->token, req->major, req->minor, req->max_readahead, req->flags);
-
- /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
- * defined (fuse version 7.6). The structure is the same from 7.6 through
- * 7.22. Beginning with 7.23, the structure increased in size and added
- * new parameters.
- */
- if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
- ERROR("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6",
- req->major, req->minor, FUSE_KERNEL_VERSION);
- return -1;
- }
-
- out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION);
- fuse_struct_size = sizeof(out);
-#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
- /* FUSE_KERNEL_VERSION >= 23. */
-
- /* If the kernel only works on minor revs older than or equal to 22,
- * then use the older structure size since this code only uses the 7.22
- * version of the structure. */
- if (req->minor <= 22) {
- fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
- }
-#endif
-
- out.major = FUSE_KERNEL_VERSION;
- out.max_readahead = req->max_readahead;
- out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
- out.max_background = 32;
- out.congestion_threshold = 32;
- out.max_write = MAX_WRITE;
- fuse_reply(fuse, hdr->unique, &out, fuse_struct_size);
- return NO_STATUS;
-}
-
-static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const void *data, size_t data_len)
-{
- switch (hdr->opcode) {
- case FUSE_LOOKUP: { /* bytez[] -> entry_out */
- const char* name = data;
- return handle_lookup(fuse, handler, hdr, name);
- }
-
- case FUSE_FORGET: {
- const struct fuse_forget_in *req = data;
- return handle_forget(fuse, handler, hdr, req);
- }
-
- case FUSE_GETATTR: { /* getattr_in -> attr_out */
- const struct fuse_getattr_in *req = data;
- return handle_getattr(fuse, handler, hdr, req);
- }
-
- case FUSE_SETATTR: { /* setattr_in -> attr_out */
- const struct fuse_setattr_in *req = data;
- return handle_setattr(fuse, handler, hdr, req);
- }
-
-// case FUSE_READLINK:
-// case FUSE_SYMLINK:
- case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
- const struct fuse_mknod_in *req = data;
- const char *name = ((const char*) data) + sizeof(*req);
- return handle_mknod(fuse, handler, hdr, req, name);
- }
-
- case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
- const struct fuse_mkdir_in *req = data;
- const char *name = ((const char*) data) + sizeof(*req);
- return handle_mkdir(fuse, handler, hdr, req, name);
- }
-
- case FUSE_UNLINK: { /* bytez[] -> */
- const char* name = data;
- return handle_unlink(fuse, handler, hdr, name);
- }
-
- case FUSE_RMDIR: { /* bytez[] -> */
- const char* name = data;
- return handle_rmdir(fuse, handler, hdr, name);
- }
-
- case FUSE_RENAME: { /* rename_in, oldname, newname -> */
- const struct fuse_rename_in *req = data;
- const char *old_name = ((const char*) data) + sizeof(*req);
- const char *new_name = old_name + strlen(old_name) + 1;
- return handle_rename(fuse, handler, hdr, req, old_name, new_name);
- }
-
-// case FUSE_LINK:
- case FUSE_OPEN: { /* open_in -> open_out */
- const struct fuse_open_in *req = data;
- return handle_open(fuse, handler, hdr, req);
- }
-
- case FUSE_READ: { /* read_in -> byte[] */
- const struct fuse_read_in *req = data;
- return handle_read(fuse, handler, hdr, req);
- }
-
- case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
- const struct fuse_write_in *req = data;
- const void* buffer = (const __u8*)data + sizeof(*req);
- return handle_write(fuse, handler, hdr, req, buffer);
- }
-
- case FUSE_STATFS: { /* getattr_in -> attr_out */
- return handle_statfs(fuse, handler, hdr);
- }
-
- case FUSE_RELEASE: { /* release_in -> */
- const struct fuse_release_in *req = data;
- return handle_release(fuse, handler, hdr, req);
- }
-
- case FUSE_FSYNC:
- case FUSE_FSYNCDIR: {
- const struct fuse_fsync_in *req = data;
- return handle_fsync(fuse, handler, hdr, req);
- }
-
-// case FUSE_SETXATTR:
-// case FUSE_GETXATTR:
-// case FUSE_LISTXATTR:
-// case FUSE_REMOVEXATTR:
- case FUSE_FLUSH: {
- return handle_flush(fuse, handler, hdr);
- }
-
- case FUSE_OPENDIR: { /* open_in -> open_out */
- const struct fuse_open_in *req = data;
- return handle_opendir(fuse, handler, hdr, req);
- }
-
- case FUSE_READDIR: {
- const struct fuse_read_in *req = data;
- return handle_readdir(fuse, handler, hdr, req);
- }
-
- case FUSE_RELEASEDIR: { /* release_in -> */
- const struct fuse_release_in *req = data;
- return handle_releasedir(fuse, handler, hdr, req);
- }
-
- case FUSE_INIT: { /* init_in -> init_out */
- const struct fuse_init_in *req = data;
- return handle_init(fuse, handler, hdr, req);
- }
-
- default: {
- TRACE("[%d] NOTIMPL op=%d uniq=%"PRIx64" nid=%"PRIx64"\n",
- handler->token, hdr->opcode, hdr->unique, hdr->nodeid);
- return -ENOSYS;
- }
- }
-}
-
-static void handle_fuse_requests(struct fuse_handler* handler)
-{
- struct fuse* fuse = handler->fuse;
- for (;;) {
- ssize_t len = read(fuse->fd,
- handler->request_buffer, sizeof(handler->request_buffer));
- if (len < 0) {
- if (errno != EINTR) {
- ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno);
- }
- continue;
- }
-
- if ((size_t)len < sizeof(struct fuse_in_header)) {
- ERROR("[%d] request too short: len=%zu\n", handler->token, (size_t)len);
- continue;
- }
-
- const struct fuse_in_header *hdr = (void*)handler->request_buffer;
- if (hdr->len != (size_t)len) {
- ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n",
- handler->token, (size_t)len, hdr->len);
- continue;
- }
-
- const void *data = handler->request_buffer + sizeof(struct fuse_in_header);
- size_t data_len = len - sizeof(struct fuse_in_header);
- __u64 unique = hdr->unique;
- int res = handle_fuse_request(fuse, handler, hdr, data, data_len);
-
- /* We do not access the request again after this point because the underlying
- * buffer storage may have been reused while processing the request. */
-
- if (res != NO_STATUS) {
- if (res) {
- TRACE("[%d] ERROR %d\n", handler->token, res);
- }
- fuse_status(fuse, unique, res);
- }
- }
-}
-
-static void* start_handler(void* data)
-{
- struct fuse_handler* handler = data;
- handle_fuse_requests(handler);
- return NULL;
-}
-
-static bool remove_str_to_int(void *key, void *value, void *context) {
- Hashmap* map = context;
- hashmapRemove(map, key);
- free(key);
- return true;
-}
-
-static bool remove_int_to_null(void *key, void *value, void *context) {
- Hashmap* map = context;
- hashmapRemove(map, key);
- return true;
-}
-
-static int read_package_list(struct fuse *fuse) {
- pthread_mutex_lock(&fuse->lock);
-
- hashmapForEach(fuse->package_to_appid, remove_str_to_int, fuse->package_to_appid);
- hashmapForEach(fuse->appid_with_rw, remove_int_to_null, fuse->appid_with_rw);
-
- FILE* file = fopen(kPackagesListFile, "r");
- if (!file) {
- ERROR("failed to open package list: %s\n", strerror(errno));
- pthread_mutex_unlock(&fuse->lock);
- return -1;
- }
-
- char buf[512];
- while (fgets(buf, sizeof(buf), file) != NULL) {
- char package_name[512];
- int appid;
- char gids[512];
-
- if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) {
- char* package_name_dup = strdup(package_name);
- hashmapPut(fuse->package_to_appid, package_name_dup, (void*) (uintptr_t) appid);
-
- char* token = strtok(gids, ",");
- while (token != NULL) {
- if (strtoul(token, NULL, 10) == fuse->write_gid) {
- hashmapPut(fuse->appid_with_rw, (void*) (uintptr_t) appid, (void*) (uintptr_t) 1);
- break;
- }
- token = strtok(NULL, ",");
- }
- }
- }
-
- TRACE("read_package_list: found %zu packages, %zu with write_gid\n",
- hashmapSize(fuse->package_to_appid),
- hashmapSize(fuse->appid_with_rw));
- fclose(file);
- pthread_mutex_unlock(&fuse->lock);
- return 0;
-}
-
-static void watch_package_list(struct fuse* fuse) {
- struct inotify_event *event;
- char event_buf[512];
-
- int nfd = inotify_init();
- if (nfd < 0) {
- ERROR("inotify_init failed: %s\n", strerror(errno));
- return;
- }
-
- bool active = false;
- while (1) {
- if (!active) {
- int res = inotify_add_watch(nfd, kPackagesListFile, IN_DELETE_SELF);
- if (res == -1) {
- if (errno == ENOENT || errno == EACCES) {
- /* Framework may not have created yet, sleep and retry */
- ERROR("missing packages.list; retrying\n");
- sleep(3);
- continue;
- } else {
- ERROR("inotify_add_watch failed: %s\n", strerror(errno));
- return;
- }
- }
-
- /* Watch above will tell us about any future changes, so
- * read the current state. */
- if (read_package_list(fuse) == -1) {
- ERROR("read_package_list failed: %s\n", strerror(errno));
- return;
- }
- active = true;
- }
-
- int event_pos = 0;
- int res = read(nfd, event_buf, sizeof(event_buf));
- if (res < (int) sizeof(*event)) {
- if (errno == EINTR)
- continue;
- ERROR("failed to read inotify event: %s\n", strerror(errno));
- return;
- }
-
- while (res >= (int) sizeof(*event)) {
- int event_size;
- event = (struct inotify_event *) (event_buf + event_pos);
-
- TRACE("inotify event: %08x\n", event->mask);
- if ((event->mask & IN_IGNORED) == IN_IGNORED) {
- /* Previously watched file was deleted, probably due to move
- * that swapped in new data; re-arm the watch and read. */
- active = false;
- }
-
- event_size = sizeof(*event) + event->len;
- res -= event_size;
- event_pos += event_size;
- }
- }
-}
-
-static int ignite_fuse(struct fuse* fuse, int num_threads)
-{
- struct fuse_handler* handlers;
- int i;
-
- handlers = malloc(num_threads * sizeof(struct fuse_handler));
- if (!handlers) {
- ERROR("cannot allocate storage for threads\n");
- return -ENOMEM;
- }
-
- for (i = 0; i < num_threads; i++) {
- handlers[i].fuse = fuse;
- handlers[i].token = i;
- }
-
- /* When deriving permissions, this thread is used to process inotify events,
- * otherwise it becomes one of the FUSE handlers. */
- i = (fuse->derive == DERIVE_NONE) ? 1 : 0;
- for (; i < num_threads; i++) {
- pthread_t thread;
- int res = pthread_create(&thread, NULL, start_handler, &handlers[i]);
- if (res) {
- ERROR("failed to start thread #%d, error=%d\n", i, res);
- goto quit;
- }
- }
-
- if (fuse->derive == DERIVE_NONE) {
- handle_fuse_requests(&handlers[0]);
- } else {
- watch_package_list(fuse);
- }
-
- ERROR("terminated prematurely\n");
-
- /* don't bother killing all of the other threads or freeing anything,
- * should never get here anyhow */
-quit:
- exit(1);
-}
-
-static int usage()
-{
- ERROR("usage: sdcard [OPTIONS] <source_path> <dest_path>\n"
- " -u: specify UID to run as\n"
- " -g: specify GID to run as\n"
- " -w: specify GID required to write (default sdcard_rw, requires -d or -l)\n"
- " -t: specify number of threads to use (default %d)\n"
- " -d: derive file permissions based on path\n"
- " -l: derive file permissions based on legacy internal layout\n"
- " -s: split derived permissions for pics, av\n"
- "\n", DEFAULT_NUM_THREADS);
- return 1;
-}
-
-static int run(const char* source_path, const char* dest_path, uid_t uid,
- gid_t gid, gid_t write_gid, int num_threads, derive_t derive,
- bool split_perms) {
- int fd;
- char opts[256];
- int res;
- struct fuse fuse;
-
- /* cleanup from previous instance, if necessary */
- umount2(dest_path, MNT_DETACH);
-
- fd = open("/dev/fuse", O_RDWR);
- if (fd < 0){
- ERROR("cannot open fuse device: %s\n", strerror(errno));
- return -1;
- }
-
- snprintf(opts, sizeof(opts),
- "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
- fd, uid, gid);
-
- res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC |
- MS_NOATIME, opts);
- if (res < 0) {
- ERROR("cannot mount fuse filesystem: %s\n", strerror(errno));
- goto error;
- }
-
- res = setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups);
- if (res < 0) {
- ERROR("cannot setgroups: %s\n", strerror(errno));
- goto error;
- }
-
- res = setgid(gid);
- if (res < 0) {
- ERROR("cannot setgid: %s\n", strerror(errno));
- goto error;
- }
-
- res = setuid(uid);
- if (res < 0) {
- ERROR("cannot setuid: %s\n", strerror(errno));
- goto error;
- }
-
- fuse_init(&fuse, fd, source_path, write_gid, derive, split_perms);
-
- umask(0);
- res = ignite_fuse(&fuse, num_threads);
-
- /* we do not attempt to umount the file system here because we are no longer
- * running as the root user */
-
-error:
- close(fd);
- return res;
-}
-
-int main(int argc, char **argv)
-{
- int res;
- const char *source_path = NULL;
- const char *dest_path = NULL;
- uid_t uid = 0;
- gid_t gid = 0;
- gid_t write_gid = AID_SDCARD_RW;
- int num_threads = DEFAULT_NUM_THREADS;
- derive_t derive = DERIVE_NONE;
- bool split_perms = false;
- int i;
- struct rlimit rlim;
- int fs_version;
-
- int opt;
- while ((opt = getopt(argc, argv, "u:g:w:t:dls")) != -1) {
- switch (opt) {
- case 'u':
- uid = strtoul(optarg, NULL, 10);
- break;
- case 'g':
- gid = strtoul(optarg, NULL, 10);
- break;
- case 'w':
- write_gid = strtoul(optarg, NULL, 10);
- break;
- case 't':
- num_threads = strtoul(optarg, NULL, 10);
- break;
- case 'd':
- derive = DERIVE_UNIFIED;
- break;
- case 'l':
- derive = DERIVE_LEGACY;
- break;
- case 's':
- split_perms = true;
- break;
- case '?':
- default:
- return usage();
- }
- }
-
- for (i = optind; i < argc; i++) {
- char* arg = argv[i];
- if (!source_path) {
- source_path = arg;
- } else if (!dest_path) {
- dest_path = arg;
- } else if (!uid) {
- uid = strtoul(arg, NULL, 10);
- } else if (!gid) {
- gid = strtoul(arg, NULL, 10);
- } else {
- ERROR("too many arguments\n");
- return usage();
- }
- }
-
- if (!source_path) {
- ERROR("no source path specified\n");
- return usage();
- }
- if (!dest_path) {
- ERROR("no dest path specified\n");
- return usage();
- }
- if (!uid || !gid) {
- ERROR("uid and gid must be nonzero\n");
- return usage();
- }
- if (num_threads < 1) {
- ERROR("number of threads must be at least 1\n");
- return usage();
- }
- if (split_perms && derive == DERIVE_NONE) {
- ERROR("cannot split permissions without deriving\n");
- return usage();
- }
-
- rlim.rlim_cur = 8192;
- rlim.rlim_max = 8192;
- if (setrlimit(RLIMIT_NOFILE, &rlim)) {
- ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno);
- }
-
- while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
- ERROR("installd fs upgrade not yet complete. Waiting...\n");
- sleep(1);
- }
-
- res = run(source_path, dest_path, uid, gid, write_gid, num_threads, derive, split_perms);
- return res < 0 ? 1 : 0;
-}
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
new file mode 100644
index 0000000..df3ce85
--- /dev/null
+++ b/sdcard/sdcard.cpp
@@ -0,0 +1,520 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#define LOG_TAG "sdcard"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fuse.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <cutils/fs.h>
+#include <cutils/multiuser.h>
+#include <cutils/properties.h>
+
+#include <packagelistparser/packagelistparser.h>
+
+#include <libminijail.h>
+#include <scoped_minijail.h>
+
+#include <private/android_filesystem_config.h>
+
+// README
+//
+// What is this?
+//
+// sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
+// directory permissions (all files are given fixed owner, group, and
+// permissions at creation, owner, group, and permissions are not
+// changeable, symlinks and hardlinks are not createable, etc.
+//
+// See usage() for command line options.
+//
+// It must be run as root, but will drop to requested UID/GID as soon as it
+// mounts a filesystem. It will refuse to run if requested UID/GID are zero.
+//
+// Things I believe to be true:
+//
+// - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
+// CREAT) must bump that node's refcount
+// - don't forget that FORGET can forget multiple references (req->nlookup)
+// - if an op that returns a fuse_entry fails writing the reply to the
+// kernel, you must rollback the refcount to reflect the reference the
+// kernel did not actually acquire
+//
+// This daemon can also derive custom filesystem permissions based on directory
+// structure when requested. These custom permissions support several features:
+//
+// - Apps can access their own files in /Android/data/com.example/ without
+// requiring any additional GIDs.
+// - Separate permissions for protecting directories like Pictures and Music.
+// - Multi-user separation on the same physical device.
+
+#include "fuse.h"
+
+#define PROP_SDCARDFS_DEVICE "ro.sys.sdcardfs"
+#define PROP_SDCARDFS_USER "persist.sys.sdcardfs"
+
+/* Supplementary groups to execute with. */
+static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
+
+static bool package_parse_callback(pkg_info *info, void *userdata) {
+ struct fuse_global *global = (struct fuse_global *)userdata;
+ bool res = global->package_to_appid->emplace(info->name, info->uid).second;
+ packagelist_free(info);
+ return res;
+}
+
+static bool read_package_list(struct fuse_global* global) {
+ pthread_mutex_lock(&global->lock);
+
+ global->package_to_appid->clear();
+ bool rc = packagelist_parse(package_parse_callback, global);
+ DLOG(INFO) << "read_package_list: found " << global->package_to_appid->size() << " packages";
+
+ // Regenerate ownership details using newly loaded mapping.
+ derive_permissions_recursive_locked(global->fuse_default, &global->root);
+
+ pthread_mutex_unlock(&global->lock);
+
+ return rc;
+}
+
+static void watch_package_list(struct fuse_global* global) {
+ struct inotify_event *event;
+ char event_buf[512];
+
+ int nfd = inotify_init();
+ if (nfd == -1) {
+ PLOG(ERROR) << "inotify_init failed";
+ return;
+ }
+
+ bool active = false;
+ while (1) {
+ if (!active) {
+ int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
+ if (res == -1) {
+ if (errno == ENOENT || errno == EACCES) {
+ /* Framework may not have created the file yet, sleep and retry. */
+ LOG(ERROR) << "missing \"" << PACKAGES_LIST_FILE << "\"; retrying...";
+ sleep(3);
+ continue;
+ } else {
+ PLOG(ERROR) << "inotify_add_watch failed";
+ return;
+ }
+ }
+
+ /* Watch above will tell us about any future changes, so
+ * read the current state. */
+ if (read_package_list(global) == false) {
+ LOG(ERROR) << "read_package_list failed";
+ return;
+ }
+ active = true;
+ }
+
+ int event_pos = 0;
+ ssize_t res = TEMP_FAILURE_RETRY(read(nfd, event_buf, sizeof(event_buf)));
+ if (res == -1) {
+ PLOG(ERROR) << "failed to read inotify event";
+ return;
+ } else if (static_cast<size_t>(res) < sizeof(*event)) {
+ LOG(ERROR) << "failed to read inotify event: read " << res << " expected "
+ << sizeof(event_buf);
+ return;
+ }
+
+ while (res >= static_cast<ssize_t>(sizeof(*event))) {
+ int event_size;
+ event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
+
+ DLOG(INFO) << "inotify event: " << std::hex << event->mask << std::dec;
+ if ((event->mask & IN_IGNORED) == IN_IGNORED) {
+ /* Previously watched file was deleted, probably due to move
+ * that swapped in new data; re-arm the watch and read. */
+ active = false;
+ }
+
+ event_size = sizeof(*event) + event->len;
+ res -= event_size;
+ event_pos += event_size;
+ }
+ }
+}
+
+static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) {
+ char opts[256];
+
+ fuse->fd = TEMP_FAILURE_RETRY(open("/dev/fuse", O_RDWR | O_CLOEXEC));
+ if (fuse->fd == -1) {
+ PLOG(ERROR) << "failed to open fuse device";
+ return -1;
+ }
+
+ umount2(fuse->dest_path, MNT_DETACH);
+
+ snprintf(opts, sizeof(opts),
+ "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
+ fuse->fd, fuse->global->uid, fuse->global->gid);
+ if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME,
+ opts) == -1) {
+ PLOG(ERROR) << "failed to mount fuse filesystem";
+ return -1;
+ }
+
+ fuse->gid = gid;
+ fuse->mask = mask;
+
+ return 0;
+}
+
+static void drop_privs(uid_t uid, gid_t gid) {
+ ScopedMinijail j(minijail_new());
+ minijail_set_supplementary_gids(j.get(), arraysize(kGroups), kGroups);
+ minijail_change_gid(j.get(), gid);
+ minijail_change_uid(j.get(), uid);
+ /* minijail_enter() will abort if priv-dropping fails. */
+ minijail_enter(j.get());
+}
+
+static void* start_handler(void* data) {
+ struct fuse_handler* handler = static_cast<fuse_handler*>(data);
+ handle_fuse_requests(handler);
+ return NULL;
+}
+
+static void run(const char* source_path, const char* label, uid_t uid,
+ gid_t gid, userid_t userid, bool multi_user, bool full_write) {
+ struct fuse_global global;
+ struct fuse fuse_default;
+ struct fuse fuse_read;
+ struct fuse fuse_write;
+ struct fuse_handler handler_default;
+ struct fuse_handler handler_read;
+ struct fuse_handler handler_write;
+ pthread_t thread_default;
+ pthread_t thread_read;
+ pthread_t thread_write;
+
+ memset(&global, 0, sizeof(global));
+ memset(&fuse_default, 0, sizeof(fuse_default));
+ memset(&fuse_read, 0, sizeof(fuse_read));
+ memset(&fuse_write, 0, sizeof(fuse_write));
+ memset(&handler_default, 0, sizeof(handler_default));
+ memset(&handler_read, 0, sizeof(handler_read));
+ memset(&handler_write, 0, sizeof(handler_write));
+
+ pthread_mutex_init(&global.lock, NULL);
+ global.package_to_appid = new AppIdMap;
+ global.uid = uid;
+ global.gid = gid;
+ global.multi_user = multi_user;
+ global.next_generation = 0;
+ global.inode_ctr = 1;
+
+ memset(&global.root, 0, sizeof(global.root));
+ global.root.nid = FUSE_ROOT_ID; /* 1 */
+ global.root.refcount = 2;
+ global.root.namelen = strlen(source_path);
+ global.root.name = strdup(source_path);
+ global.root.userid = userid;
+ global.root.uid = AID_ROOT;
+ global.root.under_android = false;
+
+ strcpy(global.source_path, source_path);
+
+ if (multi_user) {
+ global.root.perm = PERM_PRE_ROOT;
+ snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path);
+ } else {
+ global.root.perm = PERM_ROOT;
+ snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path);
+ }
+
+ fuse_default.global = &global;
+ fuse_read.global = &global;
+ fuse_write.global = &global;
+
+ global.fuse_default = &fuse_default;
+ global.fuse_read = &fuse_read;
+ global.fuse_write = &fuse_write;
+
+ snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label);
+ snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label);
+ snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label);
+
+ handler_default.fuse = &fuse_default;
+ handler_read.fuse = &fuse_read;
+ handler_write.fuse = &fuse_write;
+
+ handler_default.token = 0;
+ handler_read.token = 1;
+ handler_write.token = 2;
+
+ umask(0);
+
+ if (multi_user) {
+ /* Multi-user storage is fully isolated per user, so "other"
+ * permissions are completely masked off. */
+ if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
+ || fuse_setup(&fuse_read, AID_EVERYBODY, 0027)
+ || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) {
+ PLOG(FATAL) << "failed to fuse_setup";
+ }
+ } else {
+ /* Physical storage is readable by all users on device, but
+ * the Android directories are masked off to a single user
+ * deep inside attr_from_stat(). */
+ if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
+ || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022)
+ || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) {
+ PLOG(FATAL) << "failed to fuse_setup";
+ }
+ }
+
+ // Will abort if priv-dropping fails.
+ drop_privs(uid, gid);
+
+ if (multi_user) {
+ fs_prepare_dir(global.obb_path, 0775, uid, gid);
+ }
+
+ if (pthread_create(&thread_default, NULL, start_handler, &handler_default)
+ || pthread_create(&thread_read, NULL, start_handler, &handler_read)
+ || pthread_create(&thread_write, NULL, start_handler, &handler_write)) {
+ LOG(FATAL) << "failed to pthread_create";
+ }
+
+ watch_package_list(&global);
+ LOG(FATAL) << "terminated prematurely";
+}
+
+static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path, uid_t fsuid,
+ gid_t fsgid, bool multi_user, userid_t userid, gid_t gid, mode_t mask) {
+ std::string opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
+ fsuid, fsgid, multi_user?"multiuser,":"", mask, userid, gid);
+
+ if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
+ MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
+ PLOG(ERROR) << "failed to mount sdcardfs filesystem";
+ return false;
+ }
+
+ return true;
+}
+
+static bool sdcardfs_setup_bind_remount(const std::string& source_path, const std::string& dest_path,
+ gid_t gid, mode_t mask) {
+ std::string opts = android::base::StringPrintf("mask=%d,gid=%d", mask, gid);
+
+ if (mount(source_path.c_str(), dest_path.c_str(), nullptr,
+ MS_BIND, nullptr) != 0) {
+ PLOG(ERROR) << "failed to bind mount sdcardfs filesystem";
+ return false;
+ }
+
+ if (mount(source_path.c_str(), dest_path.c_str(), "none",
+ MS_REMOUNT | MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) != 0) {
+ PLOG(ERROR) << "failed to mount sdcardfs filesystem";
+ if (umount2(dest_path.c_str(), MNT_DETACH))
+ PLOG(WARNING) << "Failed to unmount bind";
+ return false;
+ }
+
+ return true;
+}
+
+static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
+ gid_t gid, userid_t userid, bool multi_user, bool full_write) {
+ std::string dest_path_default = "/mnt/runtime/default/" + label;
+ std::string dest_path_read = "/mnt/runtime/read/" + label;
+ std::string dest_path_write = "/mnt/runtime/write/" + label;
+
+ umask(0);
+ if (multi_user) {
+ // Multi-user storage is fully isolated per user, so "other"
+ // permissions are completely masked off.
+ if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
+ AID_SDCARD_RW, 0006)
+ || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027)
+ || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write,
+ AID_EVERYBODY, full_write ? 0007 : 0027)) {
+ LOG(FATAL) << "failed to sdcardfs_setup";
+ }
+ } else {
+ // Physical storage is readable by all users on device, but
+ // the Android directories are masked off to a single user
+ // deep inside attr_from_stat().
+ if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
+ AID_SDCARD_RW, 0006)
+ || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read,
+ AID_EVERYBODY, full_write ? 0027 : 0022)
+ || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write,
+ AID_EVERYBODY, full_write ? 0007 : 0022)) {
+ LOG(FATAL) << "failed to sdcardfs_setup";
+ }
+ }
+
+ // Will abort if priv-dropping fails.
+ drop_privs(uid, gid);
+
+ if (multi_user) {
+ std::string obb_path = source_path + "/obb";
+ fs_prepare_dir(obb_path.c_str(), 0775, uid, gid);
+ }
+
+ exit(0);
+}
+
+static bool supports_sdcardfs(void) {
+ std::string filesystems;
+ if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) {
+ PLOG(ERROR) << "Could not read /proc/filesystems";
+ return false;
+ }
+ for (const auto& fs : android::base::Split(filesystems, "\n")) {
+ if (fs.find("sdcardfs") != std::string::npos) return true;
+ }
+ return false;
+}
+
+static bool should_use_sdcardfs(void) {
+ char property[PROPERTY_VALUE_MAX];
+
+ // Allow user to have a strong opinion about state
+ property_get(PROP_SDCARDFS_USER, property, "");
+ if (!strcmp(property, "force_on")) {
+ LOG(WARNING) << "User explicitly enabled sdcardfs";
+ return supports_sdcardfs();
+ } else if (!strcmp(property, "force_off")) {
+ LOG(WARNING) << "User explicitly disabled sdcardfs";
+ return false;
+ }
+
+ // Fall back to device opinion about state
+ if (property_get_bool(PROP_SDCARDFS_DEVICE, false)) {
+ LOG(WARNING) << "Device explicitly enabled sdcardfs";
+ return supports_sdcardfs();
+ } else {
+ LOG(WARNING) << "Device explicitly disabled sdcardfs";
+ return false;
+ }
+}
+
+static int usage() {
+ LOG(ERROR) << "usage: sdcard [OPTIONS] <source_path> <label>"
+ << " -u: specify UID to run as"
+ << " -g: specify GID to run as"
+ << " -U: specify user ID that owns device"
+ << " -m: source_path is multi-user"
+ << " -w: runtime write mount has full write access";
+ return 1;
+}
+
+int main(int argc, char **argv) {
+ const char *source_path = NULL;
+ const char *label = NULL;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ userid_t userid = 0;
+ bool multi_user = false;
+ bool full_write = false;
+ int i;
+ struct rlimit rlim;
+ int fs_version;
+
+ int opt;
+ while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) {
+ switch (opt) {
+ case 'u':
+ uid = strtoul(optarg, NULL, 10);
+ break;
+ case 'g':
+ gid = strtoul(optarg, NULL, 10);
+ break;
+ case 'U':
+ userid = strtoul(optarg, NULL, 10);
+ break;
+ case 'm':
+ multi_user = true;
+ break;
+ case 'w':
+ full_write = true;
+ break;
+ case '?':
+ default:
+ return usage();
+ }
+ }
+
+ for (i = optind; i < argc; i++) {
+ char* arg = argv[i];
+ if (!source_path) {
+ source_path = arg;
+ } else if (!label) {
+ label = arg;
+ } else {
+ LOG(ERROR) << "too many arguments";
+ return usage();
+ }
+ }
+
+ if (!source_path) {
+ LOG(ERROR) << "no source path specified";
+ return usage();
+ }
+ if (!label) {
+ LOG(ERROR) << "no label specified";
+ return usage();
+ }
+ if (!uid || !gid) {
+ LOG(ERROR) << "uid and gid must be nonzero";
+ return usage();
+ }
+
+ rlim.rlim_cur = 8192;
+ rlim.rlim_max = 8192;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
+ PLOG(ERROR) << "setting RLIMIT_NOFILE failed";
+ }
+
+ while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
+ LOG(ERROR) << "installd fs upgrade not yet complete; waiting...";
+ sleep(1);
+ }
+
+ if (should_use_sdcardfs()) {
+ run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write);
+ } else {
+ run(source_path, label, uid, gid, userid, multi_user, full_write);
+ }
+ return 1;
+}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 2ae7ed2..d6ead1a 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -3,7 +3,6 @@
common_cflags := \
-Werror -Wno-unused-parameter -Wno-unused-const-variable \
- -I$(LOCAL_PATH)/upstream-netbsd/include/ \
-include bsd-compatibility.h \
@@ -21,53 +20,31 @@
upstream-netbsd/lib/libc/string/swab.c \
upstream-netbsd/lib/libutil/raise_default_signal.c
LOCAL_CFLAGS += $(common_cflags) -Dmain=dd_main -DNO_CONV
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
LOCAL_MODULE := libtoolbox_dd
include $(BUILD_STATIC_LIBRARY)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := upstream-netbsd/usr.bin/du/du.c
-LOCAL_CFLAGS += $(common_cflags) -Dmain=du_main
-LOCAL_MODULE := libtoolbox_du
-include $(BUILD_STATIC_LIBRARY)
-
include $(CLEAR_VARS)
BSD_TOOLS := \
dd \
- du \
OUR_TOOLS := \
- df \
getevent \
- iftop \
- ioctl \
- log \
- ls \
- lsof \
- nandread \
newfs_msdos \
- ps \
- prlimit \
- sendevent \
- start \
- stop \
- top \
- uptime \
ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS)
LOCAL_SRC_FILES := \
- start_stop.cpp \
toolbox.c \
$(patsubst %,%.c,$(OUR_TOOLS)) \
LOCAL_CFLAGS += $(common_cflags)
-LOCAL_CONLYFLAGS += -std=gnu99
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
LOCAL_SHARED_LIBRARIES := \
libcutils \
- libselinux \
LOCAL_WHOLE_STATIC_LIBRARIES := $(patsubst %,libtoolbox_%,$(BSD_TOOLS))
@@ -83,7 +60,7 @@
$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h
TOOLS_H := $(intermediates)/tools.h
-$(TOOLS_H): PRIVATE_TOOLS := $(ALL_TOOLS)
+$(TOOLS_H): PRIVATE_TOOLS := toolbox $(ALL_TOOLS)
$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done
$(TOOLS_H): $(LOCAL_PATH)/Android.mk
$(TOOLS_H):
@@ -91,21 +68,19 @@
$(LOCAL_PATH)/getevent.c: $(intermediates)/input.h-labels.h
+UAPI_INPUT_EVENT_CODES_H := bionic/libc/kernel/uapi/linux/input.h bionic/libc/kernel/uapi/linux/input-event-codes.h
INPUT_H_LABELS_H := $(intermediates)/input.h-labels.h
$(INPUT_H_LABELS_H): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
-$(INPUT_H_LABELS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/generate-input.h-labels.py > $@
-$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py
+# The PRIVATE_CUSTOM_TOOL line uses = to evaluate the output path late.
+# We copy the input path so it can't be accidentally modified later.
+$(INPUT_H_LABELS_H): PRIVATE_UAPI_INPUT_EVENT_CODES_H := $(UAPI_INPUT_EVENT_CODES_H)
+$(INPUT_H_LABELS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/generate-input.h-labels.py $(PRIVATE_UAPI_INPUT_EVENT_CODES_H) > $@
+# The dependency line though gets evaluated now, so the PRIVATE_ copy doesn't exist yet,
+# and the original can't yet have been modified, so this is both sufficient and necessary.
+$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py $(UAPI_INPUT_EVENT_CODES_H)
$(INPUT_H_LABELS_H):
$(transform-generated-source)
-# We only want 'r' on userdebug and eng builds.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := r.c
-LOCAL_CFLAGS += $(common_cflags)
-LOCAL_MODULE := r
-LOCAL_MODULE_TAGS := debug
-include $(BUILD_EXECUTABLE)
-
# We build BSD grep separately, so it can provide egrep and fgrep too.
include $(CLEAR_VARS)
@@ -116,6 +91,7 @@
upstream-netbsd/usr.bin/grep/queue.c \
upstream-netbsd/usr.bin/grep/util.c
LOCAL_CFLAGS += $(common_cflags)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
LOCAL_MODULE := grep
LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
include $(BUILD_EXECUTABLE)
diff --git a/toolbox/bsd-compatibility.h b/toolbox/bsd-compatibility.h
index 434d370..7c3ddd4 100644
--- a/toolbox/bsd-compatibility.h
+++ b/toolbox/bsd-compatibility.h
@@ -43,7 +43,7 @@
#define __type_fit(t, a) (0 == 0)
// TODO: should this be in our <sys/cdefs.h>?
-#define __arraycount(a) (sizeof(a) / sizeof(a[0]))
+#define __arraycount(a) (sizeof(a) / sizeof((a)[0]))
// This at least matches GNU dd(1) behavior.
#define SIGINFO SIGUSR1
diff --git a/toolbox/df.c b/toolbox/df.c
deleted file mode 100644
index 9cd0743..0000000
--- a/toolbox/df.c
+++ /dev/null
@@ -1,85 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/statfs.h>
-
-static int ok = EXIT_SUCCESS;
-
-static void printsize(long long n)
-{
- char unit = 'K';
- long long t;
-
- n *= 10;
-
- if (n > 1024*1024*10) {
- n /= 1024;
- unit = 'M';
- }
-
- if (n > 1024*1024*10) {
- n /= 1024;
- unit = 'G';
- }
-
- t = (n + 512) / 1024;
- printf("%4lld.%1lld%c", t/10, t%10, unit);
-}
-
-static void df(char *s, int always) {
- struct statfs st;
-
- if (statfs(s, &st) < 0) {
- fprintf(stderr, "%s: %s\n", s, strerror(errno));
- ok = EXIT_FAILURE;
- } else {
- if (st.f_blocks == 0 && !always)
- return;
- printf("%-20s ", s);
- printsize((long long)st.f_blocks * (long long)st.f_bsize);
- printf(" ");
- printsize((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize);
- printf(" ");
- printsize((long long)st.f_bfree * (long long)st.f_bsize);
- printf(" %d\n", (int) st.f_bsize);
- }
-}
-
-int df_main(int argc, char *argv[]) {
- printf("Filesystem Size Used Free Blksize\n");
- if (argc == 1) {
- char s[2000];
- FILE *f = fopen("/proc/mounts", "r");
-
- while (fgets(s, 2000, f)) {
- char *c, *e = s;
-
- for (c = s; *c; c++) {
- if (*c == ' ') {
- e = c + 1;
- break;
- }
- }
-
- for (c = e; *c; c++) {
- if (*c == ' ') {
- *c = '\0';
- break;
- }
- }
-
- df(e, 0);
- }
-
- fclose(f);
- } else {
- int i;
-
- for (i = 1; i < argc; i++) {
- df(argv[i], 1);
- }
- }
-
- exit(ok);
-}
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
index ebb9588..c0e9fce 100755
--- a/toolbox/generate-input.h-labels.py
+++ b/toolbox/generate-input.h-labels.py
@@ -16,8 +16,10 @@
#
# pylint: disable=bad-indentation,bad-continuation
+from __future__ import print_function
import os
import re
+import sys
input_prop_list = []
ev_list = []
@@ -36,46 +38,47 @@
r = re.compile(r'#define\s+(\S+)\s+((?:0x)?\d+)')
-with open('bionic/libc/kernel/uapi/linux/input.h', 'r') as f:
- for line in f:
- m = r.match(line)
- if m:
- name = m.group(1)
- if name.startswith("INPUT_PROP_"):
- input_prop_list.append(name)
- elif name.startswith("EV_"):
- ev_list.append(name)
- elif name.startswith("SYN_"):
- syn_list.append(name)
- elif name.startswith("KEY_") or name.startswith("BTN_"):
- key_list.append(name)
- elif name.startswith("REL_"):
- rel_list.append(name)
- elif name.startswith("ABS_"):
- abs_list.append(name)
- elif name.startswith("SW_"):
- sw_list.append(name)
- elif name.startswith("MSC_"):
- msc_list.append(name)
- elif name.startswith("LED_"):
- led_list.append(name)
- elif name.startswith("REP_"):
- rep_list.append(name)
- elif name.startswith("SND_"):
- snd_list.append(name)
- elif name.startswith("MT_TOOL_"):
- mt_tool_list.append(name)
- elif name.startswith("FF_STATUS_"):
- ff_status_list.append(name)
- elif name.startswith("FF_"):
- ff_list.append(name)
+for arg in sys.argv[1:]:
+ with open(arg, 'r') as f:
+ for line in f:
+ m = r.match(line)
+ if m:
+ name = m.group(1)
+ if name.startswith("INPUT_PROP_"):
+ input_prop_list.append(name)
+ elif name.startswith("EV_"):
+ ev_list.append(name)
+ elif name.startswith("SYN_"):
+ syn_list.append(name)
+ elif name.startswith("KEY_") or name.startswith("BTN_"):
+ key_list.append(name)
+ elif name.startswith("REL_"):
+ rel_list.append(name)
+ elif name.startswith("ABS_"):
+ abs_list.append(name)
+ elif name.startswith("SW_"):
+ sw_list.append(name)
+ elif name.startswith("MSC_"):
+ msc_list.append(name)
+ elif name.startswith("LED_"):
+ led_list.append(name)
+ elif name.startswith("REP_"):
+ rep_list.append(name)
+ elif name.startswith("SND_"):
+ snd_list.append(name)
+ elif name.startswith("MT_TOOL_"):
+ mt_tool_list.append(name)
+ elif name.startswith("FF_STATUS_"):
+ ff_status_list.append(name)
+ elif name.startswith("FF_"):
+ ff_list.append(name)
def Dump(struct_name, values):
- print 'static struct label %s[] = {' % (struct_name)
+ print('static struct label %s[] = {' % (struct_name))
for value in values:
- print ' LABEL(%s),' % (value)
- print ' LABEL_END,'
- print '};'
+ print(' LABEL(%s),' % (value))
+ print(' LABEL_END,')
+ print('};')
Dump("input_prop_labels", input_prop_list)
Dump("ev_labels", ev_list)
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index 30053af..e6def6b 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -9,6 +9,7 @@
#include <sys/limits.h>
#include <sys/poll.h>
#include <linux/input.h>
+#include <err.h>
#include <errno.h>
#include <unistd.h>
@@ -110,10 +111,8 @@
break;
bits_size = res + 16;
bits = realloc(bits, bits_size * 2);
- if(bits == NULL) {
- fprintf(stderr, "failed to allocate buffer of size %d\n", (int)bits_size);
- return 1;
- }
+ if(bits == NULL)
+ err(1, "failed to allocate buffer of size %d\n", (int)bits_size);
}
res2 = 0;
switch(i) {
diff --git a/toolbox/iftop.c b/toolbox/iftop.c
deleted file mode 100644
index 800c0f0..0000000
--- a/toolbox/iftop.c
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (c) 2008, 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.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" 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 <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <net/if.h>
-
-#define PROC_NET_DEV "/proc/net/dev"
-
-#define MAX_IF 8 /* max interfaces we can handle */
-
-#ifndef PAGE_SIZE
-# define PAGE_SIZE 4096
-#endif
-
-#define _STR(s) #s
-#define STR(s) _STR(s)
-
-struct if_stats {
- char name[IFNAMSIZ];
-
- unsigned int mtu;
-
- unsigned int rx_bytes;
- unsigned int rx_packets;
- unsigned int rx_errors;
- unsigned int rx_dropped;
-
- unsigned int tx_bytes;
- unsigned int tx_packets;
- unsigned int tx_errors;
- unsigned int tx_dropped;
-};
-
-static int get_mtu(const char *if_name)
-{
- struct ifreq ifr;
- int s, ret;
-
- s = socket(AF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- perror("socket");
- exit(EXIT_FAILURE);
- }
-
- memset(&ifr, 0, sizeof(struct ifreq));
- ifr.ifr_addr.sa_family = AF_INET;
- strcpy(ifr.ifr_name, if_name);
-
- ret = ioctl(s, SIOCGIFMTU, &ifr);
- if (ret < 0) {
- perror("ioctl");
- exit(EXIT_FAILURE);
- }
-
- ret = close(s);
- if (ret < 0) {
- perror("close");
- exit(EXIT_FAILURE);
- }
-
- return ifr.ifr_mtu;
-}
-
-static int get_interfaces(struct if_stats *ifs)
-{
- char buf[PAGE_SIZE];
- char *p;
- int ret, nr, fd;
-
- fd = open(PROC_NET_DEV, O_RDONLY);
- if (fd < 0) {
- perror("open");
- exit(EXIT_FAILURE);
- }
-
- ret = read(fd, buf, sizeof(buf) - 1);
- if (ret < 0) {
- perror("read");
- exit(EXIT_FAILURE);
- } else if (!ret) {
- fprintf(stderr, "reading " PROC_NET_DEV " returned premature EOF\n");
- exit(EXIT_FAILURE);
- }
- buf[ret] = '\0';
-
- /* skip down to the third line */
- p = strchr(buf, '\n');
- if (!p) {
- fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
- exit(EXIT_FAILURE);
- }
- p = strchr(p + 1, '\n');
- if (!p) {
- fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
- exit(EXIT_FAILURE);
- }
- p += 1;
-
- /*
- * Key:
- * if: (Rx) bytes packets errs drop fifo frame compressed multicast \
- * (Tx) bytes packets errs drop fifo colls carrier compressed
- */
- for (nr = 0; nr < MAX_IF; nr++) {
- char *c;
-
- ret = sscanf(p, "%" STR(IFNAMSIZ) "s", ifs->name);
- if (ret != 1) {
- fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
- exit(EXIT_FAILURE);
- }
-
- /*
- * This works around a bug in the proc file where large interface names
- * or Rx byte counts eat the delimiter, breaking sscanf.
- */
- c = strchr(ifs->name, ':');
- if (c)
- *c = '\0';
-
- p = strchr(p, ':') + 1;
-
- ret = sscanf(p, "%u %u %u %u %*u %*u %*u %*u %u %u %u %u %*u %*u "
- "%*u %*u\n", &ifs->rx_bytes, &ifs->rx_packets,
- &ifs->rx_errors, &ifs->rx_dropped, &ifs->tx_bytes,
- &ifs->tx_packets, &ifs->tx_errors, &ifs->tx_dropped);
- if (ret != 8) {
- fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
- exit(EXIT_FAILURE);
- }
-
- ifs->mtu = get_mtu(ifs->name);
-
- p = strchr(p, '\n') + 1;
- if (*p == '\0') {
- nr++;
- break;
- }
-
- ifs++;
- }
-
- ret = close(fd);
- if (ret) {
- perror("close");
- exit(EXIT_FAILURE);
- }
-
- return nr;
-}
-
-static void print_header(void)
-{
- printf(" Rx Tx\n");
- printf("%-8s %-5s %-10s %-8s %-5s %-5s %-10s %-8s %-5s %-5s\n",
- "name", "MTU", "bytes", "packets", "errs", "drpd", "bytes",
- "packets", "errs", "drpd");
-}
-
-static int print_interfaces(struct if_stats *old, struct if_stats *new, int nr)
-{
- int i = 0;
-
- while (nr--) {
- if (old->rx_packets || old->tx_packets) {
- printf("%-8s %-5u %-10u %-8u %-5u %-5u %-10u %-8u %-5u %-5u\n",
- new->name, new->mtu,
- new->rx_bytes - old->rx_bytes,
- new->rx_packets - old->rx_packets,
- new->rx_errors - old->rx_errors,
- new->rx_dropped - old->rx_dropped,
- new->tx_bytes - old->tx_bytes,
- new->tx_packets - old->tx_packets,
- new->tx_errors - old->tx_errors,
- new->tx_dropped - old->tx_dropped);
- i++;
- }
- old++;
- new++;
- }
-
- return i;
-}
-
-static void usage(const char *cmd)
-{
- fprintf(stderr, "usage: %s [ -r repeats] [ -d delay ]\n", cmd);
-}
-
-int iftop_main(int argc, char *argv[])
-{
- struct if_stats ifs[2][MAX_IF];
- int count = 0, header_interval = 22, delay = 1, i;
- unsigned int toggle = 0;
-
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-d")) {
- if (i >= argc - 1) {
- fprintf(stderr, "Option -d requires an argument.\n");
- exit(EXIT_FAILURE);
- }
- delay = atoi(argv[i++]);
- if (!delay)
- delay = 1;
- continue;
- }
- if (!strcmp(argv[i], "-r")) {
- if (i >= argc - 1) {
- fprintf(stderr, "Option -r requires an argument.\n");
- exit(EXIT_FAILURE);
- }
- header_interval = atoi(argv[i++]);
- if (header_interval < MAX_IF)
- header_interval = MAX_IF;
- continue;
- }
- if (!strcmp(argv[i], "-h")) {
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- }
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- get_interfaces(ifs[!toggle]);
- if (header_interval)
- print_header();
- while (1) {
- int nr;
-
- sleep(delay);
- nr = get_interfaces(ifs[toggle]);
- if (header_interval && count + nr > header_interval) {
- print_header();
- count = 0;
- }
- count += print_interfaces(ifs[!toggle], ifs[toggle], nr);
- toggle = !toggle;
- }
-
- return 0;
-}
diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c
deleted file mode 100644
index 093e467..0000000
--- a/toolbox/ioctl.c
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2008, 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.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" 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 <errno.h>
-#include <error.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-
-static void usage() {
- fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n"
- " -l <length> Length of io buffer\n"
- " -a <argsize> Size of each argument (1-8)\n"
- " -r Open device in read only mode\n"
- " -d Direct argument (no iobuffer)\n"
- " -h Print help\n", getprogname());
- exit(1);
-}
-
-static int xstrtoi(const char* s, const char* what) {
- char* endp;
- errno = 0;
- long result = strtol(s, &endp, 0);
- if (errno != 0 || *endp != '\0') {
- error(1, errno, "couldn't parse %s '%s'", what, s);
- }
- if (result > INT_MAX || result < INT_MIN) {
- error(1, errno, "%s '%s' out of range", what, s);
- }
- return result;
-}
-
-int ioctl_main(int argc, char* argv[]) {
- int read_only = 0;
- int length = -1;
- int arg_size = 4;
- int direct_arg = 0;
-
- void *ioctl_args = NULL;
- uint8_t *ioctl_argp;
- uint8_t *ioctl_argp_save = NULL;
- int rem;
-
- int c;
- while ((c = getopt(argc, argv, "rdl:a:h")) != -1) {
- switch (c) {
- case 'r':
- read_only = 1;
- break;
- case 'd':
- direct_arg = 1;
- break;
- case 'l':
- length = xstrtoi(optarg, "length");
- break;
- case 'a':
- arg_size = xstrtoi(optarg, "argument size");
- break;
- case 'h':
- usage();
- break;
- default:
- error(1, 0, "invalid option -%c", optopt);
- }
- }
-
- if (optind + 2 > argc) {
- usage();
- }
-
- const char* device = argv[optind];
- int fd;
- if (strcmp(device, "-") == 0) {
- fd = STDIN_FILENO;
- } else {
- fd = open(device, read_only ? O_RDONLY : (O_RDWR | O_SYNC));
- if (fd == -1) {
- error(1, errno, "cannot open %s", argv[optind]);
- }
- }
- optind++;
-
- // IOCTL(2) wants second parameter as a signed int.
- // Let's let the user specify either negative numbers or large positive
- // numbers, for the case where ioctl number is larger than INT_MAX.
- errno = 0;
- char* endp;
- int ioctl_nr = UINT_MAX & strtoll(argv[optind], &endp, 0);
- if (errno != 0 || *endp != '\0') {
- error(1, errno, "couldn't parse ioctl number '%s'", argv[optind]);
- }
- optind++;
-
- if(direct_arg) {
- arg_size = 4;
- length = 4;
- }
-
- if(length < 0) {
- length = (argc - optind) * arg_size;
- }
- if(length) {
- ioctl_args = calloc(1, length);
-
- ioctl_argp_save = ioctl_argp = ioctl_args;
- rem = length;
- while (optind < argc) {
- uint64_t tmp = strtoull(argv[optind], NULL, 0);
- if (rem < arg_size) {
- error(1, 0, "too many arguments");
- }
- memcpy(ioctl_argp, &tmp, arg_size);
- ioctl_argp += arg_size;
- rem -= arg_size;
- optind++;
- }
- }
- printf("sending ioctl 0x%x", ioctl_nr);
- rem = length;
- while(rem--) {
- printf(" 0x%02x", *ioctl_argp_save++);
- }
- printf(" to %s\n", device);
-
- int res;
- if(direct_arg)
- res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args);
- else if(length)
- res = ioctl(fd, ioctl_nr, ioctl_args);
- else
- res = ioctl(fd, ioctl_nr, 0);
- if (res < 0) {
- free(ioctl_args);
- error(1, errno, "ioctl 0x%x failed (returned %d)", ioctl_nr, res);
- }
-
- if (length) {
- printf("return buf:");
- ioctl_argp = ioctl_args;
- rem = length;
- while(rem--) {
- printf(" %02x", *ioctl_argp++);
- }
- printf("\n");
- }
- free(ioctl_args);
- close(fd);
- return 0;
-}
diff --git a/toolbox/log.c b/toolbox/log.c
deleted file mode 100644
index 2f020a8..0000000
--- a/toolbox/log.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (c) 2008, 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.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" 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 <stdio.h>
-#include <log/logd.h>
-#include <ctype.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <cutils/sockets.h>
-#include <unistd.h>
-
-/*
- * Note: also accepts 0-9 priorities
- * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
- */
-static android_LogPriority filterCharToPri (char c)
-{
- android_LogPriority pri;
-
- c = tolower(c);
-
- if (c >= '0' && c <= '9') {
- if (c >= ('0'+ANDROID_LOG_SILENT)) {
- pri = ANDROID_LOG_VERBOSE;
- } else {
- pri = (android_LogPriority)(c - '0');
- }
- } else if (c == 'v') {
- pri = ANDROID_LOG_VERBOSE;
- } else if (c == 'd') {
- pri = ANDROID_LOG_DEBUG;
- } else if (c == 'i') {
- pri = ANDROID_LOG_INFO;
- } else if (c == 'w') {
- pri = ANDROID_LOG_WARN;
- } else if (c == 'e') {
- pri = ANDROID_LOG_ERROR;
- } else if (c == 'f') {
- pri = ANDROID_LOG_FATAL;
- } else if (c == 's') {
- pri = ANDROID_LOG_SILENT;
- } else if (c == '*') {
- pri = ANDROID_LOG_DEFAULT;
- } else {
- pri = ANDROID_LOG_UNKNOWN;
- }
-
- return pri;
-}
-
-static int usage(const char *s)
-{
- fprintf(stderr, "USAGE: %s [-p priorityChar] [-t tag] message\n", s);
-
- fprintf(stderr, "\tpriorityChar should be one of:\n"
- "\t\tv,d,i,w,e\n");
- exit(-1);
-}
-
-
-int log_main(int argc, char *argv[])
-{
- android_LogPriority priority;
- const char *tag = "log";
- char buffer[4096];
- int i;
-
- priority = ANDROID_LOG_INFO;
-
- for (;;) {
- int ret;
-
- ret = getopt(argc, argv, "t:p:h");
-
- if (ret < 0) {
- break;
- }
-
- switch(ret) {
- case 't':
- tag = optarg;
- break;
-
- case 'p':
- priority = filterCharToPri(optarg[0]);
- if (priority == ANDROID_LOG_UNKNOWN) {
- usage(argv[0]);
- }
- break;
-
- case 'h':
- usage(argv[0]);
- break;
- }
- }
-
- if (optind == argc) {
- usage(argv[0]);
- }
-
- buffer[0] = '\0';
-
- for (i = optind ; i < argc ; i++) {
- strlcat(buffer, argv[i], sizeof(buffer)-1);
- strlcat(buffer, " ", sizeof(buffer)-1);
- }
-
- if(buffer[0] == 0) {
- usage(argv[0]);
- }
-
- __android_log_print(priority, tag, "%s", buffer);
-
- return 0;
-}
-
diff --git a/toolbox/ls.c b/toolbox/ls.c
deleted file mode 100644
index 9a89dd4..0000000
--- a/toolbox/ls.c
+++ /dev/null
@@ -1,588 +0,0 @@
-#include <dirent.h>
-#include <errno.h>
-#include <grp.h>
-#include <limits.h>
-#include <pwd.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <selinux/selinux.h>
-
-// simple dynamic array of strings.
-typedef struct {
- int count;
- int capacity;
- void** items;
-} strlist_t;
-
-#define STRLIST_INITIALIZER { 0, 0, NULL }
-
-/* Used to iterate over a strlist_t
- * _list :: pointer to strlist_t object
- * _item :: name of local variable name defined within the loop with
- * type 'char*'
- * _stmnt :: C statement executed in each iteration
- *
- * This macro is only intended for simple uses. Do not add or remove items
- * to/from the list during iteration.
- */
-#define STRLIST_FOREACH(_list,_item,_stmnt) \
- do { \
- int _nn_##__LINE__ = 0; \
- for (;_nn_##__LINE__ < (_list)->count; ++ _nn_##__LINE__) { \
- char* _item = (char*)(_list)->items[_nn_##__LINE__]; \
- _stmnt; \
- } \
- } while (0)
-
-static void dynarray_reserve_more( strlist_t *a, int count ) {
- int old_cap = a->capacity;
- int new_cap = old_cap;
- const int max_cap = INT_MAX/sizeof(void*);
- void** new_items;
- int new_count = a->count + count;
-
- if (count <= 0)
- return;
-
- if (count > max_cap - a->count)
- abort();
-
- new_count = a->count + count;
-
- while (new_cap < new_count) {
- old_cap = new_cap;
- new_cap += (new_cap >> 2) + 4;
- if (new_cap < old_cap || new_cap > max_cap) {
- new_cap = max_cap;
- }
- }
- new_items = realloc(a->items, new_cap*sizeof(void*));
- if (new_items == NULL)
- abort();
-
- a->items = new_items;
- a->capacity = new_cap;
-}
-
-void strlist_init( strlist_t *list ) {
- list->count = list->capacity = 0;
- list->items = NULL;
-}
-
-// append a new string made of the first 'slen' characters from 'str'
-// followed by a trailing zero.
-void strlist_append_b( strlist_t *list, const void* str, size_t slen ) {
- char *copy = malloc(slen+1);
- memcpy(copy, str, slen);
- copy[slen] = '\0';
- if (list->count >= list->capacity)
- dynarray_reserve_more(list, 1);
- list->items[list->count++] = copy;
-}
-
-// append the copy of a given input string to a strlist_t.
-void strlist_append_dup( strlist_t *list, const char *str) {
- strlist_append_b(list, str, strlen(str));
-}
-
-// note: strlist_done will free all the strings owned by the list.
-void strlist_done( strlist_t *list ) {
- STRLIST_FOREACH(list, string, free(string));
- free(list->items);
- list->items = NULL;
- list->count = list->capacity = 0;
-}
-
-static int strlist_compare_strings(const void* a, const void* b) {
- const char *sa = *(const char **)a;
- const char *sb = *(const char **)b;
- return strcmp(sa, sb);
-}
-
-/* sort the strings in a given list (using strcmp) */
-void strlist_sort( strlist_t *list ) {
- if (list->count > 0) {
- qsort(list->items, (size_t)list->count, sizeof(void*), strlist_compare_strings);
- }
-}
-
-
-// bits for flags argument
-#define LIST_LONG (1 << 0)
-#define LIST_ALL (1 << 1)
-#define LIST_RECURSIVE (1 << 2)
-#define LIST_DIRECTORIES (1 << 3)
-#define LIST_SIZE (1 << 4)
-#define LIST_LONG_NUMERIC (1 << 5)
-#define LIST_CLASSIFY (1 << 6)
-#define LIST_MACLABEL (1 << 7)
-#define LIST_INODE (1 << 8)
-
-// fwd
-static int listpath(const char *name, int flags);
-
-static char mode2kind(mode_t mode)
-{
- switch(mode & S_IFMT){
- case S_IFSOCK: return 's';
- case S_IFLNK: return 'l';
- case S_IFREG: return '-';
- case S_IFDIR: return 'd';
- case S_IFBLK: return 'b';
- case S_IFCHR: return 'c';
- case S_IFIFO: return 'p';
- default: return '?';
- }
-}
-
-void strmode(mode_t mode, char *out)
-{
- *out++ = mode2kind(mode);
-
- *out++ = (mode & 0400) ? 'r' : '-';
- *out++ = (mode & 0200) ? 'w' : '-';
- if(mode & 04000) {
- *out++ = (mode & 0100) ? 's' : 'S';
- } else {
- *out++ = (mode & 0100) ? 'x' : '-';
- }
- *out++ = (mode & 040) ? 'r' : '-';
- *out++ = (mode & 020) ? 'w' : '-';
- if(mode & 02000) {
- *out++ = (mode & 010) ? 's' : 'S';
- } else {
- *out++ = (mode & 010) ? 'x' : '-';
- }
- *out++ = (mode & 04) ? 'r' : '-';
- *out++ = (mode & 02) ? 'w' : '-';
- if(mode & 01000) {
- *out++ = (mode & 01) ? 't' : 'T';
- } else {
- *out++ = (mode & 01) ? 'x' : '-';
- }
- *out = 0;
-}
-
-static void user2str(uid_t uid, char *out, size_t out_size)
-{
- struct passwd *pw = getpwuid(uid);
- if(pw) {
- strlcpy(out, pw->pw_name, out_size);
- } else {
- snprintf(out, out_size, "%d", uid);
- }
-}
-
-static void group2str(gid_t gid, char *out, size_t out_size)
-{
- struct group *gr = getgrgid(gid);
- if(gr) {
- strlcpy(out, gr->gr_name, out_size);
- } else {
- snprintf(out, out_size, "%d", gid);
- }
-}
-
-static int show_total_size(const char *dirname, DIR *d, int flags)
-{
- struct dirent *de;
- char tmp[1024];
- struct stat s;
- int sum = 0;
-
- /* run through the directory and sum up the file block sizes */
- while ((de = readdir(d)) != 0) {
- if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
- continue;
- if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
- continue;
-
- if (strcmp(dirname, "/") == 0)
- snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
- else
- snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name);
-
- if (lstat(tmp, &s) < 0) {
- fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno));
- rewinddir(d);
- return -1;
- }
-
- sum += s.st_blocks / 2;
- }
-
- printf("total %d\n", sum);
- rewinddir(d);
- return 0;
-}
-
-static int listfile_size(const char *path, const char *filename, struct stat *s,
- int flags)
-{
- if(!s || !path) {
- return -1;
- }
-
- /* blocks are 512 bytes, we want output to be KB */
- if ((flags & LIST_SIZE) != 0) {
- printf("%lld ", (long long)s->st_blocks / 2);
- }
-
- if ((flags & LIST_CLASSIFY) != 0) {
- char filetype = mode2kind(s->st_mode);
- if (filetype != 'l') {
- printf("%c ", filetype);
- } else {
- struct stat link_dest;
- if (!stat(path, &link_dest)) {
- printf("l%c ", mode2kind(link_dest.st_mode));
- } else {
- fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno));
- printf("l? ");
- }
- }
- }
-
- printf("%s\n", filename);
-
- return 0;
-}
-
-static int listfile_long(const char *path, struct stat *s, int flags)
-{
- char date[32];
- char mode[16];
- char user[32];
- char group[32];
- const char *name;
-
- if(!s || !path) {
- return -1;
- }
-
- /* name is anything after the final '/', or the whole path if none*/
- name = strrchr(path, '/');
- if(name == 0) {
- name = path;
- } else {
- name++;
- }
-
- strmode(s->st_mode, mode);
- if (flags & LIST_LONG_NUMERIC) {
- snprintf(user, sizeof(user), "%u", s->st_uid);
- snprintf(group, sizeof(group), "%u", s->st_gid);
- } else {
- user2str(s->st_uid, user, sizeof(user));
- group2str(s->st_gid, group, sizeof(group));
- }
-
- strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime));
- date[31] = 0;
-
-// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
-// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
-
- switch(s->st_mode & S_IFMT) {
- case S_IFBLK:
- case S_IFCHR:
- printf("%s %-8s %-8s %3d, %3d %s %s\n",
- mode, user, group,
- major(s->st_rdev), minor(s->st_rdev),
- date, name);
- break;
- case S_IFREG:
- printf("%s %-8s %-8s %8lld %s %s\n",
- mode, user, group, (long long)s->st_size, date, name);
- break;
- case S_IFLNK: {
- char linkto[256];
- ssize_t len;
-
- len = readlink(path, linkto, 256);
- if(len < 0) return -1;
-
- if(len > 255) {
- linkto[252] = '.';
- linkto[253] = '.';
- linkto[254] = '.';
- linkto[255] = 0;
- } else {
- linkto[len] = 0;
- }
-
- printf("%s %-8s %-8s %s %s -> %s\n",
- mode, user, group, date, name, linkto);
- break;
- }
- default:
- printf("%s %-8s %-8s %s %s\n",
- mode, user, group, date, name);
-
- }
- return 0;
-}
-
-static int listfile_maclabel(const char *path, struct stat *s)
-{
- char mode[16];
- char user[32];
- char group[32];
- char *maclabel = NULL;
- const char *name;
-
- if(!s || !path) {
- return -1;
- }
-
- /* name is anything after the final '/', or the whole path if none*/
- name = strrchr(path, '/');
- if(name == 0) {
- name = path;
- } else {
- name++;
- }
-
- lgetfilecon(path, &maclabel);
- if (!maclabel) {
- return -1;
- }
-
- strmode(s->st_mode, mode);
- user2str(s->st_uid, user, sizeof(user));
- group2str(s->st_gid, group, sizeof(group));
-
- switch(s->st_mode & S_IFMT) {
- case S_IFLNK: {
- char linkto[256];
- ssize_t len;
-
- len = readlink(path, linkto, sizeof(linkto));
- if(len < 0) return -1;
-
- if((size_t)len > sizeof(linkto)-1) {
- linkto[sizeof(linkto)-4] = '.';
- linkto[sizeof(linkto)-3] = '.';
- linkto[sizeof(linkto)-2] = '.';
- linkto[sizeof(linkto)-1] = 0;
- } else {
- linkto[len] = 0;
- }
-
- printf("%s %-8s %-8s %s %s -> %s\n",
- mode, user, group, maclabel, name, linkto);
- break;
- }
- default:
- printf("%s %-8s %-8s %s %s\n",
- mode, user, group, maclabel, name);
-
- }
-
- free(maclabel);
-
- return 0;
-}
-
-static int listfile(const char *dirname, const char *filename, int flags)
-{
- struct stat s;
-
- if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL | LIST_INODE)) == 0) {
- printf("%s\n", filename);
- return 0;
- }
-
- char tmp[4096];
- const char* pathname = filename;
-
- if (dirname != NULL) {
- snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename);
- pathname = tmp;
- } else {
- pathname = filename;
- }
-
- if(lstat(pathname, &s) < 0) {
- fprintf(stderr, "lstat '%s' failed: %s\n", pathname, strerror(errno));
- return -1;
- }
-
- if(flags & LIST_INODE) {
- printf("%8llu ", (unsigned long long)s.st_ino);
- }
-
- if ((flags & LIST_MACLABEL) != 0) {
- return listfile_maclabel(pathname, &s);
- } else if ((flags & LIST_LONG) != 0) {
- return listfile_long(pathname, &s, flags);
- } else /*((flags & LIST_SIZE) != 0)*/ {
- return listfile_size(pathname, filename, &s, flags);
- }
-}
-
-static int listdir(const char *name, int flags)
-{
- char tmp[4096];
- DIR *d;
- struct dirent *de;
- strlist_t files = STRLIST_INITIALIZER;
-
- d = opendir(name);
- if(d == 0) {
- fprintf(stderr, "opendir failed, %s\n", strerror(errno));
- return -1;
- }
-
- if ((flags & LIST_SIZE) != 0) {
- show_total_size(name, d, flags);
- }
-
- while((de = readdir(d)) != 0){
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
- if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
-
- strlist_append_dup(&files, de->d_name);
- }
-
- strlist_sort(&files);
- STRLIST_FOREACH(&files, filename, listfile(name, filename, flags));
- strlist_done(&files);
-
- if (flags & LIST_RECURSIVE) {
- strlist_t subdirs = STRLIST_INITIALIZER;
-
- rewinddir(d);
-
- while ((de = readdir(d)) != 0) {
- struct stat s;
- int err;
-
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
- continue;
- if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
- continue;
-
- if (!strcmp(name, "/"))
- snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
- else
- snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name);
-
- /*
- * If the name ends in a '/', use stat() so we treat it like a
- * directory even if it's a symlink.
- */
- if (tmp[strlen(tmp)-1] == '/')
- err = stat(tmp, &s);
- else
- err = lstat(tmp, &s);
-
- if (err < 0) {
- perror(tmp);
- closedir(d);
- return -1;
- }
-
- if (S_ISDIR(s.st_mode)) {
- strlist_append_dup(&subdirs, tmp);
- }
- }
- strlist_sort(&subdirs);
- STRLIST_FOREACH(&subdirs, path, {
- printf("\n%s:\n", path);
- listdir(path, flags);
- });
- strlist_done(&subdirs);
- }
-
- closedir(d);
- return 0;
-}
-
-static int listpath(const char *name, int flags)
-{
- struct stat s;
- int err;
-
- /*
- * If the name ends in a '/', use stat() so we treat it like a
- * directory even if it's a symlink.
- */
- if (name[strlen(name)-1] == '/')
- err = stat(name, &s);
- else
- err = lstat(name, &s);
-
- if (err < 0) {
- perror(name);
- return -1;
- }
-
- if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
- if (flags & LIST_RECURSIVE)
- printf("\n%s:\n", name);
- return listdir(name, flags);
- } else {
- /* yeah this calls stat() again*/
- return listfile(NULL, name, flags);
- }
-}
-
-int ls_main(int argc, char **argv)
-{
- int flags = 0;
-
- if(argc > 1) {
- int i;
- int err = 0;
- strlist_t files = STRLIST_INITIALIZER;
-
- for (i = 1; i < argc; i++) {
- if (argv[i][0] == '-') {
- /* an option ? */
- const char *arg = argv[i]+1;
- while (arg[0]) {
- switch (arg[0]) {
- case 'l': flags |= LIST_LONG; break;
- case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break;
- case 's': flags |= LIST_SIZE; break;
- case 'R': flags |= LIST_RECURSIVE; break;
- case 'd': flags |= LIST_DIRECTORIES; break;
- case 'Z': flags |= LIST_MACLABEL; break;
- case 'a': flags |= LIST_ALL; break;
- case 'F': flags |= LIST_CLASSIFY; break;
- case 'i': flags |= LIST_INODE; break;
- default:
- fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]);
- exit(1);
- }
- arg++;
- }
- } else {
- /* not an option ? */
- strlist_append_dup(&files, argv[i]);
- }
- }
-
- if (files.count > 0) {
- STRLIST_FOREACH(&files, path, {
- if (listpath(path, flags) != 0) {
- err = EXIT_FAILURE;
- }
- });
- strlist_done(&files);
- return err;
- }
- }
-
- // list working directory if no files or directories were specified
- return listpath(".", flags);
-}
diff --git a/toolbox/lsof.c b/toolbox/lsof.c
deleted file mode 100644
index 982f5aa..0000000
--- a/toolbox/lsof.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (c) 2010, 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.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" 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 <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <pwd.h>
-#include <sys/stat.h>
-
-#define BUF_MAX 1024
-#define CMD_DISPLAY_MAX (9 + 1)
-#define USER_DISPLAY_MAX (10 + 1)
-
-struct pid_info_t {
- pid_t pid;
- char user[USER_DISPLAY_MAX];
-
- char cmdline[CMD_DISPLAY_MAX];
-
- char path[PATH_MAX];
- ssize_t parent_length;
-};
-
-static void print_header()
-{
- printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n",
- "COMMAND",
- "PID",
- "USER",
- "FD",
- "TYPE",
- "DEVICE",
- "SIZE/OFF",
- "NODE",
- "NAME");
-}
-
-static void print_type(char *type, struct pid_info_t* info)
-{
- static ssize_t link_dest_size;
- static char link_dest[PATH_MAX];
-
- strlcat(info->path, type, sizeof(info->path));
- if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) {
- if (errno == ENOENT)
- goto out;
-
- snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno));
- } else {
- link_dest[link_dest_size] = '\0';
- }
-
- // Things that are just the root filesystem are uninteresting (we already know)
- if (!strcmp(link_dest, "/"))
- goto out;
-
- printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
- info->cmdline, info->pid, info->user, type,
- "???", "???", "???", "???", link_dest);
-
-out:
- info->path[info->parent_length] = '\0';
-}
-
-// Prints out all file that have been memory mapped
-static void print_maps(struct pid_info_t* info)
-{
- FILE *maps;
- size_t offset;
- char device[10];
- long int inode;
- char file[PATH_MAX];
-
- strlcat(info->path, "maps", sizeof(info->path));
-
- maps = fopen(info->path, "r");
- if (!maps)
- goto out;
-
- while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode,
- file) == 4) {
- // We don't care about non-file maps
- if (inode == 0 || !strcmp(device, "00:00"))
- continue;
-
- printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n",
- info->cmdline, info->pid, info->user, "mem",
- "???", device, offset, inode, file);
- }
-
- fclose(maps);
-
-out:
- info->path[info->parent_length] = '\0';
-}
-
-// Prints out all open file descriptors
-static void print_fds(struct pid_info_t* info)
-{
- static char* fd_path = "fd/";
- strlcat(info->path, fd_path, sizeof(info->path));
-
- int previous_length = info->parent_length;
- info->parent_length += strlen(fd_path);
-
- DIR *dir = opendir(info->path);
- if (dir == NULL) {
- char msg[BUF_MAX];
- snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno));
- printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
- info->cmdline, info->pid, info->user, "FDS",
- "", "", "", "", msg);
- goto out;
- }
-
- struct dirent* de;
- while ((de = readdir(dir))) {
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
- continue;
-
- print_type(de->d_name, info);
- }
- closedir(dir);
-
-out:
- info->parent_length = previous_length;
- info->path[info->parent_length] = '\0';
-}
-
-static void lsof_dumpinfo(pid_t pid)
-{
- int fd;
- struct pid_info_t info;
- struct stat pidstat;
- struct passwd *pw;
-
- info.pid = pid;
- snprintf(info.path, sizeof(info.path), "/proc/%d/", pid);
- info.parent_length = strlen(info.path);
-
- // Get the UID by calling stat on the proc/pid directory.
- if (!stat(info.path, &pidstat)) {
- pw = getpwuid(pidstat.st_uid);
- if (pw) {
- strlcpy(info.user, pw->pw_name, sizeof(info.user));
- } else {
- snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid);
- }
- } else {
- strcpy(info.user, "???");
- }
-
- // Read the command line information; each argument is terminated with NULL.
- strlcat(info.path, "cmdline", sizeof(info.path));
- fd = open(info.path, O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "Couldn't read %s\n", info.path);
- return;
- }
-
- char cmdline[PATH_MAX];
- int numRead = read(fd, cmdline, sizeof(cmdline) - 1);
- close(fd);
-
- if (numRead < 0) {
- fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno));
- return;
- }
-
- cmdline[numRead] = '\0';
-
- // We only want the basename of the cmdline
- strlcpy(info.cmdline, basename(cmdline), sizeof(info.cmdline));
-
- // Read each of these symlinks
- print_type("cwd", &info);
- print_type("exe", &info);
- print_type("root", &info);
-
- print_fds(&info);
- print_maps(&info);
-}
-
-int lsof_main(int argc, char *argv[])
-{
- long int pid = 0;
- char* endptr;
- if (argc == 2) {
- pid = strtol(argv[1], &endptr, 10);
- }
-
- print_header();
-
- if (pid) {
- lsof_dumpinfo(pid);
- } else {
- DIR *dir = opendir("/proc");
- if (dir == NULL) {
- fprintf(stderr, "Couldn't open /proc\n");
- return -1;
- }
-
- struct dirent* de;
- while ((de = readdir(dir))) {
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
- continue;
-
- // Only inspect directories that are PID numbers
- pid = strtol(de->d_name, &endptr, 10);
- if (*endptr != '\0')
- continue;
-
- lsof_dumpinfo(pid);
- }
- closedir(dir);
- }
-
- return 0;
-}
diff --git a/toolbox/nandread.c b/toolbox/nandread.c
deleted file mode 100644
index bd19942..0000000
--- a/toolbox/nandread.c
+++ /dev/null
@@ -1,287 +0,0 @@
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <mtd/mtd-user.h>
-#include <sys/ioctl.h>
-
-static int test_empty(const char *buf, size_t size)
-{
- while(size--) {
- if (*buf++ != (char) 0xff)
- return 0;
- }
- return 1;
-}
-
-int nandread_main(int argc, char **argv)
-{
- char *devname = NULL;
- char *filename = NULL;
- char *statusfilename = NULL;
- char *statusext = ".stat";
- int fd;
- int outfd = -1;
- FILE *statusfile = NULL;
- int ret;
- int verbose = 0;
- void *buffer;
- loff_t pos, opos, end, bpos;
- loff_t start = 0, len = 0;
- int c;
- int i;
- int empty_pages = 0;
- int page_count = 0;
- int bad_block;
- int rawmode = 0;
- uint32_t *oob_data;
- uint8_t *oob_fixed;
- size_t spare_size = 64;
- struct mtd_info_user mtdinfo;
- struct mtd_ecc_stats initial_ecc, last_ecc, ecc;
- struct mtd_oob_buf oobbuf;
- nand_ecclayout_t ecclayout;
-
- do {
- c = getopt(argc, argv, "d:f:s:S:L:Rhv");
- if (c == EOF)
- break;
- switch (c) {
- case 'd':
- devname = optarg;
- break;
- case 'f':
- filename = optarg;
- break;
- case 's':
- spare_size = atoi(optarg);
- break;
- case 'S':
- start = strtoll(optarg, NULL, 0);
- break;
- case 'L':
- len = strtoll(optarg, NULL, 0);
- break;
- case 'R':
- rawmode = 1;
- break;
- case 'v':
- verbose++;
- break;
- case 'h':
- fprintf(stderr, "%s [-d <dev>] [-f file] [-s sparesize] [-vh]\n"
- " -d <dev> Read from <dev>\n"
- " -f <file> Write to <file>\n"
- " -s <size> Number of spare bytes in file (default 64)\n"
- " -R Raw mode\n"
- " -S <start> Start offset (default 0)\n"
- " -L <len> Length (default 0)\n"
- " -v Print info\n"
- " -h Print help\n", argv[0]);
- return -1;
- case '?':
- fprintf(stderr, "%s: invalid option -%c\n",
- argv[0], optopt);
- exit(1);
- }
- } while (1);
-
- if (optind < argc) {
- fprintf(stderr, "%s: extra arguments\n", argv[0]);
- return 1;
- }
- if (!devname) {
- fprintf(stderr, "%s: specify device name\n", argv[0]);
- return 1;
- }
-
- fd = open(devname, O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "cannot open %s, %s\n", devname, strerror(errno));
- return 1;
- }
-
- if (filename) {
- outfd = creat(filename, 0666);
- if (outfd < 0) {
- fprintf(stderr, "cannot open %s, %s\n", filename, strerror(errno));
- return 1;
- }
- statusfilename = malloc(strlen(filename) + strlen(statusext) + 1);
- strcpy(statusfilename, filename);
- strcat(statusfilename, statusext);
- statusfile = fopen(statusfilename, "w+");
- if (!statusfile) {
- fprintf(stderr, "cannot open %s, %s\n", statusfilename, strerror(errno));
- return 1;
- }
- }
-
- ret = ioctl(fd, MEMGETINFO, &mtdinfo);
- if (ret) {
- fprintf(stderr, "failed get mtd info for %s, %s\n",
- devname, strerror(errno));
- return 1;
- }
-
- if (verbose) {
- printf("size: %u\n", mtdinfo.size);
- printf("erase size: %u\n", mtdinfo.erasesize);
- printf("write size: %u\n", mtdinfo.writesize);
- printf("oob size: %u\n", mtdinfo.oobsize);
- }
-
- buffer = malloc(mtdinfo.writesize + mtdinfo.oobsize + spare_size);
- if (!buffer) {
- fprintf(stderr, "failed allocate readbuffer size %u\n",
- mtdinfo.writesize + mtdinfo.oobsize);
- return 1;
- }
-
- oobbuf.length = mtdinfo.oobsize;
- oob_data = (uint32_t *)((uint8_t *)buffer + mtdinfo.writesize);
- memset(oob_data, 0xff, mtdinfo.oobsize + spare_size);
- oobbuf.ptr = (uint8_t *)oob_data + spare_size;
-
- ret = ioctl(fd, ECCGETLAYOUT, &ecclayout);
- if (ret) {
- fprintf(stderr, "failed get ecc layout for %s, %s\n",
- devname, strerror(errno));
- return 1;
- }
- if (verbose) {
- printf("ecc bytes: %u\n", ecclayout.eccbytes);
- printf("oobavail: %u\n", ecclayout.oobavail);
- }
- if (ecclayout.oobavail > spare_size)
- printf("oobavail, %d > image spare size, %zu\n", ecclayout.oobavail, spare_size);
-
- ret = ioctl(fd, ECCGETSTATS, &initial_ecc);
- if (ret) {
- fprintf(stderr, "failed get ecc stats for %s, %s\n",
- devname, strerror(errno));
- return 1;
- }
- last_ecc = initial_ecc;
-
- if (verbose) {
- printf("initial ecc corrected: %u\n", initial_ecc.corrected);
- printf("initial ecc failed: %u\n", initial_ecc.failed);
- printf("initial ecc badblocks: %u\n", initial_ecc.badblocks);
- printf("initial ecc bbtblocks: %u\n", initial_ecc.bbtblocks);
- }
-
- if (rawmode) {
- rawmode = mtdinfo.oobsize;
- ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW);
- if (ret) {
- fprintf(stderr, "failed set raw mode for %s, %s\n",
- devname, strerror(errno));
- return 1;
- }
- }
-
- end = len ? (start + len) : mtdinfo.size;
- for (pos = start, opos = 0; pos < end; pos += mtdinfo.writesize) {
- bad_block = 0;
- if (verbose > 3)
- printf("reading at %" PRIx64 "\n", pos);
- lseek64(fd, pos, SEEK_SET);
- ret = read(fd, buffer, mtdinfo.writesize + rawmode);
- if (ret < (int)mtdinfo.writesize) {
- fprintf(stderr, "short read at %" PRIx64 ", %d\n", pos, ret);
- bad_block = 2;
- }
- if (!rawmode) {
- oobbuf.start = pos;
- ret = ioctl(fd, MEMREADOOB, &oobbuf);
- if (ret) {
- fprintf(stderr, "failed to read oob data at %" PRIx64 ", %d\n", pos, ret);
- bad_block = 2;
- }
- }
- ret = ioctl(fd, ECCGETSTATS, &ecc);
- if (ret) {
- fprintf(stderr, "failed get ecc stats for %s, %s\n",
- devname, strerror(errno));
- return 1;
- }
- bpos = pos / mtdinfo.erasesize * mtdinfo.erasesize;
- ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
- if (ret && errno != EOPNOTSUPP) {
- printf("badblock at %" PRIx64 "\n", pos);
- bad_block = 1;
- }
- if (ecc.corrected != last_ecc.corrected)
- printf("ecc corrected, %u, at %" PRIx64 "\n", ecc.corrected - last_ecc.corrected, pos);
- if (ecc.failed != last_ecc.failed)
- printf("ecc failed, %u, at %" PRIx64 "\n", ecc.failed - last_ecc.failed, pos);
- if (ecc.badblocks != last_ecc.badblocks)
- printf("ecc badblocks, %u, at %" PRIx64 "\n", ecc.badblocks - last_ecc.badblocks, pos);
- if (ecc.bbtblocks != last_ecc.bbtblocks)
- printf("ecc bbtblocks, %u, at %" PRIx64 "\n", ecc.bbtblocks - last_ecc.bbtblocks, pos);
-
- if (!rawmode) {
- oob_fixed = (uint8_t *)oob_data;
- for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
- int len = ecclayout.oobfree[i].length;
- if (oob_fixed + len > oobbuf.ptr)
- len = oobbuf.ptr - oob_fixed;
- if (len) {
- memcpy(oob_fixed, oobbuf.ptr + ecclayout.oobfree[i].offset, len);
- oob_fixed += len;
- }
- }
- }
-
- if (outfd >= 0) {
- ret = write(outfd, buffer, mtdinfo.writesize + spare_size);
- if (ret < (int)(mtdinfo.writesize + spare_size)) {
- fprintf(stderr, "short write at %" PRIx64 ", %d\n", pos, ret);
- close(outfd);
- outfd = -1;
- }
- if (ecc.corrected != last_ecc.corrected)
- fprintf(statusfile, "%08" PRIx64 ": ecc corrected\n", opos);
- if (ecc.failed != last_ecc.failed)
- fprintf(statusfile, "%08" PRIx64 ": ecc failed\n", opos);
- if (bad_block == 1)
- fprintf(statusfile, "%08" PRIx64 ": badblock\n", opos);
- if (bad_block == 2)
- fprintf(statusfile, "%08" PRIx64 ": read error\n", opos);
- opos += mtdinfo.writesize + spare_size;
- }
-
- last_ecc = ecc;
- page_count++;
- if (test_empty(buffer, mtdinfo.writesize + mtdinfo.oobsize + spare_size))
- empty_pages++;
- else if (verbose > 2 || (verbose > 1 && !(pos & (mtdinfo.erasesize - 1))))
- printf("page at %" PRIx64 " (%d oobbytes): %08x %08x %08x %08x "
- "%08x %08x %08x %08x\n", pos, oobbuf.start,
- oob_data[0], oob_data[1], oob_data[2], oob_data[3],
- oob_data[4], oob_data[5], oob_data[6], oob_data[7]);
- }
-
- if (outfd >= 0) {
- fprintf(statusfile, "read %d pages, %d empty\n", page_count, empty_pages);
- fprintf(statusfile, "total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
- fprintf(statusfile, "total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
- fprintf(statusfile, "total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
- fprintf(statusfile, "total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
- }
- if (verbose) {
- printf("total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
- printf("total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
- printf("total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
- printf("total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
- }
- printf("read %d pages, %d empty\n", page_count, empty_pages);
-
- return 0;
-}
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
index 5b98a01..d7047e2 100644
--- a/toolbox/newfs_msdos.c
+++ b/toolbox/newfs_msdos.c
@@ -695,7 +695,7 @@
(u_int)tm->tm_min));
mk4(bsx->volid, x);
mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
- sprintf(buf, "FAT%u", fat);
+ snprintf(buf, sizeof(buf), "FAT%u", fat);
setstr(bsx->type, buf, sizeof(bsx->type));
if (!opt_B) {
x1 += sizeof(struct bsx);
@@ -741,6 +741,7 @@
exit(1);
}
}
+ free(img);
}
return 0;
}
diff --git a/toolbox/prlimit.c b/toolbox/prlimit.c
deleted file mode 100644
index 8cf202a..0000000
--- a/toolbox/prlimit.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2014, 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.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" 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 <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-static void
-usage(const char *s)
-{
- fprintf(stderr, "usage: %s pid resource cur max\n", s);
- exit(EXIT_FAILURE);
-}
-
-int prlimit_main(int argc, char *argv[])
-{
- pid_t pid;
- struct rlimit64 rl;
- int resource;
- int rc;
-
- if (argc != 5)
- usage(*argv);
-
- if (sscanf(argv[1], "%d", &pid) != 1)
- usage(*argv);
-
- if (sscanf(argv[2], "%d", &resource) != 1)
- usage(*argv);
-
- if (sscanf(argv[3], "%llu", &rl.rlim_cur) != 1)
- usage(*argv);
-
- if (sscanf(argv[4], "%llu", &rl.rlim_max) != 1)
- usage(*argv);
-
- printf("setting resource %d of pid %d to [%llu,%llu]\n", resource, pid,
- rl.rlim_cur, rl.rlim_max);
- rc = prlimit64(pid, resource, &rl, NULL);
- if (rc < 0) {
- perror("prlimit");
- exit(EXIT_FAILURE);
- }
-
- return 0;
-}
diff --git a/toolbox/ps.c b/toolbox/ps.c
deleted file mode 100644
index cf3f05a..0000000
--- a/toolbox/ps.c
+++ /dev/null
@@ -1,326 +0,0 @@
-#include <ctype.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/sched_policy.h>
-
-static char *nexttoksep(char **strp, char *sep)
-{
- char *p = strsep(strp,sep);
- return (p == 0) ? "" : p;
-}
-static char *nexttok(char **strp)
-{
- return nexttoksep(strp, " ");
-}
-
-#define SHOW_PRIO 1
-#define SHOW_TIME 2
-#define SHOW_POLICY 4
-#define SHOW_CPU 8
-#define SHOW_MACLABEL 16
-#define SHOW_NUMERIC_UID 32
-#define SHOW_ABI 64
-
-#if __LP64__
-#define PC_WIDTH 10 /* Realistically, the top bits will be 0, so don't waste space. */
-#else
-#define PC_WIDTH (2*sizeof(uintptr_t))
-#endif
-
-static int display_flags = 0;
-static int ppid_filter = 0;
-
-static void print_exe_abi(int pid);
-
-static int ps_line(int pid, int tid, char *namefilter)
-{
- char statline[1024];
- char cmdline[1024];
- char macline[1024];
- char user[32];
- struct stat stats;
- int fd, r;
- char *ptr, *name, *state;
- int ppid;
- unsigned rss, vss;
- uintptr_t eip;
- unsigned utime, stime;
- int prio, nice, rtprio, sched, psr;
- struct passwd *pw;
-
- sprintf(statline, "/proc/%d", pid);
- stat(statline, &stats);
-
- if(tid) {
- sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
- cmdline[0] = 0;
- snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid);
- } else {
- sprintf(statline, "/proc/%d/stat", pid);
- sprintf(cmdline, "/proc/%d/cmdline", pid);
- snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
- fd = open(cmdline, O_RDONLY);
- if(fd == 0) {
- r = 0;
- } else {
- r = read(fd, cmdline, 1023);
- close(fd);
- if(r < 0) r = 0;
- }
- cmdline[r] = 0;
- }
-
- fd = open(statline, O_RDONLY);
- if(fd == 0) return -1;
- r = read(fd, statline, 1023);
- close(fd);
- if(r < 0) return -1;
- statline[r] = 0;
-
- ptr = statline;
- nexttok(&ptr); // skip pid
- ptr++; // skip "("
-
- name = ptr;
- ptr = strrchr(ptr, ')'); // Skip to *last* occurence of ')',
- *ptr++ = '\0'; // and null-terminate name.
-
- ptr++; // skip " "
- state = nexttok(&ptr);
- ppid = atoi(nexttok(&ptr));
- nexttok(&ptr); // pgrp
- nexttok(&ptr); // sid
- nexttok(&ptr); // tty
- nexttok(&ptr); // tpgid
- nexttok(&ptr); // flags
- nexttok(&ptr); // minflt
- nexttok(&ptr); // cminflt
- nexttok(&ptr); // majflt
- nexttok(&ptr); // cmajflt
-#if 1
- utime = atoi(nexttok(&ptr));
- stime = atoi(nexttok(&ptr));
-#else
- nexttok(&ptr); // utime
- nexttok(&ptr); // stime
-#endif
- nexttok(&ptr); // cutime
- nexttok(&ptr); // cstime
- prio = atoi(nexttok(&ptr));
- nice = atoi(nexttok(&ptr));
- nexttok(&ptr); // threads
- nexttok(&ptr); // itrealvalue
- nexttok(&ptr); // starttime
- vss = strtoul(nexttok(&ptr), 0, 10); // vsize
- rss = strtoul(nexttok(&ptr), 0, 10); // rss
- nexttok(&ptr); // rlim
- nexttok(&ptr); // startcode
- nexttok(&ptr); // endcode
- nexttok(&ptr); // startstack
- nexttok(&ptr); // kstkesp
- eip = strtoul(nexttok(&ptr), 0, 10); // kstkeip
- nexttok(&ptr); // signal
- nexttok(&ptr); // blocked
- nexttok(&ptr); // sigignore
- nexttok(&ptr); // sigcatch
- nexttok(&ptr); // wchan
- nexttok(&ptr); // nswap
- nexttok(&ptr); // cnswap
- nexttok(&ptr); // exit signal
- psr = atoi(nexttok(&ptr)); // processor
- rtprio = atoi(nexttok(&ptr)); // rt_priority
- sched = atoi(nexttok(&ptr)); // scheduling policy
-
- nexttok(&ptr); // tty
-
- if(tid != 0) {
- ppid = pid;
- pid = tid;
- }
-
- pw = getpwuid(stats.st_uid);
- if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) {
- sprintf(user,"%d",(int)stats.st_uid);
- } else {
- strcpy(user,pw->pw_name);
- }
-
- if(ppid_filter != 0 && ppid != ppid_filter) {
- return 0;
- }
-
- if(!namefilter || !strncmp(cmdline[0] ? cmdline : name, namefilter, strlen(namefilter))) {
- if (display_flags & SHOW_MACLABEL) {
- fd = open(macline, O_RDONLY);
- strcpy(macline, "-");
- if (fd >= 0) {
- r = read(fd, macline, sizeof(macline)-1);
- close(fd);
- if (r > 0)
- macline[r] = 0;
- }
- printf("%-30s %-9s %-5d %-5d %s\n", macline, user, pid, ppid, cmdline[0] ? cmdline : name);
- return 0;
- }
-
- printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
- if (display_flags & SHOW_CPU)
- printf(" %-2d", psr);
- if (display_flags & SHOW_PRIO)
- printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
- if (display_flags & SHOW_POLICY) {
- SchedPolicy p;
- if (get_sched_policy(pid, &p) < 0)
- printf(" un ");
- else
- printf(" %.2s ", get_sched_policy_name(p));
- }
- char path[PATH_MAX];
- snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
- char wchan[10];
- int fd = open(path, O_RDONLY);
- ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
- if (wchan_len == -1) {
- wchan[wchan_len = 0] = '\0';
- }
- close(fd);
- printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
- if (display_flags & SHOW_ABI) {
- print_exe_abi(pid);
- }
- printf("%s", cmdline[0] ? cmdline : name);
- if(display_flags&SHOW_TIME)
- printf(" (u:%d, s:%d)", utime, stime);
-
- printf("\n");
- }
- return 0;
-}
-
-static void print_exe_abi(int pid)
-{
- int fd, r;
- char exeline[1024];
-
- sprintf(exeline, "/proc/%d/exe", pid);
- fd = open(exeline, O_RDONLY);
- if(fd == 0) {
- printf(" ");
- return;
- }
- r = read(fd, exeline, 5 /* 4 byte ELFMAG + 1 byte EI_CLASS */);
- close(fd);
- if(r < 0) {
- printf(" ");
- return;
- }
- if (memcmp("\177ELF", exeline, 4) != 0) {
- printf("?? ");
- return;
- }
- switch (exeline[4]) {
- case 1:
- printf("32 ");
- return;
- case 2:
- printf("64 ");
- return;
- default:
- printf("?? ");
- return;
- }
-}
-
-void ps_threads(int pid, char *namefilter)
-{
- char tmp[128];
- DIR *d;
- struct dirent *de;
-
- sprintf(tmp,"/proc/%d/task",pid);
- d = opendir(tmp);
- if(d == 0) return;
-
- while((de = readdir(d)) != 0){
- if(isdigit(de->d_name[0])){
- int tid = atoi(de->d_name);
- if(tid == pid) continue;
- ps_line(pid, tid, namefilter);
- }
- }
- closedir(d);
-}
-
-int ps_main(int argc, char **argv)
-{
- DIR *d;
- struct dirent *de;
- char *namefilter = 0;
- int pidfilter = 0;
- int threads = 0;
-
- d = opendir("/proc");
- if(d == 0) return -1;
-
- while(argc > 1){
- if(!strcmp(argv[1],"-t")) {
- threads = 1;
- } else if(!strcmp(argv[1],"-n")) {
- display_flags |= SHOW_NUMERIC_UID;
- } else if(!strcmp(argv[1],"-x")) {
- display_flags |= SHOW_TIME;
- } else if(!strcmp(argv[1], "-Z")) {
- display_flags |= SHOW_MACLABEL;
- } else if(!strcmp(argv[1],"-P")) {
- display_flags |= SHOW_POLICY;
- } else if(!strcmp(argv[1],"-p")) {
- display_flags |= SHOW_PRIO;
- } else if(!strcmp(argv[1],"-c")) {
- display_flags |= SHOW_CPU;
- } else if(!strcmp(argv[1],"--abi")) {
- display_flags |= SHOW_ABI;
- } else if(!strcmp(argv[1],"--ppid")) {
- ppid_filter = atoi(argv[2]);
- argc--;
- argv++;
- } else if(isdigit(argv[1][0])){
- pidfilter = atoi(argv[1]);
- } else {
- namefilter = argv[1];
- }
- argc--;
- argv++;
- }
-
- if (display_flags & SHOW_MACLABEL) {
- printf("LABEL USER PID PPID NAME\n");
- } else {
- printf("USER PID PPID VSIZE RSS %s%s %sWCHAN %*s %sNAME\n",
- (display_flags&SHOW_CPU)?"CPU ":"",
- (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"",
- (display_flags&SHOW_POLICY)?"PCY " : "",
- (int) PC_WIDTH, "PC",
- (display_flags&SHOW_ABI)?"ABI " : "");
- }
- while((de = readdir(d)) != 0){
- if(isdigit(de->d_name[0])){
- int pid = atoi(de->d_name);
- if(!pidfilter || (pidfilter == pid)) {
- ps_line(pid, 0, namefilter);
- if(threads) ps_threads(pid, namefilter);
- }
- }
- }
- closedir(d);
- return 0;
-}
-
diff --git a/toolbox/r.c b/toolbox/r.c
deleted file mode 100644
index b96cdb2..0000000
--- a/toolbox/r.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#if __LP64__
-#define strtoptr strtoull
-#else
-#define strtoptr strtoul
-#endif
-
-static int usage()
-{
- fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
- return -1;
-}
-
-int main(int argc, char *argv[])
-{
- if(argc < 2) return usage();
-
- int width = 4;
- if(!strcmp(argv[1], "-b")) {
- width = 1;
- argc--;
- argv++;
- } else if(!strcmp(argv[1], "-s")) {
- width = 2;
- argc--;
- argv++;
- }
-
- if(argc < 2) return usage();
- uintptr_t addr = strtoptr(argv[1], 0, 16);
-
- uintptr_t endaddr = 0;
- char* end = strchr(argv[1], '-');
- if (end)
- endaddr = strtoptr(end + 1, 0, 16);
-
- if (!endaddr)
- endaddr = addr + width - 1;
-
- if (endaddr <= addr) {
- fprintf(stderr, "end address <= start address\n");
- return -1;
- }
-
- bool set = false;
- uint32_t value = 0;
- if(argc > 2) {
- set = true;
- value = strtoul(argv[2], 0, 16);
- }
-
- int fd = open("/dev/mem", O_RDWR | O_SYNC);
- if(fd < 0) {
- fprintf(stderr,"cannot open /dev/mem\n");
- return -1;
- }
-
- off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
- size_t mmap_size = endaddr - mmap_start + 1;
- mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-
- void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, mmap_start);
-
- if(page == MAP_FAILED){
- fprintf(stderr,"cannot mmap region\n");
- return -1;
- }
-
- while (addr <= endaddr) {
- switch(width){
- case 4: {
- uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
- break;
- }
- case 2: {
- uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
- break;
- }
- case 1: {
- uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
- break;
- }
- }
- addr += width;
- }
- return 0;
-}
diff --git a/toolbox/sendevent.c b/toolbox/sendevent.c
deleted file mode 100644
index 4d0ca17..0000000
--- a/toolbox/sendevent.c
+++ /dev/null
@@ -1,42 +0,0 @@
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/input.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-
-int sendevent_main(int argc, char *argv[])
-{
- int fd;
- ssize_t ret;
- int version;
- struct input_event event;
-
- if(argc != 5) {
- fprintf(stderr, "use: %s device type code value\n", argv[0]);
- return 1;
- }
-
- fd = open(argv[1], O_RDWR);
- if(fd < 0) {
- fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
- return 1;
- }
- if (ioctl(fd, EVIOCGVERSION, &version)) {
- fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno));
- return 1;
- }
- memset(&event, 0, sizeof(event));
- event.type = atoi(argv[2]);
- event.code = atoi(argv[3]);
- event.value = atoi(argv[4]);
- ret = write(fd, &event, sizeof(event));
- if(ret < (ssize_t) sizeof(event)) {
- fprintf(stderr, "write event failed, %s\n", strerror(errno));
- return -1;
- }
- return 0;
-}
diff --git a/toolbox/start.c b/toolbox/start.c
deleted file mode 100644
index cca5fef..0000000
--- a/toolbox/start.c
+++ /dev/null
@@ -1 +0,0 @@
-/* Needed by Android.mk. Actual code in start_stop.cpp. */
diff --git a/toolbox/start_stop.cpp b/toolbox/start_stop.cpp
deleted file mode 100644
index dc48c0c..0000000
--- a/toolbox/start_stop.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <error.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <cutils/properties.h>
-
-static const char* services[] = {
- "netd",
- "surfaceflinger",
- "zygote",
- "zygote_secondary",
-};
-
-static int start_stop(bool start, int argc, char* argv[]) {
- if (getuid() != 0) error(1, 0, "must be root");
- const char* property = start ? "ctl.start" : "ctl.stop";
- if (argc > 2) {
- error(1, 0, "usage: %s [SERVICE]\n", argv[0]);
- } else if (argc == 2) {
- property_set(property, argv[1]);
- } else {
- if (start) {
- for (size_t i = 0; i < sizeof(services)/sizeof(services[0]); ++i) {
- property_set(property, services[i]);
- }
- } else {
- for (int i = sizeof(services)/sizeof(services[0]) - 1; i >= 0; --i) {
- property_set(property, services[i]);
- }
- }
- }
- return 0;
-}
-
-extern "C" int start_main(int argc, char* argv[]) {
- return start_stop(true, argc, argv);
-}
-
-extern "C" int stop_main(int argc, char* argv[]) {
- return start_stop(false, argc, argv);
-}
diff --git a/toolbox/stop.c b/toolbox/stop.c
deleted file mode 100644
index cca5fef..0000000
--- a/toolbox/stop.c
+++ /dev/null
@@ -1 +0,0 @@
-/* Needed by Android.mk. Actual code in start_stop.cpp. */
diff --git a/toolbox/toolbox.c b/toolbox/toolbox.c
index 915da44..b5a942c 100644
--- a/toolbox/toolbox.c
+++ b/toolbox/toolbox.c
@@ -4,29 +4,14 @@
#include <string.h>
#include <unistd.h>
-int main(int, char **);
-
-static int toolbox_main(int argc, char **argv)
-{
- // "toolbox foo ..." is equivalent to "foo ..."
- if (argc > 1) {
- return main(argc - 1, argv + 1);
- } else {
- printf("Toolbox!\n");
- return 0;
- }
-}
-
#define TOOL(name) int name##_main(int, char**);
#include "tools.h"
#undef TOOL
-static struct
-{
- const char *name;
+static struct {
+ const char* name;
int (*func)(int, char**);
} tools[] = {
- { "toolbox", toolbox_main },
#define TOOL(name) { #name, name##_main },
#include "tools.h"
#undef TOOL
@@ -40,33 +25,35 @@
_exit(0);
}
-int main(int argc, char **argv)
-{
- int i;
- char *name = argv[0];
-
+int main(int argc, char** argv) {
// Let's assume that none of this code handles broken pipes. At least ls,
// ps, and top were broken (though I'd previously added this fix locally
// to top). We exit rather than use SIG_IGN because tools like top will
// just keep on writing to nowhere forever if we don't stop them.
signal(SIGPIPE, SIGPIPE_handler);
- if((argc > 1) && (argv[1][0] == '@')) {
- name = argv[1] + 1;
- argc--;
- argv++;
- } else {
- char *cmd = strrchr(argv[0], '/');
- if (cmd)
- name = cmd + 1;
- }
+ char* cmd = strrchr(argv[0], '/');
+ char* name = cmd ? (cmd + 1) : argv[0];
- for(i = 0; tools[i].name; i++){
- if(!strcmp(tools[i].name, name)){
+ for (size_t i = 0; tools[i].name; i++) {
+ if (!strcmp(tools[i].name, name)) {
return tools[i].func(argc, argv);
}
}
printf("%s: no such tool\n", argv[0]);
- return -1;
+ return 127;
+}
+
+int toolbox_main(int argc, char** argv) {
+ // "toolbox foo ..." is equivalent to "foo ..."
+ if (argc > 1) {
+ return main(argc - 1, argv + 1);
+ }
+
+ // Plain "toolbox" lists the tools.
+ for (size_t i = 1; tools[i].name; i++) {
+ printf("%s%c", tools[i].name, tools[i+1].name ? ' ' : '\n');
+ }
+ return 0;
}
diff --git a/toolbox/top.c b/toolbox/top.c
deleted file mode 100644
index 1e99d4c..0000000
--- a/toolbox/top.c
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
- * Copyright (c) 2008, 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.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" 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 <ctype.h>
-#include <dirent.h>
-#include <grp.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/sched_policy.h>
-
-struct cpu_info {
- long unsigned utime, ntime, stime, itime;
- long unsigned iowtime, irqtime, sirqtime;
-};
-
-#define PROC_NAME_LEN 64
-#define THREAD_NAME_LEN 32
-#define POLICY_NAME_LEN 4
-
-struct proc_info {
- struct proc_info *next;
- pid_t pid;
- pid_t tid;
- uid_t uid;
- gid_t gid;
- char name[PROC_NAME_LEN];
- char tname[THREAD_NAME_LEN];
- char state;
- uint64_t utime;
- uint64_t stime;
- uint64_t delta_utime;
- uint64_t delta_stime;
- uint64_t delta_time;
- uint64_t vss;
- uint64_t rss;
- int prs;
- int num_threads;
- char policy[POLICY_NAME_LEN];
-};
-
-struct proc_list {
- struct proc_info **array;
- int size;
-};
-
-#define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); }
-
-#define INIT_PROCS 50
-#define THREAD_MULT 8
-static struct proc_info **old_procs, **new_procs;
-static int num_old_procs, num_new_procs;
-static struct proc_info *free_procs;
-static int num_used_procs, num_free_procs;
-
-static int max_procs, delay, iterations, threads;
-
-static struct cpu_info old_cpu, new_cpu;
-
-static struct proc_info *alloc_proc(void);
-static void free_proc(struct proc_info *proc);
-static void read_procs(void);
-static int read_stat(char *filename, struct proc_info *proc);
-static void read_policy(int pid, struct proc_info *proc);
-static void add_proc(int proc_num, struct proc_info *proc);
-static int read_cmdline(char *filename, struct proc_info *proc);
-static int read_status(char *filename, struct proc_info *proc);
-static void print_procs(void);
-static struct proc_info *find_old_proc(pid_t pid, pid_t tid);
-static void free_old_procs(void);
-static int (*proc_cmp)(const void *a, const void *b);
-static int proc_cpu_cmp(const void *a, const void *b);
-static int proc_vss_cmp(const void *a, const void *b);
-static int proc_rss_cmp(const void *a, const void *b);
-static int proc_thr_cmp(const void *a, const void *b);
-static int numcmp(long long a, long long b);
-static void usage(char *cmd);
-
-int top_main(int argc, char *argv[]) {
- num_used_procs = num_free_procs = 0;
-
- max_procs = 0;
- delay = 3;
- iterations = -1;
- proc_cmp = &proc_cpu_cmp;
- for (int i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-m")) {
- if (i + 1 >= argc) {
- fprintf(stderr, "Option -m expects an argument.\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- max_procs = atoi(argv[++i]);
- continue;
- }
- if (!strcmp(argv[i], "-n")) {
- if (i + 1 >= argc) {
- fprintf(stderr, "Option -n expects an argument.\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- iterations = atoi(argv[++i]);
- continue;
- }
- if (!strcmp(argv[i], "-d")) {
- if (i + 1 >= argc) {
- fprintf(stderr, "Option -d expects an argument.\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- delay = atoi(argv[++i]);
- continue;
- }
- if (!strcmp(argv[i], "-s")) {
- if (i + 1 >= argc) {
- fprintf(stderr, "Option -s expects an argument.\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
- ++i;
- if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; }
- if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; }
- if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; }
- if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; }
- fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]);
- exit(EXIT_FAILURE);
- }
- if (!strcmp(argv[i], "-t")) { threads = 1; continue; }
- if (!strcmp(argv[i], "-h")) {
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- }
- fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- if (threads && proc_cmp == &proc_thr_cmp) {
- fprintf(stderr, "Sorting by threads per thread makes no sense!\n");
- exit(EXIT_FAILURE);
- }
-
- free_procs = NULL;
-
- num_new_procs = num_old_procs = 0;
- new_procs = old_procs = NULL;
-
- read_procs();
- while ((iterations == -1) || (iterations-- > 0)) {
- old_procs = new_procs;
- num_old_procs = num_new_procs;
- memcpy(&old_cpu, &new_cpu, sizeof(old_cpu));
- sleep(delay);
- read_procs();
- print_procs();
- free_old_procs();
- }
-
- return 0;
-}
-
-static struct proc_info *alloc_proc(void) {
- struct proc_info *proc;
-
- if (free_procs) {
- proc = free_procs;
- free_procs = free_procs->next;
- num_free_procs--;
- } else {
- proc = malloc(sizeof(*proc));
- if (!proc) die("Could not allocate struct process_info.\n");
- }
-
- num_used_procs++;
-
- return proc;
-}
-
-static void free_proc(struct proc_info *proc) {
- proc->next = free_procs;
- free_procs = proc;
-
- num_used_procs--;
- num_free_procs++;
-}
-
-#define MAX_LINE 256
-
-static void read_procs(void) {
- DIR *proc_dir, *task_dir;
- struct dirent *pid_dir, *tid_dir;
- char filename[64];
- FILE *file;
- int proc_num;
- struct proc_info *proc;
- pid_t pid, tid;
-
- int i;
-
- proc_dir = opendir("/proc");
- if (!proc_dir) die("Could not open /proc.\n");
-
- new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *));
- num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1);
-
- file = fopen("/proc/stat", "r");
- if (!file) die("Could not open /proc/stat.\n");
- fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime,
- &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime);
- fclose(file);
-
- proc_num = 0;
- while ((pid_dir = readdir(proc_dir))) {
- if (!isdigit(pid_dir->d_name[0]))
- continue;
-
- pid = atoi(pid_dir->d_name);
-
- struct proc_info cur_proc;
-
- if (!threads) {
- proc = alloc_proc();
-
- proc->pid = proc->tid = pid;
-
- sprintf(filename, "/proc/%d/stat", pid);
- read_stat(filename, proc);
-
- sprintf(filename, "/proc/%d/cmdline", pid);
- read_cmdline(filename, proc);
-
- sprintf(filename, "/proc/%d/status", pid);
- read_status(filename, proc);
-
- read_policy(pid, proc);
-
- proc->num_threads = 0;
- } else {
- sprintf(filename, "/proc/%d/cmdline", pid);
- read_cmdline(filename, &cur_proc);
-
- sprintf(filename, "/proc/%d/status", pid);
- read_status(filename, &cur_proc);
-
- proc = NULL;
- }
-
- sprintf(filename, "/proc/%d/task", pid);
- task_dir = opendir(filename);
- if (!task_dir) continue;
-
- while ((tid_dir = readdir(task_dir))) {
- if (!isdigit(tid_dir->d_name[0]))
- continue;
-
- if (threads) {
- tid = atoi(tid_dir->d_name);
-
- proc = alloc_proc();
-
- proc->pid = pid; proc->tid = tid;
-
- sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
- read_stat(filename, proc);
-
- read_policy(tid, proc);
-
- strcpy(proc->name, cur_proc.name);
- proc->uid = cur_proc.uid;
- proc->gid = cur_proc.gid;
-
- add_proc(proc_num++, proc);
- } else {
- proc->num_threads++;
- }
- }
-
- closedir(task_dir);
-
- if (!threads)
- add_proc(proc_num++, proc);
- }
-
- for (i = proc_num; i < num_new_procs; i++)
- new_procs[i] = NULL;
-
- closedir(proc_dir);
-}
-
-static int read_stat(char *filename, struct proc_info *proc) {
- FILE *file;
- char buf[MAX_LINE], *open_paren, *close_paren;
-
- file = fopen(filename, "r");
- if (!file) return 1;
- fgets(buf, MAX_LINE, file);
- fclose(file);
-
- /* Split at first '(' and last ')' to get process name. */
- open_paren = strchr(buf, '(');
- close_paren = strrchr(buf, ')');
- if (!open_paren || !close_paren) return 1;
-
- *open_paren = *close_paren = '\0';
- strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN);
- proc->tname[THREAD_NAME_LEN-1] = 0;
-
- /* Scan rest of string. */
- sscanf(close_paren + 1,
- " %c " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
- "%" SCNu64
- "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d "
- "%" SCNu64
- "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
- "%d",
- &proc->state,
- &proc->utime,
- &proc->stime,
- &proc->vss,
- &proc->rss,
- &proc->prs);
-
- return 0;
-}
-
-static void add_proc(int proc_num, struct proc_info *proc) {
- int i;
-
- if (proc_num >= num_new_procs) {
- new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *));
- if (!new_procs) die("Could not expand procs array.\n");
- for (i = num_new_procs; i < 2 * num_new_procs; i++)
- new_procs[i] = NULL;
- num_new_procs = 2 * num_new_procs;
- }
- new_procs[proc_num] = proc;
-}
-
-static int read_cmdline(char *filename, struct proc_info *proc) {
- FILE *file;
- char line[MAX_LINE];
-
- line[0] = '\0';
- file = fopen(filename, "r");
- if (!file) return 1;
- fgets(line, MAX_LINE, file);
- fclose(file);
- if (strlen(line) > 0) {
- strncpy(proc->name, line, PROC_NAME_LEN);
- proc->name[PROC_NAME_LEN-1] = 0;
- } else
- proc->name[0] = 0;
- return 0;
-}
-
-static void read_policy(int pid, struct proc_info *proc) {
- SchedPolicy p;
- if (get_sched_policy(pid, &p) < 0)
- strlcpy(proc->policy, "unk", POLICY_NAME_LEN);
- else {
- strlcpy(proc->policy, get_sched_policy_name(p), POLICY_NAME_LEN);
- proc->policy[2] = '\0';
- }
-}
-
-static int read_status(char *filename, struct proc_info *proc) {
- FILE *file;
- char line[MAX_LINE];
- unsigned int uid, gid;
-
- file = fopen(filename, "r");
- if (!file) return 1;
- while (fgets(line, MAX_LINE, file)) {
- sscanf(line, "Uid: %u", &uid);
- sscanf(line, "Gid: %u", &gid);
- }
- fclose(file);
- proc->uid = uid; proc->gid = gid;
- return 0;
-}
-
-static void print_procs(void) {
- int i;
- struct proc_info *old_proc, *proc;
- long unsigned total_delta_time;
- struct passwd *user;
- char *user_str, user_buf[20];
-
- for (i = 0; i < num_new_procs; i++) {
- if (new_procs[i]) {
- old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid);
- if (old_proc) {
- new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime;
- new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime;
- } else {
- new_procs[i]->delta_utime = 0;
- new_procs[i]->delta_stime = 0;
- }
- new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime;
- }
- }
-
- total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime
- + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime)
- - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime
- + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime);
-
- qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp);
-
- printf("\n\n\n");
- printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n",
- ((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100 / total_delta_time,
- ((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time,
- ((new_cpu.iowtime) - (old_cpu.iowtime)) * 100 / total_delta_time,
- ((new_cpu.irqtime + new_cpu.sirqtime)
- - (old_cpu.irqtime + old_cpu.sirqtime)) * 100 / total_delta_time);
- printf("User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = %ld\n",
- new_cpu.utime - old_cpu.utime,
- new_cpu.ntime - old_cpu.ntime,
- new_cpu.stime - old_cpu.stime,
- new_cpu.itime - old_cpu.itime,
- new_cpu.iowtime - old_cpu.iowtime,
- new_cpu.irqtime - old_cpu.irqtime,
- new_cpu.sirqtime - old_cpu.sirqtime,
- total_delta_time);
- printf("\n");
- if (!threads)
- printf("%5s %2s %4s %1s %5s %7s %7s %3s %-8s %s\n", "PID", "PR", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "UID", "Name");
- else
- printf("%5s %5s %2s %4s %1s %7s %7s %3s %-8s %-15s %s\n", "PID", "TID", "PR", "CPU%", "S", "VSS", "RSS", "PCY", "UID", "Thread", "Proc");
-
- for (i = 0; i < num_new_procs; i++) {
- proc = new_procs[i];
-
- if (!proc || (max_procs && (i >= max_procs)))
- break;
- user = getpwuid(proc->uid);
- if (user && user->pw_name) {
- user_str = user->pw_name;
- } else {
- snprintf(user_buf, 20, "%d", proc->uid);
- user_str = user_buf;
- }
- if (!threads) {
- printf("%5d %2d %3" PRIu64 "%% %c %5d %6" PRIu64 "K %6" PRIu64 "K %3s %-8.8s %s\n",
- proc->pid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads,
- proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->name[0] != 0 ? proc->name : proc->tname);
- } else {
- printf("%5d %5d %2d %3" PRIu64 "%% %c %6" PRIu64 "K %6" PRIu64 "K %3s %-8.8s %-15s %s\n",
- proc->pid, proc->tid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state,
- proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->tname, proc->name);
- }
- }
-}
-
-static struct proc_info *find_old_proc(pid_t pid, pid_t tid) {
- int i;
-
- for (i = 0; i < num_old_procs; i++)
- if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid))
- return old_procs[i];
-
- return NULL;
-}
-
-static void free_old_procs(void) {
- int i;
-
- for (i = 0; i < num_old_procs; i++)
- if (old_procs[i])
- free_proc(old_procs[i]);
-
- free(old_procs);
-}
-
-static int proc_cpu_cmp(const void *a, const void *b) {
- struct proc_info *pa, *pb;
-
- pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
-
- if (!pa && !pb) return 0;
- if (!pa) return 1;
- if (!pb) return -1;
-
- return -numcmp(pa->delta_time, pb->delta_time);
-}
-
-static int proc_vss_cmp(const void *a, const void *b) {
- struct proc_info *pa, *pb;
-
- pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
-
- if (!pa && !pb) return 0;
- if (!pa) return 1;
- if (!pb) return -1;
-
- return -numcmp(pa->vss, pb->vss);
-}
-
-static int proc_rss_cmp(const void *a, const void *b) {
- struct proc_info *pa, *pb;
-
- pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
-
- if (!pa && !pb) return 0;
- if (!pa) return 1;
- if (!pb) return -1;
-
- return -numcmp(pa->rss, pb->rss);
-}
-
-static int proc_thr_cmp(const void *a, const void *b) {
- struct proc_info *pa, *pb;
-
- pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
-
- if (!pa && !pb) return 0;
- if (!pa) return 1;
- if (!pb) return -1;
-
- return -numcmp(pa->num_threads, pb->num_threads);
-}
-
-static int numcmp(long long a, long long b) {
- if (a < b) return -1;
- if (a > b) return 1;
- return 0;
-}
-
-static void usage(char *cmd) {
- fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]\n"
- " -m num Maximum number of processes to display.\n"
- " -n num Updates to show before exiting.\n"
- " -d num Seconds to wait between updates.\n"
- " -s col Column to sort by (cpu,vss,rss,thr).\n"
- " -t Show threads instead of processes.\n"
- " -h Display this help screen.\n",
- cmd);
-}
diff --git a/crash_reporter/MODULE_LICENSE_BSD b/toolbox/upstream-netbsd/include/util.h
similarity index 100%
rename from crash_reporter/MODULE_LICENSE_BSD
rename to toolbox/upstream-netbsd/include/util.h
diff --git a/toolbox/upstream-netbsd/usr.bin/du/du.c b/toolbox/upstream-netbsd/usr.bin/du/du.c
deleted file mode 100644
index 086ac4a..0000000
--- a/toolbox/upstream-netbsd/usr.bin/du/du.c
+++ /dev/null
@@ -1,364 +0,0 @@
-/* $NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $ */
-
-/*
- * Copyright (c) 1989, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Newcomb.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <sys/cdefs.h>
-#ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
- The Regents of the University of California. All rights reserved.");
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <fts.h>
-#include <inttypes.h>
-#include <util.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-
-/* Count inodes or file size */
-#define COUNT (iflag ? 1 : p->fts_statp->st_blocks)
-
-static int linkchk(dev_t, ino_t);
-static void prstat(const char *, int64_t);
-__dead static void usage(void);
-
-static int hflag, iflag;
-static long blocksize;
-
-int
-main(int argc, char *argv[])
-{
- FTS *fts;
- FTSENT *p;
- int64_t totalblocks;
- int ftsoptions, listfiles;
- int depth;
- int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag;
- const char *noargv[2];
-
- Hflag = Lflag = aflag = cflag = dflag = gkmflag = nflag = sflag = 0;
- totalblocks = 0;
- ftsoptions = FTS_PHYSICAL;
- depth = INT_MAX;
- while ((ch = getopt(argc, argv, "HLPacd:ghikmnrsx")) != -1)
- switch (ch) {
- case 'H':
- Hflag = 1;
- Lflag = 0;
- break;
- case 'L':
- Lflag = 1;
- Hflag = 0;
- break;
- case 'P':
- Hflag = Lflag = 0;
- break;
- case 'a':
- aflag = 1;
- break;
- case 'c':
- cflag = 1;
- break;
- case 'd':
- dflag = 1;
- depth = atoi(optarg);
- if (depth < 0 || depth > SHRT_MAX) {
- warnx("invalid argument to option d: %s",
- optarg);
- usage();
- }
- break;
- case 'g':
- blocksize = 1024 * 1024 * 1024;
- gkmflag = 1;
- break;
- case 'h':
- hflag = 1;
- break;
- case 'i':
- iflag = 1;
- break;
- case 'k':
- blocksize = 1024;
- gkmflag = 1;
- break;
- case 'm':
- blocksize = 1024 * 1024;
- gkmflag = 1;
- break;
- case 'n':
- nflag = 1;
- break;
- case 'r':
- break;
- case 's':
- sflag = 1;
- break;
- case 'x':
- ftsoptions |= FTS_XDEV;
- break;
- case '?':
- default:
- usage();
- }
- argc -= optind;
- argv += optind;
-
- /*
- * XXX
- * Because of the way that fts(3) works, logical walks will not count
- * the blocks actually used by symbolic links. We rationalize this by
- * noting that users computing logical sizes are likely to do logical
- * copies, so not counting the links is correct. The real reason is
- * that we'd have to re-implement the kernel's symbolic link traversing
- * algorithm to get this right. If, for example, you have relative
- * symbolic links referencing other relative symbolic links, it gets
- * very nasty, very fast. The bottom line is that it's documented in
- * the man page, so it's a feature.
- */
- if (Hflag)
- ftsoptions |= FTS_COMFOLLOW;
- if (Lflag) {
- ftsoptions &= ~FTS_PHYSICAL;
- ftsoptions |= FTS_LOGICAL;
- }
-
- listfiles = 0;
- if (aflag) {
- if (sflag || dflag)
- usage();
- listfiles = 1;
- } else if (sflag) {
- if (dflag)
- usage();
- depth = 0;
- }
-
- if (!*argv) {
- noargv[0] = ".";
- noargv[1] = NULL;
- argv = __UNCONST(noargv);
- }
-
- if (!gkmflag)
- (void)getbsize(NULL, &blocksize);
- blocksize /= 512;
-
- if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
- err(1, "fts_open `%s'", *argv);
-
- for (rval = 0; (p = fts_read(fts)) != NULL;) {
-#ifndef __ANDROID__
- if (nflag) {
- switch (p->fts_info) {
- case FTS_NS:
- case FTS_SLNONE:
- /* nothing */
- break;
- default:
- if (p->fts_statp->st_flags & UF_NODUMP) {
- fts_set(fts, p, FTS_SKIP);
- continue;
- }
- }
- }
-#endif
- switch (p->fts_info) {
- case FTS_D: /* Ignore. */
- break;
- case FTS_DP:
- p->fts_parent->fts_number +=
- p->fts_number += COUNT;
- if (cflag)
- totalblocks += COUNT;
- /*
- * If listing each directory, or not listing files
- * or directories and this is post-order of the
- * root of a traversal, display the total.
- */
- if (p->fts_level <= depth
- || (!listfiles && !p->fts_level))
- prstat(p->fts_path, p->fts_number);
- break;
- case FTS_DC: /* Ignore. */
- break;
- case FTS_DNR: /* Warn, continue. */
- case FTS_ERR:
- case FTS_NS:
- warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
- rval = 1;
- break;
- default:
- if (p->fts_statp->st_nlink > 1 &&
- linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino))
- break;
- /*
- * If listing each file, or a non-directory file was
- * the root of a traversal, display the total.
- */
- if (listfiles || !p->fts_level)
- prstat(p->fts_path, COUNT);
- p->fts_parent->fts_number += COUNT;
- if (cflag)
- totalblocks += COUNT;
- }
- }
- if (errno)
- err(1, "fts_read");
- if (cflag)
- prstat("total", totalblocks);
- exit(rval);
-}
-
-static void
-prstat(const char *fname, int64_t blocks)
-{
- if (iflag) {
- (void)printf("%" PRId64 "\t%s\n", blocks, fname);
- return;
- }
-
- if (hflag) {
- char buf[5];
- int64_t sz = blocks * 512;
-
- humanize_number(buf, sizeof(buf), sz, "", HN_AUTOSCALE,
- HN_B | HN_NOSPACE | HN_DECIMAL);
-
- (void)printf("%s\t%s\n", buf, fname);
- } else
- (void)printf("%" PRId64 "\t%s\n",
- howmany(blocks, (int64_t)blocksize),
- fname);
-}
-
-static int
-linkchk(dev_t dev, ino_t ino)
-{
- static struct entry {
- dev_t dev;
- ino_t ino;
- } *htable;
- static int htshift; /* log(allocated size) */
- static int htmask; /* allocated size - 1 */
- static int htused; /* 2*number of insertions */
- static int sawzero; /* Whether zero is in table or not */
- int h, h2;
- uint64_t tmp;
- /* this constant is (1<<64)/((1+sqrt(5))/2)
- * aka (word size)/(golden ratio)
- */
- const uint64_t HTCONST = 11400714819323198485ULL;
- const int HTBITS = CHAR_BIT * sizeof(tmp);
-
- /* Never store zero in hashtable */
- if (dev == 0 && ino == 0) {
- h = sawzero;
- sawzero = 1;
- return h;
- }
-
- /* Extend hash table if necessary, keep load under 0.5 */
- if (htused<<1 >= htmask) {
- struct entry *ohtable;
-
- if (!htable)
- htshift = 10; /* starting hashtable size */
- else
- htshift++; /* exponential hashtable growth */
-
- htmask = (1 << htshift) - 1;
- htused = 0;
-
- ohtable = htable;
- htable = calloc(htmask+1, sizeof(*htable));
- if (!htable)
- err(1, "calloc");
-
- /* populate newly allocated hashtable */
- if (ohtable) {
- int i;
- for (i = 0; i <= htmask>>1; i++)
- if (ohtable[i].ino || ohtable[i].dev)
- linkchk(ohtable[i].dev, ohtable[i].ino);
- free(ohtable);
- }
- }
-
- /* multiplicative hashing */
- tmp = dev;
- tmp <<= HTBITS>>1;
- tmp |= ino;
- tmp *= HTCONST;
- h = tmp >> (HTBITS - htshift);
- h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */
-
- /* open address hashtable search with double hash probing */
- while (htable[h].ino || htable[h].dev) {
- if ((htable[h].ino == ino) && (htable[h].dev == dev))
- return 1;
- h = (h + h2) & htmask;
- }
-
- /* Insert the current entry into hashtable */
- htable[h].dev = dev;
- htable[h].ino = ino;
- htused++;
- return 0;
-}
-
-static void
-usage(void)
-{
-
- (void)fprintf(stderr,
- "usage: du [-H | -L | -P] [-a | -d depth | -s] [-cghikmnrx] [file ...]\n");
- exit(1);
-}
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
deleted file mode 100644
index ebfb15e..0000000
--- a/toolbox/uptime.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2010, 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.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" 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 <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-
-static void format_time(int time, char* buffer) {
- int seconds = time % 60;
- time /= 60;
- int minutes = time % 60;
- time /= 60;
- int hours = time % 24;
- int days = time / 24;
-
- if (days > 0) {
- sprintf(buffer, "%d day%s, %02d:%02d:%02d", days, (days == 1) ? "" : "s", hours, minutes, seconds);
- } else {
- sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
- }
-}
-
-int uptime_main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
- FILE* file = fopen("/proc/uptime", "r");
- if (!file) {
- fprintf(stderr, "Could not open /proc/uptime\n");
- return -1;
- }
- float idle_time;
- if (fscanf(file, "%*f %f", &idle_time) != 1) {
- fprintf(stderr, "Could not parse /proc/uptime\n");
- fclose(file);
- return -1;
- }
- fclose(file);
-
- struct timespec up_timespec;
- if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) == -1) {
- fprintf(stderr, "Could not get monotonic time: %s\n", strerror(errno));
- return -1;
- }
- float up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9;
-
- struct timespec elapsed_timespec;
- if (clock_gettime(CLOCK_BOOTTIME, &elapsed_timespec) == -1) {
- fprintf(stderr, "Could not get boot time: %s\n", strerror(errno));
- return -1;
- }
- int elapsed = elapsed_timespec.tv_sec;
-
- char up_string[100], idle_string[100], sleep_string[100];
- format_time(elapsed, up_string);
- format_time((int)idle_time, idle_string);
- format_time((int)(elapsed - up_time), sleep_string);
- printf("up time: %s, idle time: %s, sleep time: %s\n", up_string, idle_string, sleep_string);
-
- return 0;
-}
diff --git a/trusty/gatekeeper/Android.mk b/trusty/gatekeeper/Android.mk
new file mode 100644
index 0000000..3982c8f
--- /dev/null
+++ b/trusty/gatekeeper/Android.mk
@@ -0,0 +1,46 @@
+#
+# 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.
+#
+
+# WARNING: Everything listed here will be built on ALL platforms,
+# including x86, the emulator, and the SDK. Modules must be uniquely
+# named (liblights.panda), and must build everywhere, or limit themselves
+# to only building on ARM if they include assembly. Individual makefiles
+# are responsible for having their own logic, for fine-grained control.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := gatekeeper.trusty
+
+LOCAL_MODULE_RELATIVE_PATH := hw
+
+LOCAL_SRC_FILES := \
+ module.cpp \
+ trusty_gatekeeper_ipc.c \
+ trusty_gatekeeper.cpp
+
+LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+ libgatekeeper \
+ liblog \
+ libcutils \
+ libtrusty
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/gatekeeper/gatekeeper_ipc.h b/trusty/gatekeeper/gatekeeper_ipc.h
new file mode 100644
index 0000000..b05dcd8
--- /dev/null
+++ b/trusty/gatekeeper/gatekeeper_ipc.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#define GATEKEEPER_PORT "com.android.trusty.gatekeeper"
+#define GATEKEEPER_MAX_BUFFER_LENGTH 1024
+
+enum gatekeeper_command {
+ GK_REQ_SHIFT = 1,
+ GK_RESP_BIT = 1,
+
+ GK_ENROLL = (0 << GK_REQ_SHIFT),
+ GK_VERIFY = (1 << GK_REQ_SHIFT),
+};
+
+/**
+ * gatekeeper_message - Serial header for communicating with GK server
+ * @cmd: the command, one of ENROLL, VERIFY. Payload must be a serialized
+ * buffer of the corresponding request object.
+ * @payload: start of the serialized command specific payload
+ */
+struct gatekeeper_message {
+ uint32_t cmd;
+ uint8_t payload[0];
+};
+
diff --git a/trusty/gatekeeper/module.cpp b/trusty/gatekeeper/module.cpp
new file mode 100644
index 0000000..0ee3c2f
--- /dev/null
+++ b/trusty/gatekeeper/module.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/hardware.h>
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "trusty_gatekeeper.h"
+
+using gatekeeper::TrustyGateKeeperDevice;
+
+static int trusty_gatekeeper_open(const hw_module_t *module, const char *name,
+ hw_device_t **device) {
+
+ if (strcmp(name, HARDWARE_GATEKEEPER) != 0) {
+ return -EINVAL;
+ }
+
+ TrustyGateKeeperDevice *gatekeeper = new TrustyGateKeeperDevice(module);
+ if (gatekeeper == NULL) return -ENOMEM;
+ *device = gatekeeper->hw_device();
+
+ return 0;
+}
+
+static struct hw_module_methods_t gatekeeper_module_methods = {
+ .open = trusty_gatekeeper_open,
+};
+
+struct gatekeeper_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = GATEKEEPER_MODULE_API_VERSION_0_1,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = GATEKEEPER_HARDWARE_MODULE_ID,
+ .name = "Trusty GateKeeper HAL",
+ .author = "The Android Open Source Project",
+ .methods = &gatekeeper_module_methods,
+ .dso = 0,
+ .reserved = {}
+ },
+};
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
new file mode 100644
index 0000000..b3fbfa9
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TrustyGateKeeper"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <type_traits>
+
+#include <log/log.h>
+
+#include "trusty_gatekeeper.h"
+#include "trusty_gatekeeper_ipc.h"
+#include "gatekeeper_ipc.h"
+
+namespace gatekeeper {
+
+const uint32_t SEND_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = 8192;
+
+TrustyGateKeeperDevice::TrustyGateKeeperDevice(const hw_module_t *module) {
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+ static_assert(std::is_standard_layout<TrustyGateKeeperDevice>::value,
+ "TrustyGateKeeperDevice must be standard layout");
+ static_assert(offsetof(TrustyGateKeeperDevice, device_) == 0,
+ "device_ must be the first member of TrustyGateKeeperDevice");
+ static_assert(offsetof(TrustyGateKeeperDevice, device_.common) == 0,
+ "common must be the first member of gatekeeper_device");
+#else
+ assert(reinterpret_cast<gatekeeper_device_t *>(this) == &device_);
+ assert(reinterpret_cast<hw_device_t *>(this) == &(device_.common));
+#endif
+
+ memset(&device_, 0, sizeof(device_));
+ device_.common.tag = HARDWARE_DEVICE_TAG;
+ device_.common.version = 1;
+ device_.common.module = const_cast<hw_module_t *>(module);
+ device_.common.close = close_device;
+
+ device_.enroll = enroll;
+ device_.verify = verify;
+ device_.delete_user = nullptr;
+ device_.delete_all_users = nullptr;
+
+ int rc = trusty_gatekeeper_connect();
+ if (rc < 0) {
+ ALOGE("Error initializing trusty session: %d", rc);
+ }
+
+ error_ = rc;
+
+}
+
+hw_device_t* TrustyGateKeeperDevice::hw_device() {
+ return &device_.common;
+}
+
+int TrustyGateKeeperDevice::close_device(hw_device_t* dev) {
+ delete reinterpret_cast<TrustyGateKeeperDevice *>(dev);
+ return 0;
+}
+
+TrustyGateKeeperDevice::~TrustyGateKeeperDevice() {
+ trusty_gatekeeper_disconnect();
+}
+
+int TrustyGateKeeperDevice::Enroll(uint32_t uid, const uint8_t *current_password_handle,
+ uint32_t current_password_handle_length, const uint8_t *current_password,
+ uint32_t current_password_length, const uint8_t *desired_password,
+ uint32_t desired_password_length, uint8_t **enrolled_password_handle,
+ uint32_t *enrolled_password_handle_length) {
+
+ if (error_ != 0) {
+ return error_;
+ }
+
+ SizedBuffer desired_password_buffer(desired_password_length);
+ memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
+
+ SizedBuffer current_password_handle_buffer(current_password_handle_length);
+ if (current_password_handle) {
+ memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
+ current_password_handle_length);
+ }
+
+ SizedBuffer current_password_buffer(current_password_length);
+ if (current_password) {
+ memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
+ }
+
+ EnrollRequest request(uid, ¤t_password_handle_buffer, &desired_password_buffer,
+ ¤t_password_buffer);
+ EnrollResponse response;
+
+ gatekeeper_error_t error = Send(request, &response);
+
+ if (error == ERROR_RETRY) {
+ return response.retry_timeout;
+ } else if (error != ERROR_NONE) {
+ return -EINVAL;
+ }
+
+ *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
+ *enrolled_password_handle_length = response.enrolled_password_handle.length;
+
+
+ return 0;
+}
+
+int TrustyGateKeeperDevice::Verify(uint32_t uid, uint64_t challenge,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+ if (error_ != 0) {
+ return error_;
+ }
+
+ SizedBuffer password_handle_buffer(enrolled_password_handle_length);
+ memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
+ enrolled_password_handle_length);
+ SizedBuffer provided_password_buffer(provided_password_length);
+ memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
+
+ VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
+ VerifyResponse response;
+
+ gatekeeper_error_t error = Send(request, &response);
+
+ if (error == ERROR_RETRY) {
+ return response.retry_timeout;
+ } else if (error != ERROR_NONE) {
+ return -EINVAL;
+ }
+
+ if (auth_token != NULL && auth_token_length != NULL) {
+ *auth_token = response.auth_token.buffer.release();
+ *auth_token_length = response.auth_token.length;
+ }
+
+ if (request_reenroll != NULL) {
+ *request_reenroll = response.request_reenroll;
+ }
+
+ return 0;
+}
+
+gatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,
+ GateKeeperMessage *response) {
+ uint32_t request_size = request.GetSerializedSize();
+ if (request_size > SEND_BUF_SIZE)
+ return ERROR_INVALID;
+ uint8_t send_buf[SEND_BUF_SIZE];
+ request.Serialize(send_buf, send_buf + request_size);
+
+ // Send it
+ uint8_t recv_buf[RECV_BUF_SIZE];
+ uint32_t response_size = RECV_BUF_SIZE;
+ int rc = trusty_gatekeeper_call(command, send_buf, request_size, recv_buf, &response_size);
+ if (rc < 0) {
+ ALOGE("error (%d) calling gatekeeper TA", rc);
+ return ERROR_INVALID;
+ }
+
+ const gatekeeper_message *msg = reinterpret_cast<gatekeeper_message *>(recv_buf);
+ const uint8_t *payload = msg->payload;
+
+ return response->Deserialize(payload, payload + response_size);
+}
+
+static inline TrustyGateKeeperDevice *convert_device(const gatekeeper_device *dev) {
+ return reinterpret_cast<TrustyGateKeeperDevice *>(const_cast<gatekeeper_device *>(dev));
+}
+
+/* static */
+int TrustyGateKeeperDevice::enroll(const struct gatekeeper_device *dev, uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
+
+ if (dev == NULL ||
+ enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
+ desired_password == NULL || desired_password_length == 0)
+ return -EINVAL;
+
+ // Current password and current password handle go together
+ if (current_password_handle == NULL || current_password_handle_length == 0 ||
+ current_password == NULL || current_password_length == 0) {
+ current_password_handle = NULL;
+ current_password_handle_length = 0;
+ current_password = NULL;
+ current_password_length = 0;
+ }
+
+ return convert_device(dev)->Enroll(uid, current_password_handle, current_password_handle_length,
+ current_password, current_password_length, desired_password, desired_password_length,
+ enrolled_password_handle, enrolled_password_handle_length);
+
+}
+
+/* static */
+int TrustyGateKeeperDevice::verify(const struct gatekeeper_device *dev, uint32_t uid,
+ uint64_t challenge, const uint8_t *enrolled_password_handle,
+ uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
+ uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
+ bool *request_reenroll) {
+
+ if (dev == NULL || enrolled_password_handle == NULL ||
+ provided_password == NULL) {
+ return -EINVAL;
+ }
+
+ return convert_device(dev)->Verify(uid, challenge, enrolled_password_handle,
+ enrolled_password_handle_length, provided_password, provided_password_length,
+ auth_token, auth_token_length, request_reenroll);
+}
+};
diff --git a/trusty/gatekeeper/trusty_gatekeeper.h b/trusty/gatekeeper/trusty_gatekeeper.h
new file mode 100644
index 0000000..2becc49
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TRUSTY_GATEKEEPER_H
+#define TRUSTY_GATEKEEPER_H
+
+#include <hardware/gatekeeper.h>
+#include <gatekeeper/gatekeeper_messages.h>
+
+#include "gatekeeper_ipc.h"
+
+namespace gatekeeper {
+
+class TrustyGateKeeperDevice {
+ public:
+
+ explicit TrustyGateKeeperDevice(const hw_module_t* module);
+ ~TrustyGateKeeperDevice();
+
+ hw_device_t* hw_device();
+
+ /**
+ * Enrolls password_payload, which should be derived from a user selected pin or password,
+ * with the authentication factor private key used only for enrolling authentication
+ * factor data.
+ *
+ * Returns: 0 on success or an error code less than 0 on error.
+ * On error, enrolled_password will not be allocated.
+ */
+ int Enroll(uint32_t uid, const uint8_t *current_password_handle,
+ uint32_t current_password_handle_length, const uint8_t *current_password,
+ uint32_t current_password_length, const uint8_t *desired_password,
+ uint32_t desired_password_length, uint8_t **enrolled_password_handle,
+ uint32_t *enrolled_password_handle_length);
+
+ /**
+ * Verifies provided_password matches expected_password after enrolling
+ * with the authentication factor private key.
+ *
+ * Implementations of this module may retain the result of this call
+ * to attest to the recency of authentication.
+ *
+ * On success, writes the address of a verification token to verification_token,
+ *
+ * Returns: 0 on success or an error code less than 0 on error
+ * On error, verification token will not be allocated
+ */
+ int Verify(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle,
+ uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
+ uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
+ bool *request_reenroll);
+
+ private:
+
+ gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
+ GateKeeperMessage* response);
+
+ gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
+ return Send(GK_ENROLL, request, response);
+ }
+
+ gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
+ return Send(GK_VERIFY, request, response);
+ }
+
+ // Static methods interfacing the HAL API with the TrustyGateKeeper device
+
+ /**
+ * Enrolls desired_password, which should be derived from a user selected pin or password,
+ * with the authentication factor private key used only for enrolling authentication
+ * factor data.
+ *
+ * If there was already a password enrolled, it should be provided in
+ * current_password_handle, along with the current password in current_password
+ * that should validate against current_password_handle.
+ *
+ * Returns: 0 on success or an error code less than 0 on error.
+ * On error, enrolled_password_handle will not be allocated.
+ */
+ static int enroll(const struct gatekeeper_device *dev, uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
+
+ /**
+ * Verifies provided_password matches enrolled_password_handle.
+ *
+ * Implementations of this module may retain the result of this call
+ * to attest to the recency of authentication.
+ *
+ * On success, writes the address of a verification token to auth_token,
+ * usable to attest password verification to other trusted services. Clients
+ * may pass NULL for this value.
+ *
+ * Returns: 0 on success or an error code less than 0 on error
+ * On error, verification token will not be allocated
+ */
+ static int verify(const struct gatekeeper_device *dev, uint32_t uid, uint64_t challenge,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
+
+ static int close_device(hw_device_t* dev);
+
+ gatekeeper_device device_;
+ int error_;
+
+};
+}
+
+#endif
+
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.c b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
new file mode 100644
index 0000000..f67944b
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TrustyGateKeeper"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <trusty/tipc.h>
+
+#include "trusty_gatekeeper_ipc.h"
+#include "gatekeeper_ipc.h"
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+static int handle_ = 0;
+
+int trusty_gatekeeper_connect() {
+ int rc = tipc_connect(TRUSTY_DEVICE_NAME, GATEKEEPER_PORT);
+ if (rc < 0) {
+ return rc;
+ }
+
+ handle_ = rc;
+ return 0;
+}
+
+int trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size) {
+ if (handle_ == 0) {
+ ALOGE("not connected\n");
+ return -EINVAL;
+ }
+
+ size_t msg_size = in_size + sizeof(struct gatekeeper_message);
+ struct gatekeeper_message *msg = malloc(msg_size);
+ msg->cmd = cmd;
+ memcpy(msg->payload, in, in_size);
+
+ ssize_t rc = write(handle_, msg, msg_size);
+ free(msg);
+
+ if (rc < 0) {
+ ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
+ GATEKEEPER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ rc = read(handle_, out, *out_size);
+ if (rc < 0) {
+ ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n",
+ cmd, GATEKEEPER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ if ((size_t) rc < sizeof(struct gatekeeper_message)) {
+ ALOGE("invalid response size (%d)\n", (int) rc);
+ return -EINVAL;
+ }
+
+ msg = (struct gatekeeper_message *) out;
+
+ if ((cmd | GK_RESP_BIT) != msg->cmd) {
+ ALOGE("invalid command (%d)\n", msg->cmd);
+ return -EINVAL;
+ }
+
+ *out_size = ((size_t) rc) - sizeof(struct gatekeeper_message);
+ return rc;
+}
+
+void trusty_gatekeeper_disconnect() {
+ if (handle_ != 0) {
+ tipc_close(handle_);
+ }
+}
+
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.h b/trusty/gatekeeper/trusty_gatekeeper_ipc.h
new file mode 100644
index 0000000..f8de7f8
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+__BEGIN_DECLS
+
+int trusty_gatekeeper_connect();
+int trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size);
+void trusty_gatekeeper_disconnect();
+
+__END_DECLS
diff --git a/trusty/keymaster/Android.mk b/trusty/keymaster/Android.mk
new file mode 100644
index 0000000..0ebf52d
--- /dev/null
+++ b/trusty/keymaster/Android.mk
@@ -0,0 +1,67 @@
+#
+# 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.
+#
+
+# WARNING: Everything listed here will be built on ALL platforms,
+# including x86, the emulator, and the SDK. Modules must be uniquely
+# named (liblights.panda), and must build everywhere, or limit themselves
+# to only building on ARM if they include assembly. Individual makefiles
+# are responsible for having their own logic, for fine-grained control.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+###
+# trusty_keymaster is a binary used only for on-device testing. It
+# runs Trusty Keymaster through a basic set of operations with RSA
+# and ECDSA keys.
+###
+LOCAL_MODULE := trusty_keymaster_tipc
+LOCAL_SRC_FILES := \
+ trusty_keymaster_device.cpp \
+ trusty_keymaster_ipc.c \
+ trusty_keymaster_main.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ libcutils \
+ libkeymaster1 \
+ libtrusty \
+ libkeymaster_messages \
+ liblog
+
+include $(BUILD_EXECUTABLE)
+
+###
+# keystore.trusty is the HAL used by keystore on Trusty devices.
+##
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := keystore.trusty
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := module.cpp \
+ trusty_keymaster_ipc.c \
+ trusty_keymaster_device.cpp
+LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ libkeymaster_messages \
+ libtrusty \
+ liblog \
+ libcutils
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/keymaster/Makefile b/trusty/keymaster/Makefile
new file mode 100644
index 0000000..f575381
--- /dev/null
+++ b/trusty/keymaster/Makefile
@@ -0,0 +1,199 @@
+#####
+# Local unit test Makefile
+#
+# This makefile builds and runs the trusty_keymaster unit tests locally on the development
+# machine, not on an Android device.
+#
+# To build and run these tests, one pre-requisite must be manually installed: BoringSSL.
+# This Makefile expects to find BoringSSL in a directory adjacent to $ANDROID_BUILD_TOP.
+# To get and build it, first install the Ninja build tool (e.g. apt-get install
+# ninja-build), then do:
+#
+# cd $ANDROID_BUILD_TOP/..
+# git clone https://boringssl.googlesource.com/boringssl
+# cd boringssl
+# mdkir build
+# cd build
+# cmake -GNinja ..
+# ninja
+#
+# Then return to $ANDROID_BUILD_TOP/system/keymaster and run "make".
+#####
+
+BASE=../../../..
+SUBS=system/core \
+ system/keymaster \
+ hardware/libhardware \
+ external/gtest
+GTEST=$(BASE)/external/gtest
+KM=$(BASE)/system/keymaster
+
+INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \
+ -I $(BASE)/libnativehelper/include/nativehelper \
+ -I ../tipc/include \
+ -I $(BASE)/system/keymaster \
+ -I $(GTEST) \
+ -I$(BASE)/../boringssl/include
+
+ifdef USE_CLANG
+CC=/usr/bin/clang
+CXX=/usr/bin/clang
+CLANG_TEST_DEFINE=-DKEYMASTER_CLANG_TEST_BUILD
+COMPILER_SPECIFIC_ARGS=-std=c++11 $(CLANG_TEST_DEFINE)
+else
+COMPILER_SPECIFIC_ARGS=-std=c++0x -fprofile-arcs
+endif
+
+CPPFLAGS=$(INCLUDES) -g -O0 -MD
+CXXFLAGS=-Wall -Werror -Wno-unused -Winit-self -Wpointer-arith -Wunused-parameter \
+ -Wmissing-declarations -ftest-coverage \
+ -Wno-deprecated-declarations -fno-exceptions -DKEYMASTER_NAME_TAGS \
+ $(COMPILER_SPECIFIC_ARGS)
+LDLIBS=-L$(BASE)/../boringssl/build/crypto -lcrypto -lpthread -lstdc++
+
+CPPSRCS=\
+ $(KM)/aead_mode_operation.cpp \
+ $(KM)/aes_key.cpp \
+ $(KM)/aes_operation.cpp \
+ $(KM)/android_keymaster.cpp \
+ $(KM)/android_keymaster_messages.cpp \
+ $(KM)/android_keymaster_messages_test.cpp \
+ $(KM)/android_keymaster_test.cpp \
+ $(KM)/android_keymaster_test_utils.cpp \
+ $(KM)/android_keymaster_utils.cpp \
+ $(KM)/asymmetric_key.cpp \
+ $(KM)/auth_encrypted_key_blob.cpp \
+ $(KM)/auth_encrypted_key_blob.cpp \
+ $(KM)/authorization_set.cpp \
+ $(KM)/authorization_set_test.cpp \
+ $(KM)/ec_key.cpp \
+ $(KM)/ec_keymaster0_key.cpp \
+ $(KM)/ecdsa_operation.cpp \
+ $(KM)/hmac_key.cpp \
+ $(KM)/hmac_operation.cpp \
+ $(KM)/integrity_assured_key_blob.cpp \
+ $(KM)/key.cpp \
+ $(KM)/key_blob_test.cpp \
+ $(KM)/keymaster0_engine.cpp \
+ $(KM)/logger.cpp \
+ $(KM)/ocb_utils.cpp \
+ $(KM)/openssl_err.cpp \
+ $(KM)/openssl_utils.cpp \
+ $(KM)/operation.cpp \
+ $(KM)/operation_table.cpp \
+ $(KM)/rsa_key.cpp \
+ $(KM)/rsa_keymaster0_key.cpp \
+ $(KM)/rsa_operation.cpp \
+ $(KM)/serializable.cpp \
+ $(KM)/soft_keymaster_context.cpp \
+ $(KM)/symmetric_key.cpp \
+ $(KM)/unencrypted_key_blob.cpp \
+ trusty_keymaster_device.cpp \
+ trusty_keymaster_device_test.cpp
+CCSRCS=$(GTEST)/src/gtest-all.cc
+CSRCS=ocb.c
+
+OBJS=$(CPPSRCS:.cpp=.o) $(CCSRCS:.cc=.o) $(CSRCS:.c=.o)
+DEPS=$(CPPSRCS:.cpp=.d) $(CCSRCS:.cc=.d) $(CSRCS:.c=.d)
+GCDA=$(CPPSRCS:.cpp=.gcda) $(CCSRCS:.cc=.gcda) $(CSRCS:.c=.gcda)
+GCNO=$(CPPSRCS:.cpp=.gcno) $(CCSRCS:.cc=.gcno) $(CSRCS:.c=.gcno)
+
+LINK.o=$(LINK.cc)
+
+BINARIES=trusty_keymaster_device_test
+
+ifdef TRUSTY
+BINARIES += trusty_keymaster_device_test
+endif # TRUSTY
+
+.PHONY: coverage memcheck massif clean run
+
+%.run: %
+ ./$<
+ touch $@
+
+run: $(BINARIES:=.run)
+
+coverage: coverage.info
+ genhtml coverage.info --output-directory coverage
+
+coverage.info: run
+ lcov --capture --directory=. --output-file coverage.info
+
+%.coverage : %
+ $(MAKE) clean && $(MAKE) $<
+ ./$<
+ lcov --capture --directory=. --output-file coverage.info
+ genhtml coverage.info --output-directory coverage
+
+#UNINIT_OPTS=--track-origins=yes
+UNINIT_OPTS=--undef-value-errors=no
+
+MEMCHECK_OPTS=--leak-check=full \
+ --show-reachable=yes \
+ --vgdb=full \
+ $(UNINIT_OPTS) \
+ --error-exitcode=1
+
+MASSIF_OPTS=--tool=massif \
+ --stacks=yes
+
+%.memcheck : %
+ valgrind $(MEMCHECK_OPTS) ./$< && \
+ touch $@
+
+%.massif : %
+ valgrind $(MASSIF_OPTS) --massif-out-file=$@ ./$<
+
+memcheck: $(BINARIES:=.memcheck)
+
+massif: $(BINARIES:=.massif)
+
+trusty_keymaster_device_test: trusty_keymaster_device_test.o \
+ trusty_keymaster_device.o \
+ $(KM)/aead_mode_operation.o \
+ $(KM)/aes_key.o \
+ $(KM)/aes_operation.o \
+ $(KM)/android_keymaster.o \
+ $(KM)/android_keymaster_messages.o \
+ $(KM)/android_keymaster_test_utils.o \
+ $(KM)/android_keymaster_utils.o \
+ $(KM)/asymmetric_key.o \
+ $(KM)/auth_encrypted_key_blob.o \
+ $(KM)/auth_encrypted_key_blob.o \
+ $(KM)/authorization_set.o \
+ $(KM)/ec_key.o \
+ $(KM)/ec_keymaster0_key.cpp \
+ $(KM)/ecdsa_operation.o \
+ $(KM)/hmac_key.o \
+ $(KM)/hmac_operation.o \
+ $(KM)/integrity_assured_key_blob.o \
+ $(KM)/key.o \
+ $(KM)/keymaster0_engine.o \
+ $(KM)/logger.o \
+ $(KM)/ocb.o \
+ $(KM)/ocb_utils.o \
+ $(KM)/openssl_err.o \
+ $(KM)/openssl_utils.o \
+ $(KM)/operation.o \
+ $(KM)/operation_table.o \
+ $(KM)/rsa_key.o \
+ $(KM)/rsa_keymaster0_key.o \
+ $(KM)/rsa_operation.o \
+ $(KM)/serializable.o \
+ $(KM)/soft_keymaster_context.o \
+ $(KM)/symmetric_key.o \
+ $(GTEST)/src/gtest-all.o
+
+$(GTEST)/src/gtest-all.o: CXXFLAGS:=$(subst -Wmissing-declarations,,$(CXXFLAGS))
+ocb.o: CFLAGS=$(CLANG_TEST_DEFINE)
+
+clean:
+ rm -f $(OBJS) $(DEPS) $(GCDA) $(GCNO) $(BINARIES) \
+ $(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=.massif) \
+ coverage.info
+ rm -rf coverage
+
+-include $(CPPSRCS:.cpp=.d)
+-include $(CCSRCS:.cc=.d)
+
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
new file mode 100644
index 0000000..48fa53d
--- /dev/null
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+#define KEYMASTER_PORT "com.android.trusty.keymaster"
+#define KEYMASTER_MAX_BUFFER_LENGTH 4096
+
+// Commands
+enum keymaster_command {
+ KEYMASTER_RESP_BIT = 1,
+ KEYMASTER_REQ_SHIFT = 1,
+
+ KM_GENERATE_KEY = (0 << KEYMASTER_REQ_SHIFT),
+ KM_BEGIN_OPERATION = (1 << KEYMASTER_REQ_SHIFT),
+ KM_UPDATE_OPERATION = (2 << KEYMASTER_REQ_SHIFT),
+ KM_FINISH_OPERATION = (3 << KEYMASTER_REQ_SHIFT),
+ KM_ABORT_OPERATION = (4 << KEYMASTER_REQ_SHIFT),
+ KM_IMPORT_KEY = (5 << KEYMASTER_REQ_SHIFT),
+ KM_EXPORT_KEY = (6 << KEYMASTER_REQ_SHIFT),
+ KM_GET_VERSION = (7 << KEYMASTER_REQ_SHIFT),
+ KM_ADD_RNG_ENTROPY = (8 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_ALGORITHMS = (9 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_BLOCK_MODES = (10 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_PADDING_MODES = (11 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_DIGESTS = (12 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),
+ KM_GET_KEY_CHARACTERISTICS = (15 << KEYMASTER_REQ_SHIFT),
+};
+
+#ifdef __ANDROID__
+
+/**
+ * keymaster_message - Serial header for communicating with KM server
+ * @cmd: the command, one of keymaster_command.
+ * @payload: start of the serialized command specific payload
+ */
+struct keymaster_message {
+ uint32_t cmd;
+ uint8_t payload[0];
+};
+
+#endif
diff --git a/trusty/keymaster/module.cpp b/trusty/keymaster/module.cpp
new file mode 100644
index 0000000..81597d9
--- /dev/null
+++ b/trusty/keymaster/module.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <string.h>
+
+#include <hardware/hardware.h>
+#include <hardware/keymaster0.h>
+
+#include "trusty_keymaster_device.h"
+
+using keymaster::TrustyKeymasterDevice;
+
+/*
+ * Generic device handling
+ */
+static int trusty_keymaster_open(const hw_module_t* module, const char* name,
+ hw_device_t** device) {
+ if (strcmp(name, KEYSTORE_KEYMASTER) != 0)
+ return -EINVAL;
+
+ TrustyKeymasterDevice* dev = new TrustyKeymasterDevice(module);
+ if (dev == NULL)
+ return -ENOMEM;
+ *device = dev->hw_device();
+ // Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
+ // exist until then.
+ return 0;
+}
+
+static struct hw_module_methods_t keystore_module_methods = {
+ .open = trusty_keymaster_open,
+};
+
+struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+ .common =
+ {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = KEYMASTER_MODULE_API_VERSION_0_3,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = KEYSTORE_HARDWARE_MODULE_ID,
+ .name = "Trusty Keymaster HAL",
+ .author = "The Android Open Source Project",
+ .methods = &keystore_module_methods,
+ .dso = 0,
+ .reserved = {},
+ },
+};
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
new file mode 100644
index 0000000..1368f88
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -0,0 +1,534 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TrustyKeymaster"
+
+#include <assert.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <type_traits>
+
+#include <hardware/keymaster0.h>
+#include <keymaster/authorization_set.h>
+#include <log/log.h>
+
+#include "trusty_keymaster_device.h"
+#include "trusty_keymaster_ipc.h"
+#include "keymaster_ipc.h"
+
+const uint32_t SEND_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = 8192;
+
+namespace keymaster {
+
+static keymaster_error_t translate_error(int err) {
+ switch (err) {
+ case 0:
+ return KM_ERROR_OK;
+ case -EPERM:
+ case -EACCES:
+ return KM_ERROR_SECURE_HW_ACCESS_DENIED;
+
+ case -ECANCELED:
+ return KM_ERROR_OPERATION_CANCELLED;
+
+ case -ENODEV:
+ return KM_ERROR_UNIMPLEMENTED;
+
+ case -ENOMEM:
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ case -EBUSY:
+ return KM_ERROR_SECURE_HW_BUSY;
+
+ case -EIO:
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+
+ case -EOVERFLOW:
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+
+ default:
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+}
+
+TrustyKeymasterDevice::TrustyKeymasterDevice(const hw_module_t* module) {
+ static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
+ "TrustyKeymasterDevice must be standard layout");
+ static_assert(offsetof(TrustyKeymasterDevice, device_) == 0,
+ "device_ must be the first member of KeymasterOpenSsl");
+ static_assert(offsetof(TrustyKeymasterDevice, device_.common) == 0,
+ "common must be the first member of keymaster_device");
+
+ ALOGI("Creating device");
+ ALOGD("Device address: %p", this);
+
+ memset(&device_, 0, sizeof(device_));
+
+ device_.common.tag = HARDWARE_DEVICE_TAG;
+ device_.common.version = 1;
+ device_.common.module = const_cast<hw_module_t*>(module);
+ device_.common.close = close_device;
+
+ device_.flags = KEYMASTER_BLOBS_ARE_STANDALONE | KEYMASTER_SUPPORTS_EC;
+
+ device_.generate_keypair = generate_keypair;
+ device_.import_keypair = import_keypair;
+ device_.get_keypair_public = get_keypair_public;
+ device_.delete_keypair = NULL;
+ device_.delete_all = NULL;
+ device_.sign_data = sign_data;
+ device_.verify_data = verify_data;
+
+ device_.context = NULL;
+
+ int rc = trusty_keymaster_connect();
+ error_ = translate_error(rc);
+ if (rc < 0) {
+ ALOGE("failed to connect to keymaster (%d)", rc);
+ return;
+ }
+
+ GetVersionRequest version_request;
+ GetVersionResponse version_response;
+ error_ = Send(version_request, &version_response);
+ if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
+ ALOGI("\"Bad parameters\" error on GetVersion call. Assuming version 0.");
+ message_version_ = 0;
+ error_ = KM_ERROR_OK;
+ }
+ message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
+ version_response.subminor_ver);
+ if (message_version_ < 0) {
+ // Can't translate version? Keymaster implementation must be newer.
+ ALOGE("Keymaster version %d.%d.%d not supported.", version_response.major_ver,
+ version_response.minor_ver, version_response.subminor_ver);
+ error_ = KM_ERROR_VERSION_MISMATCH;
+ }
+}
+
+TrustyKeymasterDevice::~TrustyKeymasterDevice() {
+ trusty_keymaster_disconnect();
+}
+
+const uint64_t HUNDRED_YEARS = 1000LL * 60 * 60 * 24 * 365 * 100;
+
+int TrustyKeymasterDevice::generate_keypair(const keymaster_keypair_t key_type,
+ const void* key_params, uint8_t** key_blob,
+ size_t* key_blob_length) {
+ ALOGD("Device received generate_keypair");
+
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ GenerateKeyRequest req(message_version_);
+ StoreNewKeyParams(&req.key_description);
+
+ switch (key_type) {
+ case TYPE_RSA: {
+ req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
+ const keymaster_rsa_keygen_params_t* rsa_params =
+ static_cast<const keymaster_rsa_keygen_params_t*>(key_params);
+ ALOGD("Generating RSA pair, modulus size: %u, public exponent: %lu",
+ rsa_params->modulus_size, rsa_params->public_exponent);
+ req.key_description.push_back(TAG_KEY_SIZE, rsa_params->modulus_size);
+ req.key_description.push_back(TAG_RSA_PUBLIC_EXPONENT, rsa_params->public_exponent);
+ break;
+ }
+
+ case TYPE_EC: {
+ req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_EC);
+ const keymaster_ec_keygen_params_t* ec_params =
+ static_cast<const keymaster_ec_keygen_params_t*>(key_params);
+ ALOGD("Generating ECDSA pair, key size: %u", ec_params->field_size);
+ req.key_description.push_back(TAG_KEY_SIZE, ec_params->field_size);
+ break;
+ }
+ default:
+ ALOGD("Received request for unsuported key type %d", key_type);
+ return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ }
+
+ GenerateKeyResponse rsp(message_version_);
+ ALOGD("Sending generate request");
+ keymaster_error_t err = Send(req, &rsp);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Got error %d from send", err);
+ return err;
+ }
+
+ *key_blob_length = rsp.key_blob.key_material_size;
+ *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
+ memcpy(*key_blob, rsp.key_blob.key_material, *key_blob_length);
+ ALOGD("Returning %d bytes in key blob\n", (int)*key_blob_length);
+
+ return KM_ERROR_OK;
+}
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+
+struct PKCS8_PRIV_KEY_INFO_Delete {
+ void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
+};
+
+int TrustyKeymasterDevice::import_keypair(const uint8_t* key, const size_t key_length,
+ uint8_t** key_blob, size_t* key_blob_length) {
+ ALOGD("Device received import_keypair");
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ if (!key)
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+
+ if (!key_blob || !key_blob_length)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ ImportKeyRequest request(message_version_);
+ StoreNewKeyParams(&request.key_description);
+ keymaster_algorithm_t algorithm;
+ keymaster_error_t err = GetPkcs8KeyAlgorithm(key, key_length, &algorithm);
+ if (err != KM_ERROR_OK)
+ return err;
+ request.key_description.push_back(TAG_ALGORITHM, algorithm);
+
+ request.SetKeyMaterial(key, key_length);
+ request.key_format = KM_KEY_FORMAT_PKCS8;
+ ImportKeyResponse response(message_version_);
+ err = Send(request, &response);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ *key_blob_length = response.key_blob.key_material_size;
+ *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
+ memcpy(*key_blob, response.key_blob.key_material, *key_blob_length);
+ printf("Returning %d bytes in key blob\n", (int)*key_blob_length);
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
+ keymaster_algorithm_t* algorithm) {
+ if (key == NULL) {
+ ALOGE("No key specified for import");
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+
+ UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> pkcs8(
+ d2i_PKCS8_PRIV_KEY_INFO(NULL, &key, key_length));
+ if (pkcs8.get() == NULL) {
+ ALOGE("Could not parse PKCS8 key blob");
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKCS82PKEY(pkcs8.get()));
+ if (pkey.get() == NULL) {
+ ALOGE("Could not extract key from PKCS8 key blob");
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA:
+ *algorithm = KM_ALGORITHM_RSA;
+ break;
+ case EVP_PKEY_EC:
+ *algorithm = KM_ALGORITHM_EC;
+ break;
+ default:
+ ALOGE("Unsupported algorithm %d", EVP_PKEY_type(pkey->type));
+ return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ }
+
+ return KM_ERROR_OK;
+}
+
+int TrustyKeymasterDevice::get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
+ uint8_t** x509_data, size_t* x509_data_length) {
+ ALOGD("Device received get_keypair_public");
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ ExportKeyRequest request(message_version_);
+ request.SetKeyMaterial(key_blob, key_blob_length);
+ request.key_format = KM_KEY_FORMAT_X509;
+ ExportKeyResponse response(message_version_);
+ keymaster_error_t err = Send(request, &response);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ *x509_data_length = response.key_data_length;
+ *x509_data = static_cast<uint8_t*>(malloc(*x509_data_length));
+ memcpy(*x509_data, response.key_data, *x509_data_length);
+ printf("Returning %d bytes in x509 key\n", (int)*x509_data_length);
+
+ return KM_ERROR_OK;
+}
+
+int TrustyKeymasterDevice::sign_data(const void* signing_params, const uint8_t* key_blob,
+ const size_t key_blob_length, const uint8_t* data,
+ const size_t data_length, uint8_t** signed_data,
+ size_t* signed_data_length) {
+ ALOGD("Device received sign_data, %d", error_);
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ BeginOperationRequest begin_request(message_version_);
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ begin_request.SetKeyMaterial(key_blob, key_blob_length);
+ keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
+ &begin_request.additional_params);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Error extracting signing params: %d", err);
+ return err;
+ }
+
+ BeginOperationResponse begin_response(message_version_);
+ ALOGD("Sending signing request begin");
+ err = Send(begin_request, &begin_response);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Error sending sign begin: %d", err);
+ return err;
+ }
+
+ UpdateOperationRequest update_request(message_version_);
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(data, data_length);
+ UpdateOperationResponse update_response(message_version_);
+ ALOGD("Sending signing request update");
+ err = Send(update_request, &update_response);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Error sending sign update: %d", err);
+ return err;
+ }
+
+ FinishOperationRequest finish_request(message_version_);
+ finish_request.op_handle = begin_response.op_handle;
+ FinishOperationResponse finish_response(message_version_);
+ ALOGD("Sending signing request finish");
+ err = Send(finish_request, &finish_response);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Error sending sign finish: %d", err);
+ return err;
+ }
+
+ *signed_data_length = finish_response.output.available_read();
+ *signed_data = static_cast<uint8_t*>(malloc(*signed_data_length));
+ if (!finish_response.output.read(*signed_data, *signed_data_length)) {
+ ALOGE("Error reading response data: %d", err);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ return KM_ERROR_OK;
+}
+
+int TrustyKeymasterDevice::verify_data(const void* signing_params, const uint8_t* key_blob,
+ const size_t key_blob_length, const uint8_t* signed_data,
+ const size_t signed_data_length, const uint8_t* signature,
+ const size_t signature_length) {
+ ALOGD("Device received verify_data");
+ if (error_ != KM_ERROR_OK)
+ return error_;
+
+ BeginOperationRequest begin_request(message_version_);
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ begin_request.SetKeyMaterial(key_blob, key_blob_length);
+ keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
+ &begin_request.additional_params);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ BeginOperationResponse begin_response(message_version_);
+ err = Send(begin_request, &begin_response);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ UpdateOperationRequest update_request(message_version_);
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(signed_data, signed_data_length);
+ UpdateOperationResponse update_response(message_version_);
+ err = Send(update_request, &update_response);
+ if (err != KM_ERROR_OK)
+ return err;
+
+ FinishOperationRequest finish_request(message_version_);
+ finish_request.op_handle = begin_response.op_handle;
+ finish_request.signature.Reinitialize(signature, signature_length);
+ FinishOperationResponse finish_response(message_version_);
+ err = Send(finish_request, &finish_response);
+ if (err != KM_ERROR_OK)
+ return err;
+ return KM_ERROR_OK;
+}
+
+hw_device_t* TrustyKeymasterDevice::hw_device() {
+ return &device_.common;
+}
+
+static inline TrustyKeymasterDevice* convert_device(const keymaster0_device_t* dev) {
+ return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster0_device_t*>(dev));
+}
+
+/* static */
+int TrustyKeymasterDevice::close_device(hw_device_t* dev) {
+ delete reinterpret_cast<TrustyKeymasterDevice*>(dev);
+ return 0;
+}
+
+/* static */
+int TrustyKeymasterDevice::generate_keypair(const keymaster0_device_t* dev,
+ const keymaster_keypair_t key_type,
+ const void* key_params, uint8_t** keyBlob,
+ size_t* keyBlobLength) {
+ ALOGD("Generate keypair, sending to device: %p", convert_device(dev));
+ return convert_device(dev)->generate_keypair(key_type, key_params, keyBlob, keyBlobLength);
+}
+
+/* static */
+int TrustyKeymasterDevice::import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
+ const size_t key_length, uint8_t** key_blob,
+ size_t* key_blob_length) {
+ return convert_device(dev)->import_keypair(key, key_length, key_blob, key_blob_length);
+}
+
+/* static */
+int TrustyKeymasterDevice::get_keypair_public(const keymaster0_device_t* dev,
+ const uint8_t* key_blob, const size_t key_blob_length,
+ uint8_t** x509_data, size_t* x509_data_length) {
+ return convert_device(dev)
+ ->get_keypair_public(key_blob, key_blob_length, x509_data, x509_data_length);
+}
+
+/* static */
+int TrustyKeymasterDevice::sign_data(const keymaster0_device_t* dev, const void* params,
+ const uint8_t* keyBlob, const size_t keyBlobLength,
+ const uint8_t* data, const size_t dataLength,
+ uint8_t** signedData, size_t* signedDataLength) {
+ return convert_device(dev)
+ ->sign_data(params, keyBlob, keyBlobLength, data, dataLength, signedData, signedDataLength);
+}
+
+/* static */
+int TrustyKeymasterDevice::verify_data(const keymaster0_device_t* dev, const void* params,
+ const uint8_t* keyBlob, const size_t keyBlobLength,
+ const uint8_t* signedData, const size_t signedDataLength,
+ const uint8_t* signature, const size_t signatureLength) {
+ return convert_device(dev)->verify_data(params, keyBlob, keyBlobLength, signedData,
+ signedDataLength, signature, signatureLength);
+}
+
+keymaster_error_t TrustyKeymasterDevice::Send(uint32_t command, const Serializable& req,
+ KeymasterResponse* rsp) {
+ uint32_t req_size = req.SerializedSize();
+ if (req_size > SEND_BUF_SIZE)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ uint8_t send_buf[SEND_BUF_SIZE];
+ Eraser send_buf_eraser(send_buf, SEND_BUF_SIZE);
+ req.Serialize(send_buf, send_buf + req_size);
+
+ // Send it
+ uint8_t recv_buf[RECV_BUF_SIZE];
+ Eraser recv_buf_eraser(recv_buf, RECV_BUF_SIZE);
+ uint32_t rsp_size = RECV_BUF_SIZE;
+ printf("Sending %d byte request\n", (int)req.SerializedSize());
+ int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
+ if (rc < 0) {
+ ALOGE("tipc error: %d\n", rc);
+ // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
+ return translate_error(rc);
+ } else {
+ ALOGV("Received %d byte response\n", rsp_size);
+ }
+
+ const keymaster_message* msg = (keymaster_message *) recv_buf;
+ const uint8_t *p = msg->payload;
+ if (!rsp->Deserialize(&p, p + rsp_size)) {
+ ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
+ return KM_ERROR_UNKNOWN_ERROR;
+ } else if (rsp->error != KM_ERROR_OK) {
+ ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
+ return rsp->error;
+ }
+ return rsp->error;
+}
+
+keymaster_error_t TrustyKeymasterDevice::StoreSigningParams(const void* signing_params,
+ const uint8_t* key_blob,
+ size_t key_blob_length,
+ AuthorizationSet* auth_set) {
+ uint8_t* pub_key_data;
+ size_t pub_key_data_length;
+ int err = get_keypair_public(&device_, key_blob, key_blob_length, &pub_key_data,
+ &pub_key_data_length);
+ if (err < 0) {
+ ALOGE("Error %d extracting public key to determine algorithm", err);
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+ UniquePtr<uint8_t, Malloc_Delete> pub_key(pub_key_data);
+
+ const uint8_t* p = pub_key_data;
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(nullptr /* allocate new struct */, &p, pub_key_data_length));
+
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA: {
+ const keymaster_rsa_sign_params_t* rsa_params =
+ reinterpret_cast<const keymaster_rsa_sign_params_t*>(signing_params);
+ if (rsa_params->digest_type != DIGEST_NONE)
+ return KM_ERROR_UNSUPPORTED_DIGEST;
+ if (rsa_params->padding_type != PADDING_NONE)
+ return KM_ERROR_UNSUPPORTED_PADDING_MODE;
+ if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE) ||
+ !auth_set->push_back(TAG_PADDING, KM_PAD_NONE))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ } break;
+ case EVP_PKEY_EC: {
+ const keymaster_ec_sign_params_t* ecdsa_params =
+ reinterpret_cast<const keymaster_ec_sign_params_t*>(signing_params);
+ if (ecdsa_params->digest_type != DIGEST_NONE)
+ return KM_ERROR_UNSUPPORTED_DIGEST;
+ if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ } break;
+ default:
+ return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ }
+ return KM_ERROR_OK;
+}
+
+void TrustyKeymasterDevice::StoreNewKeyParams(AuthorizationSet* auth_set) {
+ auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN);
+ auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY);
+ auth_set->push_back(TAG_ALL_USERS);
+ auth_set->push_back(TAG_NO_AUTH_REQUIRED);
+ uint64_t now = java_time(time(NULL));
+ auth_set->push_back(TAG_CREATION_DATETIME, now);
+ auth_set->push_back(TAG_ORIGINATION_EXPIRE_DATETIME, now + HUNDRED_YEARS);
+ if (message_version_ == 0) {
+ auth_set->push_back(TAG_DIGEST_OLD, KM_DIGEST_NONE);
+ auth_set->push_back(TAG_PADDING_OLD, KM_PAD_NONE);
+ } else {
+ auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ auth_set->push_back(TAG_PADDING, KM_PAD_NONE);
+ }
+}
+
+} // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_device.h b/trusty/keymaster/trusty_keymaster_device.h
new file mode 100644
index 0000000..68cf40c
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_device.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#define EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+
+#include <hardware/keymaster0.h>
+
+#include <keymaster/android_keymaster_messages.h>
+
+#include "keymaster_ipc.h"
+
+namespace keymaster {
+
+/**
+ * Software OpenSSL-based Keymaster device.
+ *
+ * IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t
+ * and keymaster_device. This means it must remain a standard layout class (no virtual functions and
+ * no data members which aren't standard layout), and device_ must be the first data member.
+ * Assertions in the constructor validate compliance with those constraints.
+ */
+class TrustyKeymasterDevice {
+ public:
+ /*
+ * These are the only symbols that will be exported by libtrustykeymaster. All functionality
+ * can be reached via the function pointers in device_.
+ */
+ __attribute__((visibility("default"))) explicit TrustyKeymasterDevice(const hw_module_t* module);
+ __attribute__((visibility("default"))) hw_device_t* hw_device();
+
+ ~TrustyKeymasterDevice();
+
+ keymaster_error_t session_error() { return error_; }
+
+ int generate_keypair(const keymaster_keypair_t key_type, const void* key_params,
+ uint8_t** key_blob, size_t* key_blob_length);
+ int import_keypair(const uint8_t* key, const size_t key_length, uint8_t** key_blob,
+ size_t* key_blob_length);
+ int get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
+ uint8_t** x509_data, size_t* x509_data_length);
+ int sign_data(const void* signing_params, const uint8_t* key_blob, const size_t key_blob_length,
+ const uint8_t* data, const size_t data_length, uint8_t** signed_data,
+ size_t* signed_data_length);
+ int verify_data(const void* signing_params, const uint8_t* key_blob,
+ const size_t key_blob_length, const uint8_t* signed_data,
+ const size_t signed_data_length, const uint8_t* signature,
+ const size_t signature_length);
+
+ private:
+ keymaster_error_t Send(uint32_t command, const Serializable& request,
+ KeymasterResponse* response);
+ keymaster_error_t Send(const GenerateKeyRequest& request, GenerateKeyResponse* response) {
+ return Send(KM_GENERATE_KEY, request, response);
+ }
+ keymaster_error_t Send(const BeginOperationRequest& request, BeginOperationResponse* response) {
+ return Send(KM_BEGIN_OPERATION, request, response);
+ }
+ keymaster_error_t Send(const UpdateOperationRequest& request,
+ UpdateOperationResponse* response) {
+ return Send(KM_UPDATE_OPERATION, request, response);
+ }
+ keymaster_error_t Send(const FinishOperationRequest& request,
+ FinishOperationResponse* response) {
+ return Send(KM_FINISH_OPERATION, request, response);
+ }
+ keymaster_error_t Send(const ImportKeyRequest& request, ImportKeyResponse* response) {
+ return Send(KM_IMPORT_KEY, request, response);
+ }
+ keymaster_error_t Send(const ExportKeyRequest& request, ExportKeyResponse* response) {
+ return Send(KM_EXPORT_KEY, request, response);
+ }
+ keymaster_error_t Send(const GetVersionRequest& request, GetVersionResponse* response) {
+ return Send(KM_GET_VERSION, request, response);
+ }
+
+ keymaster_error_t StoreSigningParams(const void* signing_params, const uint8_t* key_blob,
+ size_t key_blob_length, AuthorizationSet* auth_set);
+ void StoreNewKeyParams(AuthorizationSet* auth_set);
+ keymaster_error_t GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
+ keymaster_algorithm_t* algorithm);
+
+ /*
+ * These static methods are the functions referenced through the function pointers in
+ * keymaster_device. They're all trivial wrappers.
+ */
+ static int close_device(hw_device_t* dev);
+ static int generate_keypair(const keymaster0_device_t* dev, const keymaster_keypair_t key_type,
+ const void* key_params, uint8_t** keyBlob, size_t* keyBlobLength);
+ static int import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
+ const size_t key_length, uint8_t** key_blob, size_t* key_blob_length);
+ static int get_keypair_public(const keymaster0_device_t* dev, const uint8_t* key_blob,
+ const size_t key_blob_length, uint8_t** x509_data,
+ size_t* x509_data_length);
+ static int sign_data(const keymaster0_device_t* dev, const void* signing_params,
+ const uint8_t* key_blob, const size_t key_blob_length, const uint8_t* data,
+ const size_t data_length, uint8_t** signed_data,
+ size_t* signed_data_length);
+ static int verify_data(const keymaster0_device_t* dev, const void* signing_params,
+ const uint8_t* key_blob, const size_t key_blob_length,
+ const uint8_t* signed_data, const size_t signed_data_length,
+ const uint8_t* signature, const size_t signature_length);
+
+ keymaster0_device_t device_;
+ keymaster_error_t error_;
+ int32_t message_version_;
+};
+
+} // namespace keymaster
+
+#endif // EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp
new file mode 100644
index 0000000..3bb5430
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_device_test.cpp
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <algorithm>
+#include <fstream>
+
+#include <UniquePtr.h>
+#include <gtest/gtest.h>
+#include <openssl/engine.h>
+
+#include <hardware/keymaster0.h>
+
+#include <keymaster/android_keymaster.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+#include <keymaster/soft_keymaster_context.h>
+
+#include "android_keymaster_test_utils.h"
+#include "trusty_keymaster_device.h"
+#include "openssl_utils.h"
+
+using std::string;
+using std::ifstream;
+using std::istreambuf_iterator;
+
+static keymaster::AndroidKeymaster *impl_ = nullptr;
+
+extern "C" {
+int __android_log_print();
+}
+
+int __android_log_print() {
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
+ return result;
+}
+
+int trusty_keymaster_connect() {
+ impl_ = new keymaster::AndroidKeymaster(new keymaster::SoftKeymasterContext(nullptr), 16);
+}
+
+void trusty_keymaster_disconnect() {
+ delete static_cast<keymaster::AndroidKeymaster*>(priv_);
+}
+
+template <typename Req, typename Rsp>
+static int fake_call(keymaster::AndroidKeymaster* device,
+ void (keymaster::AndroidKeymaster::*method)(const Req&, Rsp*), void* in_buf,
+ uint32_t in_size, void* out_buf, uint32_t* out_size) {
+ Req req;
+ const uint8_t* in = static_cast<uint8_t*>(in_buf);
+ req.Deserialize(&in, in + in_size);
+ Rsp rsp;
+ (device->*method)(req, &rsp);
+
+ *out_size = rsp.SerializedSize();
+ uint8_t* out = static_cast<uint8_t*>(out_buf);
+ rsp.Serialize(out, out + *out_size);
+ return 0;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void* in_buf, uint32_t in_size, void* out_buf,
+ uint32_t* out_size) {
+ switch (cmd) {
+ case KM_GENERATE_KEY:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::GenerateKey, in_buf, in_size,
+ out_buf, out_size);
+ case KM_BEGIN_OPERATION:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::BeginOperation, in_buf, in_size,
+ out_buf, out_size);
+ case KM_UPDATE_OPERATION:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::UpdateOperation, in_buf, in_size,
+ out_buf, out_size);
+ case KM_FINISH_OPERATION:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::FinishOperation, in_buf, in_size,
+ out_buf, out_size);
+ case KM_IMPORT_KEY:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::ImportKey, in_buf, in_size, out_buf,
+ out_size);
+ case KM_EXPORT_KEY:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::ExportKey, in_buf, in_size, out_buf,
+ out_size);
+ }
+ return -EINVAL;
+
+}
+
+namespace keymaster {
+namespace test {
+
+class TrustyKeymasterTest : public testing::Test {
+ protected:
+ TrustyKeymasterTest() : device(NULL) {}
+
+ keymaster_rsa_keygen_params_t build_rsa_params() {
+ keymaster_rsa_keygen_params_t rsa_params;
+ rsa_params.public_exponent = 65537;
+ rsa_params.modulus_size = 2048;
+ return rsa_params;
+ }
+
+ uint8_t* build_message(size_t length) {
+ uint8_t* msg = new uint8_t[length];
+ memset(msg, 'a', length);
+ return msg;
+ }
+
+ size_t dsa_message_len(const keymaster_dsa_keygen_params_t& params) {
+ switch (params.key_size) {
+ case 256:
+ case 1024:
+ return 48;
+ case 2048:
+ case 4096:
+ return 72;
+ default:
+ // Oops.
+ return 0;
+ }
+ }
+
+ TrustyKeymasterDevice device;
+};
+
+class Malloc_Delete {
+ public:
+ Malloc_Delete(void* p) : p_(p) {}
+ ~Malloc_Delete() { free(p_); }
+
+ private:
+ void* p_;
+};
+
+typedef TrustyKeymasterTest KeyGenTest;
+TEST_F(KeyGenTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+}
+
+TEST_F(KeyGenTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t ec_params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &ec_params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+}
+
+typedef TrustyKeymasterTest SigningTest;
+TEST_F(SigningTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(message_len, siglen);
+}
+
+TEST_F(SigningTest, RsaShortMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8 - 1;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
+ message_len, &signature, &siglen));
+}
+
+TEST_F(SigningTest, RsaLongMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8 + 1;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
+ message_len, &signature, &siglen));
+}
+
+TEST_F(SigningTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_GT(siglen, 69U);
+ EXPECT_LT(siglen, 73U);
+}
+
+TEST_F(SigningTest, EcdsaEmptyMessageSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_GT(siglen, 69U);
+ EXPECT_LT(siglen, 73U);
+}
+
+TEST_F(SigningTest, EcdsaLargeMessageSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ size_t message_len = 1024 * 7;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ // contents of message don't matter.
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_GT(siglen, 69U);
+ EXPECT_LT(siglen, 73U);
+}
+
+typedef TrustyKeymasterTest VerificationTest;
+TEST_F(VerificationTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
+ signature, siglen));
+}
+
+TEST_F(VerificationTest, RsaBadSignature) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+
+ Malloc_Delete sig_deleter(signature);
+ signature[siglen / 2]++;
+ EXPECT_EQ(
+ KM_ERROR_VERIFICATION_FAILED,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature, siglen));
+}
+
+TEST_F(VerificationTest, RsaBadMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ message[0]++;
+ EXPECT_EQ(
+ KM_ERROR_VERIFICATION_FAILED,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature, siglen));
+}
+
+TEST_F(VerificationTest, RsaShortMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len - 1, signature,
+ siglen));
+}
+
+TEST_F(VerificationTest, RsaLongMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len + 1));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len + 1, signature,
+ siglen));
+}
+
+TEST_F(VerificationTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, signature, siglen));
+}
+
+TEST_F(VerificationTest, EcdsaLargeMessageSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ size_t message_len = 1024 * 7;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ // contents of message don't matter.
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
+ signature, siglen));
+}
+
+static string read_file(const string& file_name) {
+ ifstream file_stream(file_name, std::ios::binary);
+ istreambuf_iterator<char> file_begin(file_stream);
+ istreambuf_iterator<char> file_end;
+ return string(file_begin, file_end);
+}
+
+typedef TrustyKeymasterTest ImportKeyTest;
+TEST_F(ImportKeyTest, RsaSuccess) {
+ string pk8_key = read_file("../../../../system/keymaster/rsa_privkey_pk8.der");
+ ASSERT_EQ(633U, pk8_key.size());
+
+ uint8_t* key = NULL;
+ size_t size;
+ ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
+ pk8_key.size(), &key, &size));
+ Malloc_Delete key_deleter(key);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_size = 1024 /* key size */ / 8;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_size]);
+ memset(message.get(), 'a', message_size);
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message.get(), message_size,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message.get(), message_size,
+ signature, siglen));
+}
+
+TEST_F(ImportKeyTest, EcdsaSuccess) {
+ string pk8_key = read_file("../../../../system/keymaster/ec_privkey_pk8.der");
+ ASSERT_EQ(138U, pk8_key.size());
+
+ uint8_t* key = NULL;
+ size_t size;
+ ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
+ pk8_key.size(), &key, &size));
+ Malloc_Delete key_deleter(key);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
+ array_size(message) - 1, signature, siglen));
+}
+
+struct EVP_PKEY_CTX_Delete {
+ void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
+};
+
+static void VerifySignature(const uint8_t* key, size_t key_len, const uint8_t* signature,
+ size_t signature_len, const uint8_t* message, size_t message_len) {
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
+ ASSERT_TRUE(pkey.get() != NULL);
+ UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ ASSERT_TRUE(ctx.get() != NULL);
+ ASSERT_EQ(1, EVP_PKEY_verify_init(ctx.get()));
+ if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA)
+ ASSERT_EQ(1, EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING));
+ EXPECT_EQ(1, EVP_PKEY_verify(ctx.get(), signature, signature_len, message, message_len));
+}
+
+typedef TrustyKeymasterTest ExportKeyTest;
+TEST_F(ExportKeyTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, ¶ms, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ uint8_t* exported;
+ size_t exported_size;
+ EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(ptr, size, &exported, &exported_size));
+ Malloc_Delete exported_deleter(exported);
+
+ // Sign a message so we can verify it with the exported pubkey.
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(message_len, siglen);
+ const uint8_t* tmp = exported;
+
+ VerifySignature(exported, exported_size, signature, siglen, message.get(), message_len);
+}
+
+typedef TrustyKeymasterTest ExportKeyTest;
+TEST_F(ExportKeyTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* key = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, ¶ms, &key, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(key);
+
+ uint8_t* exported;
+ size_t exported_size;
+ EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(key, size, &exported, &exported_size));
+ Malloc_Delete exported_deleter(exported);
+
+ // Sign a message so we can verify it with the exported pubkey.
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
+ array_size(message) - 1, signature, siglen));
+
+ VerifySignature(exported, exported_size, signature, siglen, message, array_size(message) - 1);
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_ipc.c b/trusty/keymaster/trusty_keymaster_ipc.c
new file mode 100644
index 0000000..88546af
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_ipc.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TrustyKeymaster"
+
+// TODO: make this generic in libtrusty
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <trusty/tipc.h>
+
+#include "trusty_keymaster_ipc.h"
+#include "keymaster_ipc.h"
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+static int handle_ = 0;
+
+int trusty_keymaster_connect() {
+ int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
+ if (rc < 0) {
+ return rc;
+ }
+
+ handle_ = rc;
+ return 0;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size) {
+ if (handle_ == 0) {
+ ALOGE("not connected\n");
+ return -EINVAL;
+ }
+
+ size_t msg_size = in_size + sizeof(struct keymaster_message);
+ struct keymaster_message *msg = malloc(msg_size);
+ msg->cmd = cmd;
+ memcpy(msg->payload, in, in_size);
+
+ ssize_t rc = write(handle_, msg, msg_size);
+ free(msg);
+
+ if (rc < 0) {
+ ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
+ KEYMASTER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ rc = read(handle_, out, *out_size);
+ if (rc < 0) {
+ ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n",
+ cmd, KEYMASTER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ if ((size_t) rc < sizeof(struct keymaster_message)) {
+ ALOGE("invalid response size (%d)\n", (int) rc);
+ return -EINVAL;
+ }
+
+ msg = (struct keymaster_message *) out;
+
+ if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
+ ALOGE("invalid command (%d)", msg->cmd);
+ return -EINVAL;
+ }
+
+ *out_size = ((size_t) rc) - sizeof(struct keymaster_message);
+ return rc;
+}
+
+void trusty_keymaster_disconnect() {
+ if (handle_ != 0) {
+ tipc_close(handle_);
+ }
+}
+
diff --git a/trusty/keymaster/trusty_keymaster_ipc.h b/trusty/keymaster/trusty_keymaster_ipc.h
new file mode 100644
index 0000000..9785247
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_ipc.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+__BEGIN_DECLS
+
+int trusty_keymaster_connect(void);
+int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size);
+void trusty_keymaster_disconnect(void);
+
+__END_DECLS
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
new file mode 100644
index 0000000..7ed880e
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_main.cpp
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include "trusty_keymaster_device.h"
+
+using keymaster::TrustyKeymasterDevice;
+
+unsigned char rsa_privkey_pk8_der[] = {
+ 0x30, 0x82, 0x02, 0x75, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x5f, 0x30, 0x82, 0x02, 0x5b, 0x02, 0x01,
+ 0x00, 0x02, 0x81, 0x81, 0x00, 0xc6, 0x09, 0x54, 0x09, 0x04, 0x7d, 0x86, 0x34, 0x81, 0x2d, 0x5a,
+ 0x21, 0x81, 0x76, 0xe4, 0x5c, 0x41, 0xd6, 0x0a, 0x75, 0xb1, 0x39, 0x01, 0xf2, 0x34, 0x22, 0x6c,
+ 0xff, 0xe7, 0x76, 0x52, 0x1c, 0x5a, 0x77, 0xb9, 0xe3, 0x89, 0x41, 0x7b, 0x71, 0xc0, 0xb6, 0xa4,
+ 0x4d, 0x13, 0xaf, 0xe4, 0xe4, 0xa2, 0x80, 0x5d, 0x46, 0xc9, 0xda, 0x29, 0x35, 0xad, 0xb1, 0xff,
+ 0x0c, 0x1f, 0x24, 0xea, 0x06, 0xe6, 0x2b, 0x20, 0xd7, 0x76, 0x43, 0x0a, 0x4d, 0x43, 0x51, 0x57,
+ 0x23, 0x3c, 0x6f, 0x91, 0x67, 0x83, 0xc3, 0x0e, 0x31, 0x0f, 0xcb, 0xd8, 0x9b, 0x85, 0xc2, 0xd5,
+ 0x67, 0x71, 0x16, 0x97, 0x85, 0xac, 0x12, 0xbc, 0xa2, 0x44, 0xab, 0xda, 0x72, 0xbf, 0xb1, 0x9f,
+ 0xc4, 0x4d, 0x27, 0xc8, 0x1e, 0x1d, 0x92, 0xde, 0x28, 0x4f, 0x40, 0x61, 0xed, 0xfd, 0x99, 0x28,
+ 0x07, 0x45, 0xea, 0x6d, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x81, 0x80, 0x1b, 0xe0, 0xf0,
+ 0x4d, 0x9c, 0xae, 0x37, 0x18, 0x69, 0x1f, 0x03, 0x53, 0x38, 0x30, 0x8e, 0x91, 0x56, 0x4b, 0x55,
+ 0x89, 0x9f, 0xfb, 0x50, 0x84, 0xd2, 0x46, 0x0e, 0x66, 0x30, 0x25, 0x7e, 0x05, 0xb3, 0xce, 0xab,
+ 0x02, 0x97, 0x2d, 0xfa, 0xbc, 0xd6, 0xce, 0x5f, 0x6e, 0xe2, 0x58, 0x9e, 0xb6, 0x79, 0x11, 0xed,
+ 0x0f, 0xac, 0x16, 0xe4, 0x3a, 0x44, 0x4b, 0x8c, 0x86, 0x1e, 0x54, 0x4a, 0x05, 0x93, 0x36, 0x57,
+ 0x72, 0xf8, 0xba, 0xf6, 0xb2, 0x2f, 0xc9, 0xe3, 0xc5, 0xf1, 0x02, 0x4b, 0x06, 0x3a, 0xc0, 0x80,
+ 0xa7, 0xb2, 0x23, 0x4c, 0xf8, 0xae, 0xe8, 0xf6, 0xc4, 0x7b, 0xbf, 0x4f, 0xd3, 0xac, 0xe7, 0x24,
+ 0x02, 0x90, 0xbe, 0xf1, 0x6c, 0x0b, 0x3f, 0x7f, 0x3c, 0xdd, 0x64, 0xce, 0x3a, 0xb5, 0x91, 0x2c,
+ 0xf6, 0xe3, 0x2f, 0x39, 0xab, 0x18, 0x83, 0x58, 0xaf, 0xcc, 0xcd, 0x80, 0x81, 0x02, 0x41, 0x00,
+ 0xe4, 0xb4, 0x9e, 0xf5, 0x0f, 0x76, 0x5d, 0x3b, 0x24, 0xdd, 0xe0, 0x1a, 0xce, 0xaa, 0xf1, 0x30,
+ 0xf2, 0xc7, 0x66, 0x70, 0xa9, 0x1a, 0x61, 0xae, 0x08, 0xaf, 0x49, 0x7b, 0x4a, 0x82, 0xbe, 0x6d,
+ 0xee, 0x8f, 0xcd, 0xd5, 0xe3, 0xf7, 0xba, 0x1c, 0xfb, 0x1f, 0x0c, 0x92, 0x6b, 0x88, 0xf8, 0x8c,
+ 0x92, 0xbf, 0xab, 0x13, 0x7f, 0xba, 0x22, 0x85, 0x22, 0x7b, 0x83, 0xc3, 0x42, 0xff, 0x7c, 0x55,
+ 0x02, 0x41, 0x00, 0xdd, 0xab, 0xb5, 0x83, 0x9c, 0x4c, 0x7f, 0x6b, 0xf3, 0xd4, 0x18, 0x32, 0x31,
+ 0xf0, 0x05, 0xb3, 0x1a, 0xa5, 0x8a, 0xff, 0xdd, 0xa5, 0xc7, 0x9e, 0x4c, 0xce, 0x21, 0x7f, 0x6b,
+ 0xc9, 0x30, 0xdb, 0xe5, 0x63, 0xd4, 0x80, 0x70, 0x6c, 0x24, 0xe9, 0xeb, 0xfc, 0xab, 0x28, 0xa6,
+ 0xcd, 0xef, 0xd3, 0x24, 0xb7, 0x7e, 0x1b, 0xf7, 0x25, 0x1b, 0x70, 0x90, 0x92, 0xc2, 0x4f, 0xf5,
+ 0x01, 0xfd, 0x91, 0x02, 0x40, 0x23, 0xd4, 0x34, 0x0e, 0xda, 0x34, 0x45, 0xd8, 0xcd, 0x26, 0xc1,
+ 0x44, 0x11, 0xda, 0x6f, 0xdc, 0xa6, 0x3c, 0x1c, 0xcd, 0x4b, 0x80, 0xa9, 0x8a, 0xd5, 0x2b, 0x78,
+ 0xcc, 0x8a, 0xd8, 0xbe, 0xb2, 0x84, 0x2c, 0x1d, 0x28, 0x04, 0x05, 0xbc, 0x2f, 0x6c, 0x1b, 0xea,
+ 0x21, 0x4a, 0x1d, 0x74, 0x2a, 0xb9, 0x96, 0xb3, 0x5b, 0x63, 0xa8, 0x2a, 0x5e, 0x47, 0x0f, 0xa8,
+ 0x8d, 0xbf, 0x82, 0x3c, 0xdd, 0x02, 0x40, 0x1b, 0x7b, 0x57, 0x44, 0x9a, 0xd3, 0x0d, 0x15, 0x18,
+ 0x24, 0x9a, 0x5f, 0x56, 0xbb, 0x98, 0x29, 0x4d, 0x4b, 0x6a, 0xc1, 0x2f, 0xfc, 0x86, 0x94, 0x04,
+ 0x97, 0xa5, 0xa5, 0x83, 0x7a, 0x6c, 0xf9, 0x46, 0x26, 0x2b, 0x49, 0x45, 0x26, 0xd3, 0x28, 0xc1,
+ 0x1e, 0x11, 0x26, 0x38, 0x0f, 0xde, 0x04, 0xc2, 0x4f, 0x91, 0x6d, 0xec, 0x25, 0x08, 0x92, 0xdb,
+ 0x09, 0xa6, 0xd7, 0x7c, 0xdb, 0xa3, 0x51, 0x02, 0x40, 0x77, 0x62, 0xcd, 0x8f, 0x4d, 0x05, 0x0d,
+ 0xa5, 0x6b, 0xd5, 0x91, 0xad, 0xb5, 0x15, 0xd2, 0x4d, 0x7c, 0xcd, 0x32, 0xcc, 0xa0, 0xd0, 0x5f,
+ 0x86, 0x6d, 0x58, 0x35, 0x14, 0xbd, 0x73, 0x24, 0xd5, 0xf3, 0x36, 0x45, 0xe8, 0xed, 0x8b, 0x4a,
+ 0x1c, 0xb3, 0xcc, 0x4a, 0x1d, 0x67, 0x98, 0x73, 0x99, 0xf2, 0xa0, 0x9f, 0x5b, 0x3f, 0xb6, 0x8c,
+ 0x88, 0xd5, 0xe5, 0xd9, 0x0a, 0xc3, 0x34, 0x92, 0xd6};
+unsigned int rsa_privkey_pk8_der_len = 633;
+
+unsigned char dsa_privkey_pk8_der[] = {
+ 0x30, 0x82, 0x01, 0x4b, 0x02, 0x01, 0x00, 0x30, 0x82, 0x01, 0x2b, 0x06, 0x07, 0x2a, 0x86, 0x48,
+ 0xce, 0x38, 0x04, 0x01, 0x30, 0x82, 0x01, 0x1e, 0x02, 0x81, 0x81, 0x00, 0xa3, 0xf3, 0xe9, 0xb6,
+ 0x7e, 0x7d, 0x88, 0xf6, 0xb7, 0xe5, 0xf5, 0x1f, 0x3b, 0xee, 0xac, 0xd7, 0xad, 0xbc, 0xc9, 0xd1,
+ 0x5a, 0xf8, 0x88, 0xc4, 0xef, 0x6e, 0x3d, 0x74, 0x19, 0x74, 0xe7, 0xd8, 0xe0, 0x26, 0x44, 0x19,
+ 0x86, 0xaf, 0x19, 0xdb, 0x05, 0xe9, 0x3b, 0x8b, 0x58, 0x58, 0xde, 0xe5, 0x4f, 0x48, 0x15, 0x01,
+ 0xea, 0xe6, 0x83, 0x52, 0xd7, 0xc1, 0x21, 0xdf, 0xb9, 0xb8, 0x07, 0x66, 0x50, 0xfb, 0x3a, 0x0c,
+ 0xb3, 0x85, 0xee, 0xbb, 0x04, 0x5f, 0xc2, 0x6d, 0x6d, 0x95, 0xfa, 0x11, 0x93, 0x1e, 0x59, 0x5b,
+ 0xb1, 0x45, 0x8d, 0xe0, 0x3d, 0x73, 0xaa, 0xf2, 0x41, 0x14, 0x51, 0x07, 0x72, 0x3d, 0xa2, 0xf7,
+ 0x58, 0xcd, 0x11, 0xa1, 0x32, 0xcf, 0xda, 0x42, 0xb7, 0xcc, 0x32, 0x80, 0xdb, 0x87, 0x82, 0xec,
+ 0x42, 0xdb, 0x5a, 0x55, 0x24, 0x24, 0xa2, 0xd1, 0x55, 0x29, 0xad, 0xeb, 0x02, 0x15, 0x00, 0xeb,
+ 0xea, 0x17, 0xd2, 0x09, 0xb3, 0xd7, 0x21, 0x9a, 0x21, 0x07, 0x82, 0x8f, 0xab, 0xfe, 0x88, 0x71,
+ 0x68, 0xf7, 0xe3, 0x02, 0x81, 0x80, 0x19, 0x1c, 0x71, 0xfd, 0xe0, 0x03, 0x0c, 0x43, 0xd9, 0x0b,
+ 0xf6, 0xcd, 0xd6, 0xa9, 0x70, 0xe7, 0x37, 0x86, 0x3a, 0x78, 0xe9, 0xa7, 0x47, 0xa7, 0x47, 0x06,
+ 0x88, 0xb1, 0xaf, 0xd7, 0xf3, 0xf1, 0xa1, 0xd7, 0x00, 0x61, 0x28, 0x88, 0x31, 0x48, 0x60, 0xd8,
+ 0x11, 0xef, 0xa5, 0x24, 0x1a, 0x81, 0xc4, 0x2a, 0xe2, 0xea, 0x0e, 0x36, 0xd2, 0xd2, 0x05, 0x84,
+ 0x37, 0xcf, 0x32, 0x7d, 0x09, 0xe6, 0x0f, 0x8b, 0x0c, 0xc8, 0xc2, 0xa4, 0xb1, 0xdc, 0x80, 0xca,
+ 0x68, 0xdf, 0xaf, 0xd2, 0x90, 0xc0, 0x37, 0x58, 0x54, 0x36, 0x8f, 0x49, 0xb8, 0x62, 0x75, 0x8b,
+ 0x48, 0x47, 0xc0, 0xbe, 0xf7, 0x9a, 0x92, 0xa6, 0x68, 0x05, 0xda, 0x9d, 0xaf, 0x72, 0x9a, 0x67,
+ 0xb3, 0xb4, 0x14, 0x03, 0xae, 0x4f, 0x4c, 0x76, 0xb9, 0xd8, 0x64, 0x0a, 0xba, 0x3b, 0xa8, 0x00,
+ 0x60, 0x4d, 0xae, 0x81, 0xc3, 0xc5, 0x04, 0x17, 0x02, 0x15, 0x00, 0x81, 0x9d, 0xfd, 0x53, 0x0c,
+ 0xc1, 0x8f, 0xbe, 0x8b, 0xea, 0x00, 0x26, 0x19, 0x29, 0x33, 0x91, 0x84, 0xbe, 0xad, 0x81};
+unsigned int dsa_privkey_pk8_der_len = 335;
+
+unsigned char ec_privkey_pk8_der[] = {
+ 0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+ 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02,
+ 0x01, 0x01, 0x04, 0x20, 0x73, 0x7c, 0x2e, 0xcd, 0x7b, 0x8d, 0x19, 0x40, 0xbf, 0x29, 0x30, 0xaa,
+ 0x9b, 0x4e, 0xd3, 0xff, 0x94, 0x1e, 0xed, 0x09, 0x36, 0x6b, 0xc0, 0x32, 0x99, 0x98, 0x64, 0x81,
+ 0xf3, 0xa4, 0xd8, 0x59, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xbf, 0x85, 0xd7, 0x72, 0x0d, 0x07,
+ 0xc2, 0x54, 0x61, 0x68, 0x3b, 0xc6, 0x48, 0xb4, 0x77, 0x8a, 0x9a, 0x14, 0xdd, 0x8a, 0x02, 0x4e,
+ 0x3b, 0xdd, 0x8c, 0x7d, 0xdd, 0x9a, 0xb2, 0xb5, 0x28, 0xbb, 0xc7, 0xaa, 0x1b, 0x51, 0xf1, 0x4e,
+ 0xbb, 0xbb, 0x0b, 0xd0, 0xce, 0x21, 0xbc, 0xc4, 0x1c, 0x6e, 0xb0, 0x00, 0x83, 0xcf, 0x33, 0x76,
+ 0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
+unsigned int ec_privkey_pk8_der_len = 138;
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+
+struct EVP_PKEY_CTX_Delete {
+ void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
+};
+
+static bool test_import_rsa(TrustyKeymasterDevice* device) {
+ printf("===================\n");
+ printf("= RSA Import Test =\n");
+ printf("===================\n\n");
+
+ printf("=== Importing RSA keypair === \n");
+ uint8_t* key;
+ size_t size;
+ int error = device->import_keypair(rsa_privkey_pk8_der, rsa_privkey_pk8_der_len, &key, &size);
+ if (error != KM_ERROR_OK) {
+ printf("Error importing key pair: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> key_deleter(key);
+
+ printf("=== Signing with imported RSA key ===\n");
+ keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = 1024 / 8;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ uint8_t* signature;
+ size_t signature_len;
+ error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
+ &signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error signing data with imported RSA key: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> signature_deleter(signature);
+
+ printf("=== Verifying with imported RSA key === \n");
+ error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
+ signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error verifying data with imported RSA key: %d\n\n", error);
+ return false;
+ }
+
+ printf("\n");
+ return true;
+}
+
+static bool test_rsa(TrustyKeymasterDevice* device) {
+ printf("============\n");
+ printf("= RSA Test =\n");
+ printf("============\n\n");
+
+ printf("=== Generating RSA key pair ===\n");
+ keymaster_rsa_keygen_params_t params;
+ params.public_exponent = 65537;
+ params.modulus_size = 2048;
+
+ uint8_t* key;
+ size_t size;
+ int error = device->generate_keypair(TYPE_RSA, ¶ms, &key, &size);
+ if (error != KM_ERROR_OK) {
+ printf("Error generating RSA key pair: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> deleter(key);
+
+ printf("=== Signing with RSA key === \n");
+ keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ uint8_t* signature;
+ size_t signature_len;
+ error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
+ &signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error signing data with RSA key: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> signature_deleter(signature);
+
+ printf("=== Verifying with RSA key === \n");
+ error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
+ signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error verifying data with RSA key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Exporting RSA public key ===\n");
+ uint8_t* exported_key;
+ size_t exported_size;
+ error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+ if (error != KM_ERROR_OK) {
+ printf("Error exporting RSA public key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Verifying with exported key ===\n");
+ const uint8_t* tmp = exported_key;
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
+ UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+ printf("Error initializing openss EVP context\n");
+ return false;
+ }
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+ printf("Exported key was the wrong type?!?\n");
+ return false;
+ }
+
+ EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
+ if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n");
+ return false;
+ } else {
+ printf("Verification succeeded\n");
+ }
+
+ printf("\n");
+ return true;
+}
+
+static bool test_import_ecdsa(TrustyKeymasterDevice* device) {
+ printf("=====================\n");
+ printf("= ECDSA Import Test =\n");
+ printf("=====================\n\n");
+
+ printf("=== Importing ECDSA keypair === \n");
+ uint8_t* key;
+ size_t size;
+ int error = device->import_keypair(ec_privkey_pk8_der, ec_privkey_pk8_der_len, &key, &size);
+ if (error != KM_ERROR_OK) {
+ printf("Error importing key pair: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> deleter(key);
+
+ printf("=== Signing with imported ECDSA key ===\n");
+ keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
+ size_t message_len = 30 /* arbitrary */;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ uint8_t* signature;
+ size_t signature_len;
+ error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
+ &signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error signing data with imported ECDSA key: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> signature_deleter(signature);
+
+ printf("=== Verifying with imported ECDSA key === \n");
+ error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
+ signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error verifying data with imported ECDSA key: %d\n\n", error);
+ return false;
+ }
+
+ printf("\n");
+ return true;
+}
+
+static bool test_ecdsa(TrustyKeymasterDevice* device) {
+ printf("==============\n");
+ printf("= ECDSA Test =\n");
+ printf("==============\n\n");
+
+ printf("=== Generating ECDSA key pair ===\n");
+ keymaster_ec_keygen_params_t params;
+ params.field_size = 521;
+ uint8_t* key;
+ size_t size;
+ int error = device->generate_keypair(TYPE_EC, ¶ms, &key, &size);
+ if (error != 0) {
+ printf("Error generating ECDSA key pair: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> deleter(key);
+
+ printf("=== Signing with ECDSA key === \n");
+ keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
+ size_t message_len = 30 /* arbitrary */;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ uint8_t* signature;
+ size_t signature_len;
+ error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
+ &signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error signing data with ECDSA key: %d\n\n", error);
+ return false;
+ }
+ UniquePtr<uint8_t[]> signature_deleter(signature);
+
+ printf("=== Verifying with ECDSA key === \n");
+ error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
+ signature_len);
+ if (error != KM_ERROR_OK) {
+ printf("Error verifying data with ECDSA key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Exporting ECDSA public key ===\n");
+ uint8_t* exported_key;
+ size_t exported_size;
+ error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+ if (error != KM_ERROR_OK) {
+ printf("Error exporting ECDSA public key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Verifying with exported key ===\n");
+ const uint8_t* tmp = exported_key;
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
+ UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+ printf("Error initializing openss EVP context\n");
+ return false;
+ }
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
+ printf("Exported key was the wrong type?!?\n");
+ return false;
+ }
+
+ if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n");
+ return false;
+ } else {
+ printf("Verification succeeded\n");
+ }
+
+ printf("\n");
+ return true;
+}
+
+int main(void) {
+
+ TrustyKeymasterDevice device(NULL);
+ if (device.session_error() != KM_ERROR_OK) {
+ printf("Failed to initialize Trusty session: %d\n", device.session_error());
+ return 1;
+ }
+ printf("Trusty session initialized\n");
+
+ bool success = true;
+ success &= test_rsa(&device);
+ success &= test_import_rsa(&device);
+ success &= test_ecdsa(&device);
+ success &= test_import_ecdsa(&device);
+
+ if (success) {
+ printf("\nTESTS PASSED!\n");
+ } else {
+ printf("\n!!!!TESTS FAILED!!!\n");
+ }
+
+ return success ? 0 : 1;
+}
diff --git a/trusty/libtrusty/Android.mk b/trusty/libtrusty/Android.mk
new file mode 100644
index 0000000..45fc079
--- /dev/null
+++ b/trusty/libtrusty/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# == libtrusty Static library ==
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtrusty
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := trusty.c
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+include $(BUILD_STATIC_LIBRARY)
+
+# == libtrusty shared library ==
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtrusty
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := trusty.c
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := liblog
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/libtrusty/include/trusty/tipc.h b/trusty/libtrusty/include/trusty/tipc.h
new file mode 100644
index 0000000..a3f2a3f
--- /dev/null
+++ b/trusty/libtrusty/include/trusty/tipc.h
@@ -0,0 +1,31 @@
+/*
+ * 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 _LIB_TIPC_H
+#define _LIB_TIPC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int tipc_connect(const char *dev_name, const char *srv_name);
+int tipc_close(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/trusty/libtrusty/tipc-test/Android.mk b/trusty/libtrusty/tipc-test/Android.mk
new file mode 100644
index 0000000..80030fe
--- /dev/null
+++ b/trusty/libtrusty/tipc-test/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := tipc-test
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := tipc_test.c
+LOCAL_STATIC_LIBRARIES := libc libtrusty liblog
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+include $(BUILD_EXECUTABLE)
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
new file mode 100644
index 0000000..1fb34c9
--- /dev/null
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -0,0 +1,916 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/uio.h>
+
+#include <trusty/tipc.h>
+
+#define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
+
+static const char *dev_name = NULL;
+static const char *test_name = NULL;
+
+static const char *uuid_name = "com.android.ipc-unittest.srv.uuid";
+static const char *echo_name = "com.android.ipc-unittest.srv.echo";
+static const char *ta_only_name = "com.android.ipc-unittest.srv.ta_only";
+static const char *ns_only_name = "com.android.ipc-unittest.srv.ns_only";
+static const char *datasink_name = "com.android.ipc-unittest.srv.datasink";
+static const char *closer1_name = "com.android.ipc-unittest.srv.closer1";
+static const char *closer2_name = "com.android.ipc-unittest.srv.closer2";
+static const char *closer3_name = "com.android.ipc-unittest.srv.closer3";
+static const char *main_ctrl_name = "com.android.ipc-unittest.ctrl";
+
+static const char *_sopts = "hsvD:t:r:m:b:";
+static const struct option _lopts[] = {
+ {"help", no_argument, 0, 'h'},
+ {"silent", no_argument, 0, 's'},
+ {"variable",no_argument, 0, 'v'},
+ {"dev", required_argument, 0, 'D'},
+ {"repeat", required_argument, 0, 'r'},
+ {"burst", required_argument, 0, 'b'},
+ {"msgsize", required_argument, 0, 'm'},
+ {0, 0, 0, 0}
+};
+
+static const char *usage =
+"Usage: %s [options]\n"
+"\n"
+"options:\n"
+" -h, --help prints this message and exit\n"
+" -D, --dev name device name\n"
+" -t, --test name test to run\n"
+" -r, --repeat cnt repeat count\n"
+" -m, --msgsize size max message size\n"
+" -v, --variable variable message size\n"
+" -s, --silent silent\n"
+"\n"
+;
+
+static const char *usage_long =
+"\n"
+"The following tests are available:\n"
+" connect - connect to datasink service\n"
+" connect_foo - connect to non existing service\n"
+" burst_write - send messages to datasink service\n"
+" echo - send/receive messages to echo service\n"
+" select - test select call\n"
+" blocked_read - test blocked read\n"
+" closer1 - connection closed by remote (test1)\n"
+" closer2 - connection closed by remote (test2)\n"
+" closer3 - connection closed by remote (test3)\n"
+" ta2ta-ipc - execute TA to TA unittest\n"
+" dev-uuid - print device uuid\n"
+" ta-access - test ta-access flags\n"
+" writev - writev test\n"
+" readv - readv test\n"
+"\n"
+;
+
+static uint opt_repeat = 1;
+static uint opt_msgsize = 32;
+static uint opt_msgburst = 32;
+static bool opt_variable = false;
+static bool opt_silent = false;
+
+static void print_usage_and_exit(const char *prog, int code, bool verbose)
+{
+ fprintf (stderr, usage, prog);
+ if (verbose)
+ fprintf (stderr, "%s", usage_long);
+ exit(code);
+}
+
+static void parse_options(int argc, char **argv)
+{
+ int c;
+ int oidx = 0;
+
+ while (1)
+ {
+ c = getopt_long (argc, argv, _sopts, _lopts, &oidx);
+ if (c == -1)
+ break; /* done */
+
+ switch (c) {
+
+ case 'D':
+ dev_name = strdup(optarg);
+ break;
+
+ case 't':
+ test_name = strdup(optarg);
+ break;
+
+ case 'v':
+ opt_variable = true;
+ break;
+
+ case 'r':
+ opt_repeat = atoi(optarg);
+ break;
+
+ case 'm':
+ opt_msgsize = atoi(optarg);
+ break;
+
+ case 'b':
+ opt_msgburst = atoi(optarg);
+ break;
+
+ case 's':
+ opt_silent = true;
+ break;
+
+ case 'h':
+ print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+ break;
+
+ default:
+ print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+ }
+ }
+}
+
+static int connect_test(uint repeat)
+{
+ uint i;
+ int echo_fd;
+ int dsink_fd;
+
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
+
+ for (i = 0; i < repeat; i++) {
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n",
+ "echo");
+ }
+ dsink_fd = tipc_connect(dev_name, datasink_name);
+ if (dsink_fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n",
+ "datasink");
+ }
+
+ if (echo_fd >= 0) {
+ tipc_close(echo_fd);
+ }
+ if (dsink_fd >= 0) {
+ tipc_close(dsink_fd);
+ }
+ }
+
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
+
+ return 0;
+}
+
+static int connect_foo(uint repeat)
+{
+ uint i;
+ int fd;
+
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
+
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, "foo");
+ if (fd >= 0) {
+ fprintf(stderr, "succeeded to connect to '%s' service\n",
+ "foo");
+ tipc_close(fd);
+ }
+ }
+
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
+
+ return 0;
+}
+
+
+static int closer1_test(uint repeat)
+{
+ uint i;
+ int fd;
+
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
+
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, closer1_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n",
+ "closer1");
+ continue;
+ }
+ if (!opt_silent) {
+ printf("%s: connected\n", __func__);
+ }
+ tipc_close(fd);
+ }
+
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
+
+ return 0;
+}
+
+static int closer2_test(uint repeat)
+{
+ uint i;
+ int fd;
+
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
+
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, closer2_name);
+ if (fd < 0) {
+ if (!opt_silent) {
+ printf("failed to connect to '%s' service\n", "closer2");
+ }
+ } else {
+ /* this should always fail */
+ fprintf(stderr, "connected to '%s' service\n", "closer2");
+ tipc_close(fd);
+ }
+ }
+
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
+
+ return 0;
+}
+
+static int closer3_test(uint repeat)
+{
+ uint i, j;
+ ssize_t rc;
+ int fd[4];
+ char buf[64];
+
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ /* open 4 connections to closer3 service */
+ for (j = 0; j < 4; j++) {
+ fd[j] = tipc_connect(dev_name, closer3_name);
+ if (fd[j] < 0) {
+ fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
+ } else {
+ if (!opt_silent) {
+ printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
+ }
+ memset(buf, i + j, sizeof(buf));
+ rc = write(fd[j], buf, sizeof(buf));
+ if (rc != sizeof(buf)) {
+ if (!opt_silent) {
+ printf("%s: fd[%d]=%d: write returned = %zd\n",
+ __func__, j, fd[j], rc);
+ }
+ perror("closer3_test: write");
+ }
+ }
+ }
+
+ /* sleep a bit */
+ sleep(1);
+
+ /* It is expected that they will be closed by remote */
+ for (j = 0; j < 4; j++) {
+ if (fd[j] < 0)
+ continue;
+ rc = write(fd[j], buf, sizeof(buf));
+ if (rc != sizeof(buf)) {
+ if (!opt_silent) {
+ printf("%s: fd[%d]=%d: write returned = %zd\n",
+ __func__, j, fd[j], rc);
+ }
+ perror("closer3_test: write");
+ }
+ }
+
+ /* then they have to be closed by remote */
+ for (j = 0; j < 4; j++) {
+ if (fd[j] >= 0) {
+ tipc_close(fd[j]);
+ }
+ }
+ }
+
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
+
+ return 0;
+}
+
+
+static int echo_test(uint repeat, uint msgsz, bool var)
+{
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd =-1;
+ char tx_buf[msgsz];
+ char rx_buf[msgsz];
+
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n",
+ __func__, repeat, msgsz, var ? "true" : "false");
+ }
+
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
+
+ memset(tx_buf, i + 1, msg_len);
+
+ rc = write(echo_fd, tx_buf, msg_len);
+ if ((size_t)rc != msg_len) {
+ perror("echo_test: write");
+ break;
+ }
+
+ rc = read(echo_fd, rx_buf, msg_len);
+ if (rc < 0) {
+ perror("echo_test: read");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "data truncated (%zu vs. %zu)\n",
+ rc, msg_len);
+ continue;
+ }
+
+ if (memcmp(tx_buf, rx_buf, (size_t) rc)) {
+ fprintf(stderr, "data mismatch\n");
+ continue;
+ }
+ }
+
+ tipc_close(echo_fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+static int burst_write_test(uint repeat, uint msgburst, uint msgsz, bool var)
+{
+ int fd;
+ uint i, j;
+ ssize_t rc;
+ size_t msg_len;
+ char tx_buf[msgsz];
+
+ if (!opt_silent) {
+ printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n",
+ __func__, repeat, msgburst, msgsz,
+ var ? "true" : "false");
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ fd = tipc_connect(dev_name, datasink_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n",
+ "datasink");
+ break;
+ }
+
+ for (j = 0; j < msgburst; j++) {
+ msg_len = msgsz;
+ if (var && msgsz) {
+ msg_len = rand() % msgsz;
+ }
+
+ memset(tx_buf, i + 1, msg_len);
+ rc = write(fd, tx_buf, msg_len);
+ if ((size_t)rc != msg_len) {
+ perror("burst_test: write");
+ break;
+ }
+ }
+
+ tipc_close(fd);
+ }
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+
+static int _wait_for_msg(int fd, uint msgsz, int timeout)
+{
+ int rc;
+ fd_set rfds;
+ uint msgcnt = 0;
+ char rx_buf[msgsz];
+ struct timeval tv;
+
+ if (!opt_silent) {
+ printf("waiting (%d) for msg\n", timeout);
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ for(;;) {
+ rc = select(fd+1, &rfds, NULL, NULL, &tv);
+
+ if (rc == 0) {
+ if (!opt_silent) {
+ printf("select timedout\n");
+ }
+ break;
+ }
+
+ if (rc == -1) {
+ perror("select_test: select");
+ return rc;
+ }
+
+ rc = read(fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("select_test: read");
+ return rc;
+ } else {
+ if (rc > 0) {
+ msgcnt++;
+ }
+ }
+ }
+
+ if (!opt_silent) {
+ printf("got %u messages\n", msgcnt);
+ }
+
+ return 0;
+}
+
+
+static int select_test(uint repeat, uint msgburst, uint msgsz)
+{
+ int fd;
+ uint i, j;
+ ssize_t rc;
+ char tx_buf[msgsz];
+
+ if (!opt_silent) {
+ printf("%s: repeat %u\n", __func__, repeat);
+ }
+
+ fd = tipc_connect(dev_name, echo_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n",
+ "echo");
+ return fd;
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ _wait_for_msg(fd, msgsz, 1);
+
+ if (!opt_silent) {
+ printf("sending burst: %u msg\n", msgburst);
+ }
+
+ for (j = 0; j < msgburst; j++) {
+ memset(tx_buf, i + j, msgsz);
+ rc = write(fd, tx_buf, msgsz);
+ if ((size_t)rc != msgsz) {
+ perror("burst_test: write");
+ break;
+ }
+ }
+ }
+
+ tipc_close(fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+static int blocked_read_test(uint repeat)
+{
+ int fd;
+ uint i;
+ ssize_t rc;
+ char rx_buf[512];
+
+ if (!opt_silent) {
+ printf("%s: repeat %u\n", __func__, repeat);
+ }
+
+ fd = tipc_connect(dev_name, echo_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n",
+ "echo");
+ return fd;
+ }
+
+ for (i = 0; i < repeat; i++) {
+ rc = read(fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("select_test: read");
+ break;
+ } else {
+ if (!opt_silent) {
+ printf("got %zd bytes\n", rc);
+ }
+ }
+ }
+
+ tipc_close(fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+static int ta2ta_ipc_test(void)
+{
+ int fd;
+ char rx_buf[64];
+
+ if (!opt_silent) {
+ printf("%s:\n", __func__);
+ }
+
+ fd = tipc_connect(dev_name, main_ctrl_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n",
+ "main_ctrl");
+ return fd;
+ }
+
+ /* wait for test to complete */
+ (void) read(fd, rx_buf, sizeof(rx_buf));
+
+ tipc_close(fd);
+
+ return 0;
+}
+
+typedef struct uuid
+{
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_and_node[8];
+} uuid_t;
+
+static void print_uuid(const char *dev, uuid_t *uuid)
+{
+ printf("%s:", dev);
+ printf("uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ uuid->time_low,
+ uuid->time_mid,
+ uuid->time_hi_and_version,
+ uuid->clock_seq_and_node[0],
+ uuid->clock_seq_and_node[1],
+ uuid->clock_seq_and_node[2],
+ uuid->clock_seq_and_node[3],
+ uuid->clock_seq_and_node[4],
+ uuid->clock_seq_and_node[5],
+ uuid->clock_seq_and_node[6],
+ uuid->clock_seq_and_node[7]
+ );
+}
+
+static int dev_uuid_test(void)
+{
+ int fd;
+ ssize_t rc;
+ uuid_t uuid;
+
+ fd = tipc_connect(dev_name, uuid_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n",
+ "uuid");
+ return fd;
+ }
+
+ /* wait for test to complete */
+ rc = read(fd, &uuid, sizeof(uuid));
+ if (rc < 0) {
+ perror("dev_uuid_test: read");
+ } else if (rc != sizeof(uuid)) {
+ fprintf(stderr, "unexpected uuid size (%d vs. %d)\n",
+ (int)rc, (int)sizeof(uuid));
+ } else {
+ print_uuid(dev_name, &uuid);
+ }
+
+ tipc_close(fd);
+
+ return 0;
+}
+
+static int ta_access_test(void)
+{
+ int fd;
+
+ if (!opt_silent) {
+ printf("%s:\n", __func__);
+ }
+
+ fd = tipc_connect(dev_name, ta_only_name);
+ if (fd >= 0) {
+ fprintf(stderr, "Succeed to connect to '%s' service\n",
+ "ta_only");
+ tipc_close(fd);
+ }
+
+ fd = tipc_connect(dev_name, ns_only_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n",
+ "ns_only");
+ return fd;
+ }
+ tipc_close(fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+
+static int writev_test(uint repeat, uint msgsz, bool var)
+{
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx0_buf[msgsz];
+ char tx1_buf[msgsz];
+ char rx_buf [msgsz];
+ struct iovec iovs[2]= {{tx0_buf, 0}, {tx1_buf, 0}};
+
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n",
+ __func__, repeat, msgsz, var ? "true" : "false");
+ }
+
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
+
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
+
+ memset(tx0_buf, i + 1, iovs[0].iov_len);
+ memset(tx1_buf, i + 2, iovs[1].iov_len);
+ memset(rx_buf, i + 3, sizeof(rx_buf));
+
+ rc = writev(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("writev_test: writev");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "writev", (size_t)rc, msg_len);
+ break;
+ }
+
+ rc = read(echo_fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("writev_test: read");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "read", (size_t)rc, msg_len);
+ break;
+ }
+
+ if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
+
+ if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
+
+ tipc_close(echo_fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+static int readv_test(uint repeat, uint msgsz, bool var)
+{
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx_buf [msgsz];
+ char rx0_buf[msgsz];
+ char rx1_buf[msgsz];
+ struct iovec iovs[2]= {{rx0_buf, 0}, {rx1_buf, 0}};
+
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n",
+ __func__, repeat, msgsz, var ? "true" : "false");
+ }
+
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
+
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
+
+ memset(tx_buf, i + 1, sizeof(tx_buf));
+ memset(rx0_buf, i + 2, iovs[0].iov_len);
+ memset(rx1_buf, i + 3, iovs[1].iov_len);
+
+ rc = write(echo_fd, tx_buf, msg_len);
+ if (rc < 0) {
+ perror("readv_test: write");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "write", (size_t)rc, msg_len);
+ break;
+ }
+
+ rc = readv(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("readv_test: readv");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "write", (size_t)rc, msg_len);
+ break;
+ }
+
+ if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
+
+ if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
+
+ tipc_close(echo_fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ int rc = 0;
+
+ if (argc <= 1) {
+ print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+ }
+
+ parse_options(argc, argv);
+
+ if (!dev_name) {
+ dev_name = TIPC_DEFAULT_DEVNAME;
+ }
+
+ if (!test_name) {
+ fprintf(stderr, "need a Test to run\n");
+ print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+ }
+
+ if (strcmp(test_name, "connect") == 0) {
+ rc = connect_test(opt_repeat);
+ } else if (strcmp(test_name, "connect_foo") == 0) {
+ rc = connect_foo(opt_repeat);
+ } else if (strcmp(test_name, "burst_write") == 0) {
+ rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "select") == 0) {
+ rc = select_test(opt_repeat, opt_msgburst, opt_msgsize);
+ } else if (strcmp(test_name, "blocked_read") == 0) {
+ rc = blocked_read_test(opt_repeat);
+ } else if (strcmp(test_name, "closer1") == 0) {
+ rc = closer1_test(opt_repeat);
+ } else if (strcmp(test_name, "closer2") == 0) {
+ rc = closer2_test(opt_repeat);
+ } else if (strcmp(test_name, "closer3") == 0) {
+ rc = closer3_test(opt_repeat);
+ } else if (strcmp(test_name, "echo") == 0) {
+ rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
+ } else if(strcmp(test_name, "ta2ta-ipc") == 0) {
+ rc = ta2ta_ipc_test();
+ } else if (strcmp(test_name, "dev-uuid") == 0) {
+ rc = dev_uuid_test();
+ } else if (strcmp(test_name, "ta-access") == 0) {
+ rc = ta_access_test();
+ } else if (strcmp(test_name, "writev") == 0) {
+ rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "readv") == 0) {
+ rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
+ } else {
+ fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
+ print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+ }
+
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/trusty/libtrusty/tipc_ioctl.h b/trusty/libtrusty/tipc_ioctl.h
new file mode 100644
index 0000000..27da56a
--- /dev/null
+++ b/trusty/libtrusty/tipc_ioctl.h
@@ -0,0 +1,26 @@
+/*
+ * 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 _TIPC_IOCTL_H
+#define _TIPC_IOCTL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define TIPC_IOC_MAGIC 'r'
+#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char *)
+
+#endif
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
new file mode 100644
index 0000000..a6238af
--- /dev/null
+++ b/trusty/libtrusty/trusty.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libtrusty"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include "tipc_ioctl.h"
+
+int tipc_connect(const char *dev_name, const char *srv_name)
+{
+ int fd;
+ int rc;
+
+ fd = open(dev_name, O_RDWR);
+ if (fd < 0) {
+ rc = -errno;
+ ALOGE("%s: cannot open tipc device \"%s\": %s\n",
+ __func__, dev_name, strerror(errno));
+ return rc < 0 ? rc : -1;
+ }
+
+ rc = ioctl(fd, TIPC_IOC_CONNECT, srv_name);
+ if (rc < 0) {
+ rc = -errno;
+ ALOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n",
+ __func__, srv_name, errno);
+ close(fd);
+ return rc < 0 ? rc : -1;
+ }
+
+ ALOGV("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd);
+ return fd;
+}
+
+void tipc_close(int fd)
+{
+ close(fd);
+}
diff --git a/trusty/nvram/Android.mk b/trusty/nvram/Android.mk
new file mode 100644
index 0000000..44e2212
--- /dev/null
+++ b/trusty/nvram/Android.mk
@@ -0,0 +1,43 @@
+#
+# Copyright (C) 2016 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# nvram.trusty is the Trusty NVRAM HAL module.
+include $(CLEAR_VARS)
+LOCAL_MODULE := nvram.trusty
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := \
+ module.c \
+ trusty_nvram_device.cpp \
+ trusty_nvram_implementation.cpp
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
+LOCAL_STATIC_LIBRARIES := libnvram-hal
+LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
+include $(BUILD_SHARED_LIBRARY)
+
+# nvram-wipe is a helper tool for clearing NVRAM state.
+include $(CLEAR_VARS)
+LOCAL_MODULE := nvram-wipe
+LOCAL_SRC_FILES := \
+ nvram_wipe.cpp \
+ trusty_nvram_implementation.cpp
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
+LOCAL_STATIC_LIBRARIES := libnvram-hal
+LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
+include $(BUILD_EXECUTABLE)
diff --git a/trusty/nvram/module.c b/trusty/nvram/module.c
new file mode 100644
index 0000000..a2e64d3
--- /dev/null
+++ b/trusty/nvram/module.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/nvram.h>
+
+// This function is defined in trusty_nvram_device.cpp.
+int trusty_nvram_open(const hw_module_t* module,
+ const char* device_id,
+ hw_device_t** device_ptr);
+
+static struct hw_module_methods_t nvram_module_methods = {
+ .open = trusty_nvram_open,
+};
+
+struct nvram_module HAL_MODULE_INFO_SYM
+ __attribute__((visibility("default"))) = {
+ .common = {.tag = HARDWARE_MODULE_TAG,
+ .module_api_version = NVRAM_MODULE_API_VERSION_0_1,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = NVRAM_HARDWARE_MODULE_ID,
+ .name = "Trusty NVRAM HAL",
+ .author = "The Android Open Source Project",
+ .methods = &nvram_module_methods,
+ .dso = 0,
+ .reserved = {}},
+};
diff --git a/trusty/nvram/nvram_wipe.cpp b/trusty/nvram/nvram_wipe.cpp
new file mode 100644
index 0000000..d0f4fad
--- /dev/null
+++ b/trusty/nvram/nvram_wipe.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <nvram/messages/nvram_messages.h>
+
+#include "trusty_nvram_implementation.h"
+
+void usage(const char* program_name) {
+ fprintf(stderr, "Usage: %s [status|disable|wipe]\n", program_name);
+ exit(-1);
+}
+
+int main(int argc, char* argv[]) {
+ if (argc < 2) {
+ usage(argv[0]);
+ }
+
+ nvram::TrustyNvramImplementation nvram_proxy;
+ nvram::Request request;
+ nvram::Response response;
+
+ if (!strcmp(argv[1], "status")) {
+ request.payload.Activate<nvram::COMMAND_GET_INFO>();
+ nvram_proxy.Execute(request, &response);
+ const nvram::GetInfoResponse* get_info_response =
+ response.payload.get<nvram::COMMAND_GET_INFO>();
+ if (response.result == NV_RESULT_SUCCESS) {
+ int status = get_info_response && get_info_response->wipe_disabled;
+ printf("Wiping disabled: %d\n", status);
+ return status;
+ }
+ } else if (!strcmp(argv[1], "disable")) {
+ request.payload.Activate<nvram::COMMAND_DISABLE_WIPE>();
+ nvram_proxy.Execute(request, &response);
+ } else if (!strcmp(argv[1], "wipe")) {
+ request.payload.Activate<nvram::COMMAND_WIPE_STORAGE>();
+ nvram_proxy.Execute(request, &response);
+ } else {
+ usage(argv[0]);
+ }
+
+ if (response.result != NV_RESULT_SUCCESS) {
+ fprintf(stderr, "Command execution failure: %u\n", response.result);
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/trusty/nvram/trusty_nvram_device.cpp b/trusty/nvram/trusty_nvram_device.cpp
new file mode 100644
index 0000000..2c50915
--- /dev/null
+++ b/trusty/nvram/trusty_nvram_device.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nvram/hal/nvram_device_adapter.h>
+
+#include "trusty_nvram_implementation.h"
+
+extern "C" int trusty_nvram_open(const hw_module_t* module,
+ const char* device_id,
+ hw_device_t** device_ptr) {
+ if (strcmp(NVRAM_HARDWARE_DEVICE_ID, device_id) != 0) {
+ return -EINVAL;
+ }
+
+ nvram::NvramDeviceAdapter* adapter = new nvram::NvramDeviceAdapter(
+ module, new nvram::TrustyNvramImplementation);
+ *device_ptr = adapter->as_device();
+ return 0;
+}
diff --git a/trusty/nvram/trusty_nvram_implementation.cpp b/trusty/nvram/trusty_nvram_implementation.cpp
new file mode 100644
index 0000000..9215c85
--- /dev/null
+++ b/trusty/nvram/trusty_nvram_implementation.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TrustyNVRAM"
+
+#include "trusty_nvram_implementation.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <hardware/nvram.h>
+#include <log/log.h>
+#include <trusty/tipc.h>
+
+#include <nvram/messages/blob.h>
+
+namespace nvram {
+namespace {
+
+// Character device to open for Trusty IPC connections.
+const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
+
+// App identifier of the NVRAM app.
+const char kTrustyNvramAppId[] = "com.android.trusty.nvram";
+
+} // namespace
+
+TrustyNvramImplementation::~TrustyNvramImplementation() {
+ if (tipc_nvram_fd_ != -1) {
+ tipc_close(tipc_nvram_fd_);
+ tipc_nvram_fd_ = -1;
+ }
+}
+
+void TrustyNvramImplementation::Execute(const nvram::Request& request,
+ nvram::Response* response) {
+ if (!SendRequest(request, response)) {
+ response->result = NV_RESULT_INTERNAL_ERROR;
+ }
+}
+
+bool TrustyNvramImplementation::Connect() {
+ if (tipc_nvram_fd_ != -1) {
+ return true;
+ }
+
+ int rc = tipc_connect(kTrustyDeviceName, kTrustyNvramAppId);
+ if (rc < 0) {
+ ALOGE("Failed to connect to Trusty NVRAM app: %s\n", strerror(-rc));
+ return false;
+ }
+
+ tipc_nvram_fd_ = rc;
+ return true;
+}
+
+bool TrustyNvramImplementation::SendRequest(const nvram::Request& request,
+ nvram::Response* response) {
+ if (!Connect()) {
+ return false;
+ }
+
+ nvram::Blob request_buffer;
+ if (!nvram::Encode(request, &request_buffer)) {
+ ALOGE("Failed to encode NVRAM request.\n");
+ return false;
+ }
+
+ ssize_t rc =
+ write(tipc_nvram_fd_, request_buffer.data(), request_buffer.size());
+ if (rc < 0) {
+ ALOGE("Failed to send NVRAM request: %s\n", strerror(-rc));
+ return false;
+ }
+ if (static_cast<size_t>(rc) != request_buffer.size()) {
+ ALOGE("Failed to send full request buffer: %zd\n", rc);
+ return false;
+ }
+
+ rc = read(tipc_nvram_fd_, response_buffer_, sizeof(response_buffer_));
+ if (rc < 0) {
+ ALOGE("Failed to read NVRAM response: %s\n", strerror(-rc));
+ return false;
+ }
+
+ if (static_cast<size_t>(rc) >= sizeof(response_buffer_)) {
+ ALOGE("NVRAM response exceeds response buffer size.\n");
+ return false;
+ }
+
+ if (!nvram::Decode(response_buffer_, static_cast<size_t>(rc), response)) {
+ ALOGE("Failed to decode NVRAM response.\n");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace nvram
diff --git a/trusty/nvram/trusty_nvram_implementation.h b/trusty/nvram/trusty_nvram_implementation.h
new file mode 100644
index 0000000..60758f7
--- /dev/null
+++ b/trusty/nvram/trusty_nvram_implementation.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
+#define TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
+
+#include <stdint.h>
+
+#include <nvram/hal/nvram_device_adapter.h>
+#include <nvram/messages/nvram_messages.h>
+
+namespace nvram {
+
+// |TrustyNvramImplementation| proxies requests to the Trusty NVRAM app. It
+// serializes the request objects, sends it to the Trusty app and finally reads
+// back the result and decodes it.
+class TrustyNvramImplementation : public nvram::NvramImplementation {
+ public:
+ ~TrustyNvramImplementation() override;
+
+ void Execute(const nvram::Request& request,
+ nvram::Response* response) override;
+
+ private:
+ // Connects the IPC channel to the Trusty app if it is not already open.
+ // Returns true if the channel is open, false on errors.
+ bool Connect();
+
+ // Dispatches a command to the trust app. Returns true if successful (note
+ // that the response may still indicate an error on the Trusty side), false if
+ // there are any I/O or encoding/decoding errors.
+ bool SendRequest(const nvram::Request& request,
+ nvram::Response* response);
+
+ // The file descriptor for the IPC connection to the Trusty app.
+ int tipc_nvram_fd_ = -1;
+
+ // Response buffer. This puts a hard size limit on the responses from the
+ // Trusty app. 4096 matches the maximum IPC message size currently supported
+ // by Trusty.
+ uint8_t response_buffer_[4096];
+};
+
+} // namespace nvram
+
+#endif // TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
diff --git a/trusty/storage/interface/Android.mk b/trusty/storage/interface/Android.mk
new file mode 100644
index 0000000..15cb6f3
--- /dev/null
+++ b/trusty/storage/interface/Android.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2015 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtrustystorageinterface
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/trusty/storage/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
new file mode 100644
index 0000000..b196d88
--- /dev/null
+++ b/trusty/storage/interface/include/trusty/interface/storage.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2015-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*
+ * Storage port names
+ * @STORAGE_CLIENT_TD_PORT: Port used by clients that require tamper and
+ * rollback detection.
+ * @STORAGE_CLIENT_TDEA_PORT: Port used by clients that require storage before
+ * the non-secure os has booted.
+ * @STORAGE_CLIENT_TP_PORT: Port used by clients that require tamper proof
+ * storage. Note that non-secure code can prevent
+ read and write operations from succeeding, but
+ it cannot modify on-disk data.
+ * @STORAGE_DISK_PROXY_PORT: Port used by non-secure proxy server
+ */
+#define STORAGE_CLIENT_TD_PORT "com.android.trusty.storage.client.td"
+#define STORAGE_CLIENT_TDEA_PORT "com.android.trusty.storage.client.tdea"
+#define STORAGE_CLIENT_TP_PORT "com.android.trusty.storage.client.tp"
+#define STORAGE_DISK_PROXY_PORT "com.android.trusty.storage.proxy"
+
+enum storage_cmd {
+ STORAGE_REQ_SHIFT = 1,
+ STORAGE_RESP_BIT = 1,
+
+ STORAGE_RESP_MSG_ERR = STORAGE_RESP_BIT,
+
+ STORAGE_FILE_DELETE = 1 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_OPEN = 2 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_CLOSE = 3 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_READ = 4 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_WRITE = 5 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_GET_SIZE = 6 << STORAGE_REQ_SHIFT,
+ STORAGE_FILE_SET_SIZE = 7 << STORAGE_REQ_SHIFT,
+
+ STORAGE_RPMB_SEND = 8 << STORAGE_REQ_SHIFT,
+
+ /* transaction support */
+ STORAGE_END_TRANSACTION = 9 << STORAGE_REQ_SHIFT,
+};
+
+/**
+ * enum storage_err - error codes for storage protocol
+ * @STORAGE_NO_ERROR: all OK
+ * @STORAGE_ERR_GENERIC: unknown error. Can occur when there's an internal server
+ * error, e.g. the server runs out of memory or is in a bad state.
+ * @STORAGE_ERR_NOT_VALID: input not valid. May occur if the arguments passed
+ * into the command are not valid, for example if the file handle
+ * passed in is not a valid one.
+ * @STORAGE_ERR_UNIMPLEMENTED: the command passed in is not recognized
+ * @STORAGE_ERR_ACCESS: the file is not accessible in the requested mode
+ * @STORAGE_ERR_NOT_FOUND: the file was not found
+ * @STORAGE_ERR_EXIST the file exists when it shouldn't as in with OPEN_CREATE | OPEN_EXCLUSIVE.
+ * @STORAGE_ERR_TRANSACT returned by various operations to indicate that current transaction
+ * is in error state. Such state could be only cleared by sending
+ * STORAGE_END_TRANSACTION message.
+ */
+enum storage_err {
+ STORAGE_NO_ERROR = 0,
+ STORAGE_ERR_GENERIC = 1,
+ STORAGE_ERR_NOT_VALID = 2,
+ STORAGE_ERR_UNIMPLEMENTED = 3,
+ STORAGE_ERR_ACCESS = 4,
+ STORAGE_ERR_NOT_FOUND = 5,
+ STORAGE_ERR_EXIST = 6,
+ STORAGE_ERR_TRANSACT = 7,
+};
+
+/**
+ * storage_delete_flag - flags for controlling delete semantics
+ */
+enum storage_file_delete_flag {
+ STORAGE_FILE_DELETE_MASK = 0,
+};
+
+/**
+ * storage_file_flag - Flags to control 'open' semantics.
+ * @STORAGE_FILE_OPEN_CREATE: if this file does not exist, create it.
+ * @STORAGE_FILE_OPEN_CREATE_EXCLUSIVE: causes STORAGE_FILE_OPEN_CREATE to fail if the file
+ * already exists. Only meaningful if used in combination
+ * with STORAGE_FILE_OPEN_CREATE.
+ * @STORAGE_FILE_OPEN_TRUNCATE: if this file already exists, discard existing content
+ * and open it as a new file. No change in semantics if the
+ * file does not exist.
+ * @STORAGE_FILE_OPEN_MASK: mask for all open flags supported in current protocol.
+ * All other bits must be set to 0.
+ */
+enum storage_file_open_flag {
+ STORAGE_FILE_OPEN_CREATE = (1 << 0),
+ STORAGE_FILE_OPEN_CREATE_EXCLUSIVE = (1 << 1),
+ STORAGE_FILE_OPEN_TRUNCATE = (1 << 2),
+ STORAGE_FILE_OPEN_MASK = STORAGE_FILE_OPEN_CREATE |
+ STORAGE_FILE_OPEN_TRUNCATE |
+ STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+};
+
+/**
+ * enum storage_msg_flag - protocol-level flags in struct storage_msg
+ * @STORAGE_MSG_FLAG_BATCH: if set, command belongs to a batch transaction.
+ * No response will be sent by the server until
+ * it receives a command with this flag unset, at
+ * which point a cummulative result for all messages
+ * sent with STORAGE_MSG_FLAG_BATCH will be sent.
+ * This is only supported by the non-secure disk proxy
+ * server.
+ * @STORAGE_MSG_FLAG_PRE_COMMIT: if set, indicates that server need to commit
+ * pending changes before processing this message.
+ * @STORAGE_MSG_FLAG_POST_COMMIT: if set, indicates that server need to commit
+ * pending changes after processing this message.
+ * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE: if set, indicates that server need to commit
+ * current transaction after processing this message.
+ * It is an alias for STORAGE_MSG_FLAG_POST_COMMIT.
+ */
+enum storage_msg_flag {
+ STORAGE_MSG_FLAG_BATCH = 0x1,
+ STORAGE_MSG_FLAG_PRE_COMMIT = 0x2,
+ STORAGE_MSG_FLAG_POST_COMMIT = 0x4,
+ STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT,
+};
+
+/*
+ * The following declarations are the message-specific contents of
+ * the 'payload' element inside struct storage_msg.
+ */
+
+/**
+ * struct storage_file_delete_req - request format for STORAGE_FILE_DELETE
+ * @flags: currently unused, must be set to 0.
+ * @name: the name of the file
+ */
+struct storage_file_delete_req {
+ uint32_t flags;
+ char name[0];
+};
+
+/**
+ * struct storage_file_open_req - request format for STORAGE_FILE_OPEN
+ * @flags: any of enum storage_file_flag or'ed together
+ * @name: the name of the file
+ */
+struct storage_file_open_req {
+ uint32_t flags;
+ char name[0];
+};
+
+/**
+ * struct storage_file_open_resp - response format for STORAGE_FILE_OPEN
+ * @handle: opaque handle to the opened file. Only present on success.
+ */
+struct storage_file_open_resp {
+ uint32_t handle;
+};
+
+/**
+ * struct storage_file_close_req - request format for STORAGE_FILE_CLOSE
+ * @handle: the handle for the file to close
+ */
+struct storage_file_close_req {
+ uint32_t handle;
+};
+
+/**
+ * struct storage_file_read_req - request format for STORAGE_FILE_READ
+ * @handle: the handle for the file from which to read
+ * @size: the quantity of bytes to read from the file
+ * @offset: the offset in the file from whence to read
+ */
+struct storage_file_read_req {
+ uint32_t handle;
+ uint32_t size;
+ uint64_t offset;
+};
+
+/**
+ * struct storage_file_read_resp - response format for STORAGE_FILE_READ
+ * @data: beginning of data retrieved from file
+ */
+struct storage_file_read_resp {
+ uint8_t data[0];
+};
+
+/**
+ * struct storage_file_write_req - request format for STORAGE_FILE_WRITE
+ * @handle: the handle for the file to write to
+ * @offset: the offset in the file from whence to write
+ * @__reserved: unused, must be set to 0.
+ * @data: beginning of the data to be written
+ */
+struct storage_file_write_req {
+ uint64_t offset;
+ uint32_t handle;
+ uint32_t __reserved;
+ uint8_t data[0];
+};
+
+/**
+ * struct storage_file_get_size_req - request format for STORAGE_FILE_GET_SIZE
+ * @handle: handle for which the size is requested
+ */
+struct storage_file_get_size_req {
+ uint32_t handle;
+};
+
+/**
+ * struct storage_file_get_size_resp - response format for STORAGE_FILE_GET_SIZE
+ * @size: the size of the file
+ */
+struct storage_file_get_size_resp {
+ uint64_t size;
+};
+
+/**
+ * struct storage_file_set_size_req - request format for STORAGE_FILE_SET_SIZE
+ * @handle: the file handle
+ * @size: the desired size of the file
+ */
+struct storage_file_set_size_req {
+ uint64_t size;
+ uint32_t handle;
+};
+
+/**
+ * struct storage_rpmb_send_req - request format for STORAGE_RPMB_SEND
+ * @reliable_write_size: size in bytes of reliable write region
+ * @write_size: size in bytes of write region
+ * @read_size: number of bytes to read for a read request
+ * @__reserved: unused, must be set to 0
+ * @payload: start of reliable write region, followed by
+ * write region.
+ *
+ * Only used in proxy<->server interface.
+ */
+struct storage_rpmb_send_req {
+ uint32_t reliable_write_size;
+ uint32_t write_size;
+ uint32_t read_size;
+ uint32_t __reserved;
+ uint8_t payload[0];
+};
+
+/**
+ * struct storage_rpmb_send_resp: response type for STORAGE_RPMB_SEND
+ * @data: the data frames frames retrieved from the MMC.
+ */
+struct storage_rpmb_send_resp {
+ uint8_t data[0];
+};
+
+/**
+ * struct storage_msg - generic req/resp format for all storage commands
+ * @cmd: one of enum storage_cmd
+ * @op_id: client chosen operation identifier for an instance
+ * of a command or atomic grouping of commands (transaction).
+ * @flags: one or many of enum storage_msg_flag or'ed together.
+ * @size: total size of the message including this header
+ * @result: one of enum storage_err
+ * @__reserved: unused, must be set to 0.
+ * @payload: beginning of command specific message format
+ */
+struct storage_msg {
+ uint32_t cmd;
+ uint32_t op_id;
+ uint32_t flags;
+ uint32_t size;
+ int32_t result;
+ uint32_t __reserved;
+ uint8_t payload[0];
+};
+
diff --git a/trusty/storage/lib/Android.mk b/trusty/storage/lib/Android.mk
new file mode 100644
index 0000000..7e0fc9d
--- /dev/null
+++ b/trusty/storage/lib/Android.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2015 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtrustystorage
+
+LOCAL_SRC_FILES := \
+ storage.c \
+
+LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+LOCAL_STATIC_LIBRARIES := \
+ liblog \
+ libtrusty \
+ libtrustystorageinterface
+
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/trusty/storage/lib/include/trusty/lib/storage.h b/trusty/storage/lib/include/trusty/lib/storage.h
new file mode 100644
index 0000000..b8ddf67
--- /dev/null
+++ b/trusty/storage/lib/include/trusty/lib/storage.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <trusty/interface/storage.h>
+
+#define STORAGE_MAX_NAME_LENGTH_BYTES 159
+
+__BEGIN_DECLS
+
+typedef uint32_t storage_session_t;
+typedef uint64_t file_handle_t;
+typedef uint64_t storage_off_t;
+
+#define STORAGE_INVALID_SESSION ((storage_session_t)-1)
+
+/**
+ * storage_ops_flags - storage related operation flags
+ * @STORAGE_OP_COMPLETE: forces to commit current transaction
+ */
+enum storage_ops_flags {
+ STORAGE_OP_COMPLETE = 0x1,
+};
+
+/**
+ * storage_open_session() - Opens a storage session.
+ * @device: device node for talking with Trusty
+ * @session_p: pointer to location in which to store session handle
+ * in case of success.
+ *
+ * Return: 0 on success, or an error code < 0 on failure.
+ */
+int storage_open_session(const char *device, storage_session_t *session_p, const char *port);
+
+/**
+ * storage_close_session() - Closes the session.
+ * @session: the session to close
+ */
+void storage_close_session(storage_session_t session);
+
+/**
+ * storage_open_file() - Opens a file
+ * @session: the storage_session_t returned from a call to storage_open_session
+ * @handle_p: pointer to location in which to store file handle in case of success
+ * @name: a null-terminated string identifier of the file to open.
+ * Cannot be more than STORAGE_MAX_NAME_LENGTH_BYTES in length.
+ * @flags: A bitmask consisting any storage_file_flag value or'ed together:
+ * - STORAGE_FILE_OPEN_CREATE: if this file does not exist, create it.
+ * - STORAGE_FILE_OPEN_CREATE_EXCLUSIVE: when specified, opening file with
+ * STORAGE_OPEN_FILE_CREATE flag will
+ * fail if the file already exists.
+ * Only meaningful if used in combination
+ * with STORAGE_FILE_OPEN_CREATE flag.
+ * - STORAGE_FILE_OPEN_TRUNCATE: if this file already exists, discard existing
+ * content and open it as a new file. No change
+ * in semantics if the file does not exist.
+ * @opflags: a combination of @storage_op_flags
+ *
+ * Return: 0 on success, or an error code < 0 on failure.
+ */
+int storage_open_file(storage_session_t session, file_handle_t *handle_p,
+ const char *name, uint32_t flags, uint32_t opflags);
+
+/**
+ * storage_close_file() - Closes a file.
+ * @handle: the file_handle_t retrieved from storage_open_file
+ */
+void storage_close_file(file_handle_t handle);
+
+/**
+ * storage_delete_file - Deletes a file.
+ * @session: the storage_session_t returned from a call to storage_open_session
+ * @name: the name of the file to delete
+ * @opflags: a combination of @storage_op_flags
+ *
+ * Return: 0 on success, or an error code < 0 on failure.
+ */
+int storage_delete_file(storage_session_t session, const char *name,
+ uint32_t opflags);
+
+/**
+ * storage_read() - Reads a file at a given offset.
+ * @handle: the file_handle_t retrieved from storage_open_file
+ * @off: the start offset from whence to read in the file
+ * @buf: the buffer in which to write the data read
+ * @size: the size of buf and number of bytes to read
+ *
+ * Return: the number of bytes read on success, negative error code on failure
+ */
+ssize_t storage_read(file_handle_t handle,
+ storage_off_t off, void *buf, size_t size);
+
+/**
+ * storage_write() - Writes to a file at a given offset. Grows the file if necessary.
+ * @handle: the file_handle_t retrieved from storage_open_file
+ * @off: the start offset from whence to write in the file
+ * @buf: the buffer containing the data to write
+ * @size: the size of buf and number of bytes to write
+ * @opflags: a combination of @storage_op_flags
+ *
+ * Return: the number of bytes written on success, negative error code on failure
+ */
+ssize_t storage_write(file_handle_t handle,
+ storage_off_t off, const void *buf, size_t size,
+ uint32_t opflags);
+
+/**
+ * storage_set_file_size() - Sets the size of the file.
+ * @handle: the file_handle_t retrieved from storage_open_file
+ * @off: the number of bytes to set as the new size of the file
+ * @opflags: a combination of @storage_op_flags
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int storage_set_file_size(file_handle_t handle, storage_off_t file_size,
+ uint32_t opflags);
+
+/**
+ * storage_get_file_size() - Gets the size of the file.
+ * @session: the storage_session_t returned from a call to storage_open_session
+ * @handle: the file_handle_t retrieved from storage_open_file
+ * @size: pointer to storage_off_t in which to store the file size
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int storage_get_file_size(file_handle_t handle, storage_off_t *size);
+
+
+/**
+ * storage_end_transaction: End current transaction
+ * @session: the storage_session_t returned from a call to storage_open_session
+ * @complete: if true, commit current transaction, discard it otherwise
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int storage_end_transaction(storage_session_t session, bool complete);
+
+
+__END_DECLS
diff --git a/trusty/storage/lib/storage.c b/trusty/storage/lib/storage.c
new file mode 100644
index 0000000..915bc17
--- /dev/null
+++ b/trusty/storage/lib/storage.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "trusty_storage_client"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/uio.h>
+
+#include <log/log.h>
+#include <trusty/tipc.h>
+#include <trusty/lib/storage.h>
+
+#define MAX_CHUNK_SIZE 4040
+
+static inline file_handle_t make_file_handle(storage_session_t s, uint32_t fid)
+{
+ return ((uint64_t)s << 32) | fid;
+}
+
+static inline storage_session_t _to_session(file_handle_t fh)
+{
+ return (storage_session_t)(fh >> 32);
+}
+
+static inline uint32_t _to_handle(file_handle_t fh)
+{
+ return (uint32_t) fh;
+}
+
+static inline uint32_t _to_msg_flags(uint32_t opflags)
+{
+ uint32_t msg_flags = 0;
+
+ if (opflags & STORAGE_OP_COMPLETE)
+ msg_flags |= STORAGE_MSG_FLAG_TRANSACT_COMPLETE;
+
+ return msg_flags;
+}
+
+static ssize_t check_response(struct storage_msg *msg, ssize_t res)
+{
+ if (res < 0)
+ return res;
+
+ if ((size_t)res < sizeof(*msg)) {
+ ALOGE("invalid msg length (%zd < %zd)\n", res, sizeof(*msg));
+ return -EIO;
+ }
+
+ ALOGV("cmd 0x%x: server returned %u\n", msg->cmd, msg->result);
+
+ switch(msg->result) {
+ case STORAGE_NO_ERROR:
+ return res - sizeof(*msg);
+
+ case STORAGE_ERR_NOT_FOUND:
+ return -ENOENT;
+
+ case STORAGE_ERR_EXIST:
+ return -EEXIST;
+
+ case STORAGE_ERR_NOT_VALID:
+ return -EINVAL;
+
+ case STORAGE_ERR_UNIMPLEMENTED:
+ ALOGE("cmd 0x%x: is unhandles command\n", msg->cmd);
+ return -EINVAL;
+
+ case STORAGE_ERR_ACCESS:
+ return -EACCES;
+
+ case STORAGE_ERR_TRANSACT:
+ return -EBUSY;
+
+ case STORAGE_ERR_GENERIC:
+ ALOGE("cmd 0x%x: internal server error\n", msg->cmd);
+ return -EIO;
+
+ default:
+ ALOGE("cmd 0x%x: unhandled server response %u\n",
+ msg->cmd, msg->result);
+ }
+
+ return -EIO;
+}
+
+static ssize_t send_reqv(storage_session_t session,
+ const struct iovec *tx_iovs, uint tx_iovcnt,
+ const struct iovec *rx_iovs, uint rx_iovcnt)
+{
+ ssize_t rc;
+
+ rc = writev(session, tx_iovs, tx_iovcnt);
+ if (rc < 0) {
+ rc = -errno;
+ ALOGE("failed to send request: %s\n", strerror(errno));
+ return rc;
+ }
+
+ rc = readv(session, rx_iovs, rx_iovcnt);
+ if (rc < 0) {
+ rc = -errno;
+ ALOGE("failed to recv response: %s\n", strerror(errno));
+ return rc;
+ }
+
+ return rc;
+}
+
+int storage_open_session(const char *device, storage_session_t *session_p,
+ const char *port)
+{
+ int rc = tipc_connect(device, port);
+ if (rc < 0)
+ return rc;
+ *session_p = (storage_session_t) rc;
+ return 0;
+}
+
+void storage_close_session(storage_session_t session)
+{
+ tipc_close(session);
+}
+
+
+int storage_open_file(storage_session_t session, file_handle_t *handle_p, const char *name,
+ uint32_t flags, uint32_t opflags)
+{
+ struct storage_msg msg = { .cmd = STORAGE_FILE_OPEN, .flags = _to_msg_flags(opflags)};
+ struct storage_file_open_req req = { .flags = flags };
+ struct iovec tx[3] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}, {(void *)name, strlen(name)}};
+ struct storage_file_open_resp rsp = { 0 };
+ struct iovec rx[2] = {{&msg, sizeof(msg)}, {&rsp, sizeof(rsp)}};
+
+ ssize_t rc = send_reqv(session, tx, 3, rx, 2);
+ rc = check_response(&msg, rc);
+ if (rc < 0)
+ return rc;
+
+ if ((size_t)rc != sizeof(rsp)) {
+ ALOGE("%s: invalid response length (%zd != %zd)\n", __func__, rc, sizeof(rsp));
+ return -EIO;
+ }
+
+ *handle_p = make_file_handle(session, rsp.handle);
+ return 0;
+}
+
+void storage_close_file(file_handle_t fh)
+{
+ struct storage_msg msg = { .cmd = STORAGE_FILE_CLOSE };
+ struct storage_file_close_req req = { .handle = _to_handle(fh)};
+ struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};
+ struct iovec rx[1] = {{&msg, sizeof(msg)}};
+
+ ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 1);
+ rc = check_response(&msg, rc);
+ if (rc < 0) {
+ ALOGE("close file failed (%d)\n", (int)rc);
+ }
+}
+
+int storage_delete_file(storage_session_t session, const char *name, uint32_t opflags)
+{
+ struct storage_msg msg = { .cmd = STORAGE_FILE_DELETE, .flags = _to_msg_flags(opflags)};
+ struct storage_file_delete_req req = { .flags = 0, };
+ struct iovec tx[3] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}, {(void *)name, strlen(name)}};
+ struct iovec rx[1] = {{&msg, sizeof(msg)}};
+
+ ssize_t rc = send_reqv(session, tx, 3, rx, 1);
+ return check_response(&msg, rc);
+}
+
+static int _read_chunk(file_handle_t fh, storage_off_t off, void *buf, size_t size)
+{
+ struct storage_msg msg = { .cmd = STORAGE_FILE_READ };
+ struct storage_file_read_req req = { .handle = _to_handle(fh), .size = size, .offset = off };
+ struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};
+ struct iovec rx[2] = {{&msg, sizeof(msg)}, {buf, size}};
+
+ ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 2);
+ return check_response(&msg, rc);
+}
+
+ssize_t storage_read(file_handle_t fh, storage_off_t off, void *buf, size_t size)
+{
+ int rc;
+ size_t bytes_read = 0;
+ size_t chunk = MAX_CHUNK_SIZE;
+ uint8_t *ptr = buf;
+
+ while (size) {
+ if (chunk > size)
+ chunk = size;
+ rc = _read_chunk(fh, off, ptr, chunk);
+ if (rc < 0)
+ return rc;
+ if (rc == 0)
+ break;
+ off += rc;
+ ptr += rc;
+ bytes_read += rc;
+ size -= rc;
+ }
+ return bytes_read;
+}
+
+static int _write_req(file_handle_t fh, storage_off_t off,
+ const void *buf, size_t size, uint32_t msg_flags)
+{
+ struct storage_msg msg = { .cmd = STORAGE_FILE_WRITE, .flags = msg_flags, };
+ struct storage_file_write_req req = { .handle = _to_handle(fh), .offset = off, };
+ struct iovec tx[3] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}, {(void *)buf, size}};
+ struct iovec rx[1] = {{&msg, sizeof(msg)}};
+
+ ssize_t rc = send_reqv(_to_session(fh), tx, 3, rx, 1);
+ rc = check_response(&msg, rc);
+ return rc < 0 ? rc : size;
+}
+
+ssize_t storage_write(file_handle_t fh, storage_off_t off,
+ const void *buf, size_t size, uint32_t opflags)
+{
+ int rc;
+ size_t bytes_written = 0;
+ size_t chunk = MAX_CHUNK_SIZE;
+ const uint8_t *ptr = buf;
+ uint32_t msg_flags = _to_msg_flags(opflags & ~STORAGE_OP_COMPLETE);
+
+ while (size) {
+ if (chunk >= size) {
+ /* last chunk in sequence */
+ chunk = size;
+ msg_flags = _to_msg_flags(opflags);
+ }
+ rc = _write_req(fh, off, ptr, chunk, msg_flags);
+ if (rc < 0)
+ return rc;
+ if ((size_t)rc != chunk) {
+ ALOGE("got partial write (%d)\n", (int)rc);
+ return -EIO;
+ }
+ off += chunk;
+ ptr += chunk;
+ bytes_written += chunk;
+ size -= chunk;
+ }
+ return bytes_written;
+}
+
+int storage_set_file_size(file_handle_t fh, storage_off_t file_size, uint32_t opflags)
+{
+ struct storage_msg msg = { .cmd = STORAGE_FILE_SET_SIZE, .flags = _to_msg_flags(opflags)};
+ struct storage_file_set_size_req req = { .handle = _to_handle(fh), .size = file_size, };
+ struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};
+ struct iovec rx[1] = {{&msg, sizeof(msg)}};
+
+ ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 1);
+ return check_response(&msg, rc);
+}
+
+int storage_get_file_size(file_handle_t fh, storage_off_t *size_p)
+{
+ struct storage_msg msg = { .cmd = STORAGE_FILE_GET_SIZE };
+ struct storage_file_get_size_req req = { .handle = _to_handle(fh), };
+ struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};
+ struct storage_file_get_size_resp rsp;
+ struct iovec rx[2] = {{&msg, sizeof(msg)}, {&rsp, sizeof(rsp)}};
+
+ ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 2);
+ rc = check_response(&msg, rc);
+ if (rc < 0)
+ return rc;
+
+ if ((size_t)rc != sizeof(rsp)) {
+ ALOGE("%s: invalid response length (%zd != %zd)\n", __func__, rc, sizeof(rsp));
+ return -EIO;
+ }
+
+ *size_p = rsp.size;
+ return 0;
+}
+
+int storage_end_transaction(storage_session_t session, bool complete)
+{
+ struct storage_msg msg = {
+ .cmd = STORAGE_END_TRANSACTION,
+ .flags = complete ? STORAGE_MSG_FLAG_TRANSACT_COMPLETE : 0,
+ };
+ struct iovec iov = {&msg, sizeof(msg)};
+
+ ssize_t rc = send_reqv(session, &iov, 1, &iov, 1);
+ return check_response(&msg, rc);
+}
diff --git a/trusty/storage/proxy/Android.mk b/trusty/storage/proxy/Android.mk
new file mode 100644
index 0000000..745e302
--- /dev/null
+++ b/trusty/storage/proxy/Android.mk
@@ -0,0 +1,41 @@
+#
+# Copyright (C) 2016 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := storageproxyd
+
+LOCAL_C_INCLUDES += bionic/libc/kernel/uapi
+
+LOCAL_SRC_FILES := \
+ ipc.c \
+ rpmb.c \
+ storage.c \
+ proxy.c
+
+LOCAL_CLFAGS = -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+
+LOCAL_STATIC_LIBRARIES := \
+ libtrustystorageinterface \
+ libtrusty
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/trusty/storage/proxy/ipc.c b/trusty/storage/proxy/ipc.c
new file mode 100644
index 0000000..57cf600
--- /dev/null
+++ b/trusty/storage/proxy/ipc.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <trusty/tipc.h>
+
+#include "ipc.h"
+#include "log.h"
+
+#define MAX_RECONNECT_RETRY_COUNT 5
+#define TRUSTY_RECONNECT_TIMEOUT_SEC 5
+
+static int tipc_fd = -1;
+
+int ipc_connect(const char *device, const char *port)
+{
+ int rc;
+ uint retry_cnt = 0;
+
+ assert(tipc_fd == -1);
+
+ while(true) {
+ rc = tipc_connect(device, port);
+ if (rc >= 0)
+ break;
+
+ ALOGE("failed (%d) to connect to storage server\n", rc);
+ if (++retry_cnt > MAX_RECONNECT_RETRY_COUNT) {
+ ALOGE("max number of reconnect retries (%d) has been reached\n",
+ retry_cnt);
+ return -1;
+ }
+ sleep(TRUSTY_RECONNECT_TIMEOUT_SEC);
+ }
+ tipc_fd = rc;
+ return 0;
+}
+
+void ipc_disconnect(void)
+{
+ assert(tipc_fd >= 0);
+
+ tipc_close(tipc_fd);
+ tipc_fd = -1;
+}
+
+ssize_t ipc_get_msg(struct storage_msg *msg, void *req_buf, size_t req_buf_len)
+{
+ ssize_t rc;
+ struct iovec iovs[2] = {{msg, sizeof(*msg)}, {req_buf, req_buf_len}};
+
+ assert(tipc_fd >= 0);
+
+ rc = readv(tipc_fd, iovs, 2);
+ if (rc < 0) {
+ ALOGE("failed to read request: %s\n", strerror(errno));
+ return rc;
+ }
+
+ /* check for minimum size */
+ if ((size_t)rc < sizeof(*msg)) {
+ ALOGE("message is too short (%zu bytes received)\n", rc);
+ return -1;
+ }
+
+ /* check for message completeness */
+ if (msg->size != (uint32_t)rc) {
+ ALOGE("inconsistent message size [cmd=%d] (%u != %u)\n",
+ msg->cmd, msg->size, (uint32_t)rc);
+ return -1;
+ }
+
+ return rc - sizeof(*msg);
+}
+
+int ipc_respond(struct storage_msg *msg, void *out, size_t out_size)
+{
+ ssize_t rc;
+ struct iovec iovs[2] = {{msg, sizeof(*msg)}, {out, out_size}};
+
+ assert(tipc_fd >= 0);
+
+ msg->cmd |= STORAGE_RESP_BIT;
+
+ rc = writev(tipc_fd, iovs, out ? 2 : 1);
+ if (rc < 0) {
+ ALOGE("error sending response 0x%x: %s\n",
+ msg->cmd, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
diff --git a/trusty/storage/proxy/ipc.h b/trusty/storage/proxy/ipc.h
new file mode 100644
index 0000000..2e366bb
--- /dev/null
+++ b/trusty/storage/proxy/ipc.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <trusty/interface/storage.h>
+
+int ipc_connect(const char *device, const char *service_name);
+void ipc_disconnect(void);
+ssize_t ipc_get_msg(struct storage_msg *msg, void *req_buf, size_t req_buf_len);
+int ipc_respond(struct storage_msg *msg, void *out, size_t out_size);
diff --git a/trusty/storage/proxy/log.h b/trusty/storage/proxy/log.h
new file mode 100644
index 0000000..c81beab
--- /dev/null
+++ b/trusty/storage/proxy/log.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storageproxyd"
+
+#include <log/log.h>
+
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
new file mode 100644
index 0000000..d645ac0
--- /dev/null
+++ b/trusty/storage/proxy/proxy.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "ipc.h"
+#include "log.h"
+#include "rpmb.h"
+#include "storage.h"
+
+#define REQ_BUFFER_SIZE 4096
+static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
+
+static const char *ss_data_root;
+static const char *trusty_devname;
+static const char *rpmb_devname;
+static const char *ss_srv_name = STORAGE_DISK_PROXY_PORT;
+
+static const char *_sopts = "hp:d:r:";
+static const struct option _lopts[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"trusty_dev", required_argument, NULL, 'd'},
+ {"data_path", required_argument, NULL, 'p'},
+ {"rpmb_dev", required_argument, NULL, 'r'},
+ {0, 0, 0, 0}
+};
+
+static void show_usage_and_exit(int code)
+{
+ ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
+ exit(code);
+}
+
+static int drop_privs(void)
+{
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
+ return -1;
+ }
+
+ /*
+ * ensure we're running as the system user
+ */
+ if (setgid(AID_SYSTEM) != 0) {
+ return -1;
+ }
+
+ if (setuid(AID_SYSTEM) != 0) {
+ return -1;
+ }
+
+ /*
+ * drop all capabilities except SYS_RAWIO
+ */
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted = CAP_TO_MASK(CAP_SYS_RAWIO);
+ capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective = CAP_TO_MASK(CAP_SYS_RAWIO);
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ return -1;
+ }
+
+ /* no-execute for user, no access for group and other */
+ umask(S_IXUSR | S_IRWXG | S_IRWXO);
+
+ return 0;
+}
+
+static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
+{
+ int rc;
+
+ if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
+ (msg->cmd != STORAGE_RPMB_SEND)) {
+ /*
+ * handling post commit messages on non rpmb commands are not
+ * implemented as there is no use case for this yet.
+ */
+ ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd);
+ msg->result = STORAGE_ERR_UNIMPLEMENTED;
+ return ipc_respond(msg, NULL, 0);
+ }
+
+ if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {
+ rc = storage_sync_checkpoint();
+ if (rc < 0) {
+ msg->result = STORAGE_ERR_GENERIC;
+ return ipc_respond(msg, NULL, 0);
+ }
+ }
+
+ switch (msg->cmd) {
+ case STORAGE_FILE_DELETE:
+ rc = storage_file_delete(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_OPEN:
+ rc = storage_file_open(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_CLOSE:
+ rc = storage_file_close(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_WRITE:
+ rc = storage_file_write(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_READ:
+ rc = storage_file_read(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_GET_SIZE:
+ rc = storage_file_get_size(msg, req, req_len);
+ break;
+
+ case STORAGE_FILE_SET_SIZE:
+ rc = storage_file_set_size(msg, req, req_len);
+ break;
+
+ case STORAGE_RPMB_SEND:
+ rc = rpmb_send(msg, req, req_len);
+ break;
+
+ default:
+ ALOGE("unhandled command 0x%x\n", msg->cmd);
+ msg->result = STORAGE_ERR_UNIMPLEMENTED;
+ rc = 1;
+ }
+
+ if (rc > 0) {
+ /* still need to send response */
+ rc = ipc_respond(msg, NULL, 0);
+ }
+ return rc;
+}
+
+static int proxy_loop(void)
+{
+ ssize_t rc;
+ struct storage_msg msg;
+
+ /* enter main message handling loop */
+ while (true) {
+
+ /* get incoming message */
+ rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
+ if (rc < 0)
+ return rc;
+
+ /* handle request */
+ req_buffer[rc] = 0; /* force zero termination */
+ rc = handle_req(&msg, req_buffer, rc);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static void parse_args(int argc, char *argv[])
+{
+ int opt;
+ int oidx = 0;
+
+ while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
+ switch (opt) {
+
+ case 'd':
+ trusty_devname = strdup(optarg);
+ break;
+
+ case 'p':
+ ss_data_root = strdup(optarg);
+ break;
+
+ case 'r':
+ rpmb_devname = strdup(optarg);
+ break;
+
+ default:
+ ALOGE("unrecognized option (%c):\n", opt);
+ show_usage_and_exit(EXIT_FAILURE);
+ }
+ }
+
+ if (ss_data_root == NULL ||
+ trusty_devname == NULL ||
+ rpmb_devname == NULL) {
+ ALOGE("missing required argument(s)\n");
+ show_usage_and_exit(EXIT_FAILURE);
+ }
+
+ ALOGI("starting storageproxyd\n");
+ ALOGI("storage data root: %s\n", ss_data_root);
+ ALOGI("trusty dev: %s\n", trusty_devname);
+ ALOGI("rpmb dev: %s\n", rpmb_devname);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ uint retry_cnt;
+
+ /* drop privileges */
+ if (drop_privs() < 0)
+ return EXIT_FAILURE;
+
+ /* parse arguments */
+ parse_args(argc, argv);
+
+ /* initialize secure storage directory */
+ rc = storage_init(ss_data_root);
+ if (rc < 0)
+ return EXIT_FAILURE;
+
+ /* open rpmb device */
+ rc = rpmb_open(rpmb_devname);
+ if (rc < 0)
+ return EXIT_FAILURE;
+
+ /* connect to Trusty secure storage server */
+ rc = ipc_connect(trusty_devname, ss_srv_name);
+ if (rc < 0)
+ return EXIT_FAILURE;
+
+ /* enter main loop */
+ rc = proxy_loop();
+ ALOGE("exiting proxy loop with status (%d)\n", rc);
+
+ ipc_disconnect();
+ rpmb_close();
+
+ return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
new file mode 100644
index 0000000..9c79105
--- /dev/null
+++ b/trusty/storage/proxy/rpmb.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <linux/major.h>
+#include <linux/mmc/ioctl.h>
+
+#include "ipc.h"
+#include "log.h"
+#include "rpmb.h"
+#include "storage.h"
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_RELIABLE_WRITE_FLAG (1 << 31)
+
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_CRC (1 << 2)
+#define MMC_RSP_OPCODE (1 << 4)
+#define MMC_CMD_ADTC (1 << 5)
+#define MMC_RSP_SPI_S1 (1 << 7)
+#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
+
+#define MMC_WRITE_FLAG_R 0
+#define MMC_WRITE_FLAG_W 1
+#define MMC_WRITE_FLAG_RELW (MMC_WRITE_FLAG_W | MMC_RELIABLE_WRITE_FLAG)
+
+#define MMC_BLOCK_SIZE 512
+
+static int rpmb_fd = -1;
+static uint8_t read_buf[4096];
+
+#ifdef RPMB_DEBUG
+
+static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
+{
+ size_t i;
+
+ printf("%s @%p [%zu]", prefix, buf, size);
+ for (i = 0; i < size; i++) {
+ if (i && i % 32 == 0)
+ printf("\n%*s", (int) strlen(prefix), "");
+ printf(" %02x", buf[i]);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+#endif
+
+
+int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
+{
+ int rc;
+ struct {
+ struct mmc_ioc_multi_cmd multi;
+ struct mmc_ioc_cmd cmd_buf[3];
+ } mmc = {};
+ struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
+ const struct storage_rpmb_send_req *req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
+ req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t expected_len =
+ sizeof(*req) + req->reliable_write_size + req->write_size;
+ if (req_len != expected_len) {
+ ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
+ req_len, expected_len);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ const uint8_t *write_buf = req->payload;
+ if (req->reliable_write_size) {
+ if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ cmd->write_flag = MMC_WRITE_FLAG_RELW;
+ cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd->blksz = MMC_BLOCK_SIZE;
+ cmd->blocks = req->reliable_write_size / MMC_BLOCK_SIZE;
+ mmc_ioc_cmd_set_data((*cmd), write_buf);
+#ifdef RPMB_DEBUG
+ ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
+ print_buf("request: ", write_buf, req->reliable_write_size);
+#endif
+ write_buf += req->reliable_write_size;
+ mmc.multi.num_of_cmds++;
+ cmd++;
+ }
+
+ if (req->write_size) {
+ if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid write size %u\n", req->write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ cmd->write_flag = MMC_WRITE_FLAG_W;
+ cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd->blksz = MMC_BLOCK_SIZE;
+ cmd->blocks = req->write_size / MMC_BLOCK_SIZE;
+ mmc_ioc_cmd_set_data((*cmd), write_buf);
+#ifdef RPMB_DEBUG
+ ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
+ print_buf("request: ", write_buf, req->write_size);
+#endif
+ write_buf += req->write_size;
+ mmc.multi.num_of_cmds++;
+ cmd++;
+ }
+
+ if (req->read_size) {
+ if (req->read_size % MMC_BLOCK_SIZE != 0 ||
+ req->read_size > sizeof(read_buf)) {
+ ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ cmd->write_flag = MMC_WRITE_FLAG_R;
+ cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
+ cmd->blksz = MMC_BLOCK_SIZE;
+ cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
+ mmc_ioc_cmd_set_data((*cmd), read_buf);
+#ifdef RPMB_DEBUG
+ ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
+#endif
+ mmc.multi.num_of_cmds++;
+ cmd++;
+ }
+
+ rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+ if (rc < 0) {
+ ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+#ifdef RPMB_DEBUG
+ if (req->read_size)
+ print_buf("response: ", read_buf, req->read_size);
+#endif
+
+ if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
+ /*
+ * Nothing todo for post msg commit request as MMC_IOC_MULTI_CMD
+ * is fully synchronous in this implementation.
+ */
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+ return ipc_respond(msg, read_buf, req->read_size);
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int rpmb_open(const char *rpmb_devname)
+{
+ int rc;
+
+ rc = open(rpmb_devname, O_RDWR, 0);
+ if (rc < 0) {
+ ALOGE("unable (%d) to open rpmb device '%s': %s\n",
+ errno, rpmb_devname, strerror(errno));
+ return rc;
+ }
+ rpmb_fd = rc;
+ return 0;
+}
+
+void rpmb_close(void)
+{
+ close(rpmb_fd);
+ rpmb_fd = -1;
+}
+
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
new file mode 100644
index 0000000..85cff44
--- /dev/null
+++ b/trusty/storage/proxy/rpmb.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <trusty/interface/storage.h>
+
+int rpmb_open(const char *rpmb_devname);
+int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len);
+void rpmb_close(void);
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
new file mode 100644
index 0000000..c61e89d
--- /dev/null
+++ b/trusty/storage/proxy/storage.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "ipc.h"
+#include "storage.h"
+
+#define FD_TBL_SIZE 64
+#define MAX_READ_SIZE 4096
+
+enum sync_state {
+ SS_UNUSED = -1,
+ SS_CLEAN = 0,
+ SS_DIRTY = 1,
+};
+
+static int ssdir_fd = -1;
+static const char *ssdir_name;
+
+static enum sync_state fs_state;
+static enum sync_state dir_state;
+static enum sync_state fd_state[FD_TBL_SIZE];
+
+static struct {
+ struct storage_file_read_resp hdr;
+ uint8_t data[MAX_READ_SIZE];
+} read_rsp;
+
+static uint32_t insert_fd(int open_flags, int fd)
+{
+ uint32_t handle = fd;
+
+ if (open_flags & O_CREAT) {
+ dir_state = SS_DIRTY;
+ }
+
+ if (handle < FD_TBL_SIZE) {
+ fd_state[fd] = SS_CLEAN; /* fd clean */
+ if (open_flags & O_TRUNC) {
+ fd_state[fd] = SS_DIRTY; /* set fd dirty */
+ }
+ } else {
+ ALOGW("%s: untracked fd %u\n", __func__, fd);
+ if (open_flags & (O_TRUNC | O_CREAT)) {
+ fs_state = SS_DIRTY;
+ }
+ }
+ return handle;
+}
+
+static int lookup_fd(uint32_t handle, bool dirty)
+{
+ if (dirty) {
+ if (handle < FD_TBL_SIZE) {
+ fd_state[handle] = SS_DIRTY;
+ } else {
+ fs_state = SS_DIRTY;
+ }
+ }
+ return handle;
+}
+
+static int remove_fd(uint32_t handle)
+{
+ if (handle < FD_TBL_SIZE) {
+ fd_state[handle] = SS_UNUSED; /* set to uninstalled */
+ }
+ return handle;
+}
+
+static enum storage_err translate_errno(int error)
+{
+ enum storage_err result;
+ switch (error) {
+ case 0:
+ result = STORAGE_NO_ERROR;
+ break;
+ case EBADF:
+ case EINVAL:
+ case ENOTDIR:
+ case EISDIR:
+ case ENAMETOOLONG:
+ result = STORAGE_ERR_NOT_VALID;
+ break;
+ case ENOENT:
+ result = STORAGE_ERR_NOT_FOUND;
+ break;
+ case EEXIST:
+ result = STORAGE_ERR_EXIST;
+ break;
+ case EPERM:
+ case EACCES:
+ result = STORAGE_ERR_ACCESS;
+ break;
+ default:
+ result = STORAGE_ERR_GENERIC;
+ break;
+ }
+
+ return result;
+}
+
+static ssize_t write_with_retry(int fd, const void *buf_, size_t size, off_t offset)
+{
+ ssize_t rc;
+ const uint8_t *buf = buf_;
+
+ while (size > 0) {
+ rc = TEMP_FAILURE_RETRY(pwrite(fd, buf, size, offset));
+ if (rc < 0)
+ return rc;
+ size -= rc;
+ buf += rc;
+ offset += rc;
+ }
+ return 0;
+}
+
+static ssize_t read_with_retry(int fd, void *buf_, size_t size, off_t offset)
+{
+ ssize_t rc;
+ size_t rcnt = 0;
+ uint8_t *buf = buf_;
+
+ while (size > 0) {
+ rc = TEMP_FAILURE_RETRY(pread(fd, buf, size, offset));
+ if (rc < 0)
+ return rc;
+ if (rc == 0)
+ break;
+ size -= rc;
+ buf += rc;
+ offset += rc;
+ rcnt += rc;
+ }
+ return rcnt;
+}
+
+int storage_file_delete(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ char *path = NULL;
+ const struct storage_file_delete_req *req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd < %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t fname_len = strlen(req->name);
+ if (fname_len != req_len - sizeof(*req)) {
+ ALOGE("%s: invalid filename length (%zd != %zd)\n",
+ __func__, fname_len, req_len - sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
+ if (rc < 0) {
+ ALOGE("%s: asprintf failed\n", __func__);
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+
+ dir_state = SS_DIRTY;
+ rc = unlink(path);
+ if (rc < 0) {
+ rc = errno;
+ if (errno == ENOENT) {
+ ALOGV("%s: error (%d) unlinking file '%s'\n",
+ __func__, rc, path);
+ } else {
+ ALOGE("%s: error (%d) unlinking file '%s'\n",
+ __func__, rc, path);
+ }
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ ALOGV("%s: \"%s\"\n", __func__, path);
+ msg->result = STORAGE_NO_ERROR;
+
+err_response:
+ if (path)
+ free(path);
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_open(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ char *path = NULL;
+ const struct storage_file_open_req *req = r;
+ struct storage_file_open_resp resp = {0};
+
+ if (req_len < sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd < %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t fname_len = strlen(req->name);
+ if (fname_len != req_len - sizeof(*req)) {
+ ALOGE("%s: invalid filename length (%zd != %zd)\n",
+ __func__, fname_len, req_len - sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
+ if (rc < 0) {
+ ALOGE("%s: asprintf failed\n", __func__);
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+
+ int open_flags = O_RDWR;
+
+ if (req->flags & STORAGE_FILE_OPEN_TRUNCATE)
+ open_flags |= O_TRUNC;
+
+ if (req->flags & STORAGE_FILE_OPEN_CREATE) {
+ /* open or create */
+ if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {
+ /* create exclusive */
+ open_flags |= O_CREAT | O_EXCL;
+ rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+ } else {
+ /* try open first */
+ rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+ if (rc == -1 && errno == ENOENT) {
+ /* then try open with O_CREATE */
+ open_flags |= O_CREAT;
+ rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+ }
+
+ }
+ } else {
+ /* open an existing file */
+ rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+ }
+
+ if (rc < 0) {
+ rc = errno;
+ if (errno == EEXIST || errno == ENOENT) {
+ ALOGV("%s: failed to open file \"%s\": %s\n",
+ __func__, path, strerror(errno));
+ } else {
+ ALOGE("%s: failed to open file \"%s\": %s\n",
+ __func__, path, strerror(errno));
+ }
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+ free(path);
+
+ /* at this point rc contains storage file fd */
+ msg->result = STORAGE_NO_ERROR;
+ resp.handle = insert_fd(open_flags, rc);
+ ALOGV("%s: \"%s\": fd = %u: handle = %d\n",
+ __func__, path, rc, resp.handle);
+
+ return ipc_respond(msg, &resp, sizeof(resp));
+
+err_response:
+ if (path)
+ free(path);
+ return ipc_respond(msg, NULL, 0);
+}
+
+int storage_file_close(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ const struct storage_file_close_req *req = r;
+
+ if (req_len != sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd != %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int fd = remove_fd(req->handle);
+ ALOGV("%s: handle = %u: fd = %u\n", __func__, req->handle, fd);
+
+ int rc = fsync(fd);
+ if (rc < 0) {
+ rc = errno;
+ ALOGE("%s: fsync failed for fd=%u: %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ rc = close(fd);
+ if (rc < 0) {
+ rc = errno;
+ ALOGE("%s: close failed for fd=%u: %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_write(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ int rc;
+ const struct storage_file_write_req *req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd < %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int fd = lookup_fd(req->handle, true);
+ if (write_with_retry(fd, &req->data[0], req_len - sizeof(*req),
+ req->offset) < 0) {
+ rc = errno;
+ ALOGW("%s: error writing file (fd=%d): %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_read(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ int rc;
+ const struct storage_file_read_req *req = r;
+
+ if (req_len != sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd != %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (req->size > MAX_READ_SIZE) {
+ ALOGW("%s: request is too large (%zd > %zd) - refusing\n",
+ __func__, req->size, MAX_READ_SIZE);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int fd = lookup_fd(req->handle, false);
+ ssize_t read_res = read_with_retry(fd, read_rsp.hdr.data, req->size,
+ (off_t)req->offset);
+ if (read_res < 0) {
+ rc = errno;
+ ALOGW("%s: error reading file (fd=%d): %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+ return ipc_respond(msg, &read_rsp, read_res + sizeof(read_rsp.hdr));
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_get_size(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ const struct storage_file_get_size_req *req = r;
+ struct storage_file_get_size_resp resp = {0};
+
+ if (req_len != sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd != %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ struct stat stat;
+ int fd = lookup_fd(req->handle, false);
+ int rc = fstat(fd, &stat);
+ if (rc < 0) {
+ rc = errno;
+ ALOGE("%s: error stat'ing file (fd=%d): %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ resp.size = stat.st_size;
+ msg->result = STORAGE_NO_ERROR;
+ return ipc_respond(msg, &resp, sizeof(resp));
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+
+int storage_file_set_size(struct storage_msg *msg,
+ const void *r, size_t req_len)
+{
+ const struct storage_file_set_size_req *req = r;
+
+ if (req_len != sizeof(*req)) {
+ ALOGE("%s: invalid request length (%zd != %zd)\n",
+ __func__, req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ int fd = lookup_fd(req->handle, true);
+ int rc = TEMP_FAILURE_RETRY(ftruncate(fd, req->size));
+ if (rc < 0) {
+ rc = errno;
+ ALOGE("%s: error truncating file (fd=%d): %s\n",
+ __func__, fd, strerror(errno));
+ msg->result = translate_errno(rc);
+ goto err_response;
+ }
+
+ msg->result = STORAGE_NO_ERROR;
+
+err_response:
+ return ipc_respond(msg, NULL, 0);
+}
+
+int storage_init(const char *dirname)
+{
+ fs_state = SS_CLEAN;
+ dir_state = SS_CLEAN;
+ for (uint i = 0; i < FD_TBL_SIZE; i++) {
+ fd_state[i] = SS_UNUSED; /* uninstalled */
+ }
+
+ ssdir_fd = open(dirname, O_RDONLY);
+ if (ssdir_fd < 0) {
+ ALOGE("failed to open ss root dir \"%s\": %s\n",
+ dirname, strerror(errno));
+ return -1;
+ }
+ ssdir_name = dirname;
+ return 0;
+}
+
+int storage_sync_checkpoint(void)
+{
+ int rc;
+
+ /* sync fd table and reset it to clean state first */
+ for (uint fd = 0; fd < FD_TBL_SIZE; fd++) {
+ if (fd_state[fd] == SS_DIRTY) {
+ if (fs_state == SS_CLEAN) {
+ /* need to sync individual fd */
+ rc = fsync(fd);
+ if (rc < 0) {
+ ALOGE("fsync for fd=%d failed: %s\n", fd, strerror(errno));
+ return rc;
+ }
+ }
+ fd_state[fd] = SS_CLEAN; /* set to clean */
+ }
+ }
+
+ /* check if we need to sync the directory */
+ if (dir_state == SS_DIRTY) {
+ if (fs_state == SS_CLEAN) {
+ rc = fsync(ssdir_fd);
+ if (rc < 0) {
+ ALOGE("fsync for ssdir failed: %s\n", strerror(errno));
+ return rc;
+ }
+ }
+ dir_state = SS_CLEAN; /* set to clean */
+ }
+
+ /* check if we need to sync the whole fs */
+ if (fs_state == SS_DIRTY) {
+ rc = syscall(SYS_syncfs, ssdir_fd);
+ if (rc < 0) {
+ ALOGE("syncfs failed: %s\n", strerror(errno));
+ return rc;
+ }
+ fs_state = SS_CLEAN;
+ }
+
+ return 0;
+}
+
diff --git a/trusty/storage/proxy/storage.h b/trusty/storage/proxy/storage.h
new file mode 100644
index 0000000..5a670d4
--- /dev/null
+++ b/trusty/storage/proxy/storage.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <trusty/interface/storage.h>
+
+int storage_file_delete(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_open(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_close(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_write(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_read(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_get_size(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_file_set_size(struct storage_msg *msg,
+ const void *req, size_t req_len);
+
+int storage_init(const char *dirname);
+
+int storage_sync_checkpoint(void);
+
diff --git a/trusty/storage/tests/Android.mk b/trusty/storage/tests/Android.mk
new file mode 100644
index 0000000..71c904d
--- /dev/null
+++ b/trusty/storage/tests/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := secure-storage-unit-test
+LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_STATIC_LIBRARIES := \
+ libtrustystorageinterface \
+ libtrustystorage \
+ libtrusty \
+ liblog
+LOCAL_SRC_FILES := main.cpp
+include $(BUILD_NATIVE_TEST)
+
diff --git a/trusty/storage/tests/main.cpp b/trusty/storage/tests/main.cpp
new file mode 100644
index 0000000..1fd6f8d
--- /dev/null
+++ b/trusty/storage/tests/main.cpp
@@ -0,0 +1,3040 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <gtest/gtest.h>
+
+#include <trusty/lib/storage.h>
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+static inline bool is_32bit_aligned(size_t sz)
+{
+ return ((sz & 0x3) == 0);
+}
+
+static inline bool is_valid_size(size_t sz) {
+ return (sz > 0) && is_32bit_aligned(sz);
+}
+
+static bool is_valid_offset(storage_off_t off)
+{
+ return (off & 0x3) == 0ULL;
+}
+
+static void fill_pattern32(uint32_t *buf, size_t len, storage_off_t off)
+{
+ size_t cnt = len / sizeof(uint32_t);
+ uint32_t pattern = (uint32_t)(off / sizeof(uint32_t));
+ while (cnt--) {
+ *buf++ = pattern++;
+ }
+}
+
+static bool check_pattern32(const uint32_t *buf, size_t len, storage_off_t off)
+{
+ size_t cnt = len / sizeof(uint32_t);
+ uint32_t pattern = (uint32_t)(off / sizeof(uint32_t));
+ while (cnt--) {
+ if (*buf != pattern)
+ return false;
+ buf++;
+ pattern++;
+ }
+ return true;
+}
+
+static bool check_value32(const uint32_t *buf, size_t len, uint32_t val)
+{
+ size_t cnt = len / sizeof(uint32_t);
+ while (cnt--) {
+ if (*buf != val)
+ return false;
+ buf++;
+ }
+ return true;
+}
+
+using testing::TestWithParam;
+
+class StorageServiceTest : public virtual TestWithParam<const char *> {
+public:
+ StorageServiceTest() {}
+ virtual ~StorageServiceTest() {}
+
+ virtual void SetUp() {
+ port_ = GetParam();
+ test_buf_ = NULL;
+ aux_session_ = STORAGE_INVALID_SESSION;
+ int rc = storage_open_session(TRUSTY_DEVICE_NAME, &session_, port_);
+ ASSERT_EQ(0, rc);
+ }
+
+ virtual void TearDown() {
+ if (test_buf_) {
+ delete[] test_buf_;
+ test_buf_ = NULL;
+ }
+ storage_close_session(session_);
+
+ if (aux_session_ != STORAGE_INVALID_SESSION) {
+ storage_close_session(aux_session_);
+ aux_session_ = STORAGE_INVALID_SESSION;
+ }
+ }
+
+ void WriteReadAtOffsetHelper(file_handle_t handle, size_t blk, size_t cnt, bool complete);
+
+ void WriteZeroChunk(file_handle_t handle, storage_off_t off, size_t chunk_len, bool complete );
+ void WritePatternChunk(file_handle_t handle, storage_off_t off, size_t chunk_len, bool complete);
+ void WritePattern(file_handle_t handle, storage_off_t off, size_t data_len, size_t chunk_len, bool complete);
+
+ void ReadChunk(file_handle_t handle, storage_off_t off, size_t chunk_len,
+ size_t head_len, size_t pattern_len, size_t tail_len);
+ void ReadPattern(file_handle_t handle, storage_off_t off, size_t data_len, size_t chunk_len);
+ void ReadPatternEOF(file_handle_t handle, storage_off_t off, size_t chunk_len, size_t exp_len);
+
+protected:
+ const char *port_;
+ uint32_t *test_buf_;
+ storage_session_t session_;
+ storage_session_t aux_session_;
+};
+
+INSTANTIATE_TEST_CASE_P(SS_TD_Tests, StorageServiceTest, ::testing::Values(STORAGE_CLIENT_TD_PORT));
+INSTANTIATE_TEST_CASE_P(SS_TDEA_Tests, StorageServiceTest, ::testing::Values(STORAGE_CLIENT_TDEA_PORT));
+INSTANTIATE_TEST_CASE_P(SS_TP_Tests, StorageServiceTest, ::testing::Values(STORAGE_CLIENT_TP_PORT));
+
+
+void StorageServiceTest::WriteZeroChunk(file_handle_t handle, storage_off_t off,
+ size_t chunk_len, bool complete)
+{
+ int rc;
+ uint32_t data_buf[chunk_len/sizeof(uint32_t)];
+
+ ASSERT_PRED1(is_valid_size, chunk_len);
+ ASSERT_PRED1(is_valid_offset, off);
+
+ memset(data_buf, 0, chunk_len);
+
+ rc = storage_write(handle, off, data_buf, sizeof(data_buf),
+ complete ? STORAGE_OP_COMPLETE : 0);
+ ASSERT_EQ((int)chunk_len, rc);
+}
+
+void StorageServiceTest::WritePatternChunk(file_handle_t handle, storage_off_t off,
+ size_t chunk_len, bool complete)
+{
+ int rc;
+ uint32_t data_buf[chunk_len/sizeof(uint32_t)];
+
+ ASSERT_PRED1(is_valid_size, chunk_len);
+ ASSERT_PRED1(is_valid_offset, off);
+
+ fill_pattern32(data_buf, chunk_len, off);
+
+ rc = storage_write(handle, off, data_buf, sizeof(data_buf),
+ complete ? STORAGE_OP_COMPLETE : 0);
+ ASSERT_EQ((int)chunk_len, rc);
+}
+
+void StorageServiceTest::WritePattern(file_handle_t handle, storage_off_t off,
+ size_t data_len, size_t chunk_len, bool complete)
+{
+ ASSERT_PRED1(is_valid_size, data_len);
+ ASSERT_PRED1(is_valid_size, chunk_len);
+
+ while (data_len) {
+ if (data_len < chunk_len)
+ chunk_len = data_len;
+ WritePatternChunk(handle, off, chunk_len, (chunk_len == data_len) && complete);
+ ASSERT_FALSE(HasFatalFailure());
+ off += chunk_len;
+ data_len -= chunk_len;
+ }
+}
+
+void StorageServiceTest::ReadChunk(file_handle_t handle,
+ storage_off_t off, size_t chunk_len,
+ size_t head_len, size_t pattern_len,
+ size_t tail_len)
+{
+ int rc;
+ uint32_t data_buf[chunk_len/sizeof(uint32_t)];
+ uint8_t *data_ptr = (uint8_t *)data_buf;
+
+ ASSERT_PRED1(is_valid_size, chunk_len);
+ ASSERT_PRED1(is_valid_offset, off);
+ ASSERT_EQ(head_len + pattern_len + tail_len, chunk_len);
+
+ rc = storage_read(handle, off, data_buf, chunk_len);
+ ASSERT_EQ((int)chunk_len, rc);
+
+ if (head_len) {
+ ASSERT_TRUE(check_value32((const uint32_t *)data_ptr, head_len, 0));
+ data_ptr += head_len;
+ off += head_len;
+ }
+
+ if (pattern_len) {
+ ASSERT_TRUE(check_pattern32((const uint32_t *)data_ptr, pattern_len, off));
+ data_ptr += pattern_len;
+ }
+
+ if (tail_len) {
+ ASSERT_TRUE(check_value32((const uint32_t *)data_ptr, tail_len, 0));
+ }
+}
+
+void StorageServiceTest::ReadPattern(file_handle_t handle, storage_off_t off,
+ size_t data_len, size_t chunk_len)
+{
+ int rc;
+ uint32_t data_buf[chunk_len/sizeof(uint32_t)];
+
+ ASSERT_PRED1(is_valid_size, chunk_len);
+ ASSERT_PRED1(is_valid_size, data_len);
+ ASSERT_PRED1(is_valid_offset, off);
+
+ while (data_len) {
+ if (chunk_len > data_len)
+ chunk_len = data_len;
+ rc = storage_read(handle, off, data_buf, sizeof(data_buf));
+ ASSERT_EQ((int)chunk_len, rc);
+ ASSERT_TRUE(check_pattern32(data_buf, chunk_len, off));
+ off += chunk_len;
+ data_len -= chunk_len;
+ }
+}
+
+void StorageServiceTest::ReadPatternEOF(file_handle_t handle, storage_off_t off,
+ size_t chunk_len, size_t exp_len)
+{
+ int rc;
+ size_t bytes_read = 0;
+ uint32_t data_buf[chunk_len/sizeof(uint32_t)];
+
+ ASSERT_PRED1(is_valid_size, chunk_len);
+ ASSERT_PRED1(is_32bit_aligned, exp_len);
+
+ while (true) {
+ rc = storage_read(handle, off, data_buf, sizeof(data_buf));
+ ASSERT_GE(rc, 0);
+ if (rc == 0)
+ break; // end of file reached
+ ASSERT_PRED1(is_valid_size, (size_t)rc);
+ ASSERT_TRUE(check_pattern32(data_buf, rc, off));
+ off += rc;
+ bytes_read += rc;
+ }
+ ASSERT_EQ(bytes_read, exp_len);
+}
+
+TEST_P(StorageServiceTest, CreateDelete) {
+ int rc;
+ file_handle_t handle;
+ const char *fname = "test_create_delete_file";
+
+ // make sure test file does not exist (expect success or -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ rc = (rc == -ENOENT) ? 0 : rc;
+ ASSERT_EQ(0, rc);
+
+ // one more time (expect -ENOENT only)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // create file (expect 0)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // try to create it again while it is still opened (expect -EEXIST)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EEXIST, rc);
+
+ // close it
+ storage_close_file(handle);
+
+ // try to create it again while it is closed (expect -EEXIST)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EEXIST, rc);
+
+ // delete file (expect 0)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // one more time (expect -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, DeleteOpened) {
+ int rc;
+ file_handle_t handle;
+ const char *fname = "delete_opened_test_file";
+
+ // make sure test file does not exist (expect success or -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ rc = (rc == -ENOENT) ? 0 : rc;
+ ASSERT_EQ(0, rc);
+
+ // one more time (expect -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // open/create file (expect 0)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // delete opened file (expect 0)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // one more time (expect -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // close file
+ storage_close_file(handle);
+
+ // one more time (expect -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, OpenNoCreate) {
+ int rc;
+ file_handle_t handle;
+ const char *fname = "test_open_no_create_file";
+
+ // make sure test file does not exist (expect success or -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ rc = (rc == -ENOENT) ? 0 : rc;
+ ASSERT_EQ(0, rc);
+
+ // open non-existing file (expect -ENOENT)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // create file (expect 0)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+ storage_close_file(handle);
+
+ // open existing file (expect 0)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // close it
+ storage_close_file(handle);
+
+ // delete file (expect 0)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+}
+
+
+TEST_P(StorageServiceTest, OpenOrCreate) {
+ int rc;
+ file_handle_t handle;
+ const char *fname = "test_open_create_file";
+
+ // make sure test file does not exist (expect success or -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ rc = (rc == -ENOENT) ? 0 : rc;
+ ASSERT_EQ(0, rc);
+
+ // open/create a non-existing file (expect 0)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+ storage_close_file(handle);
+
+ // open/create an existing file (expect 0)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+ storage_close_file(handle);
+
+ // delete file (expect 0)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+}
+
+
+TEST_P(StorageServiceTest, OpenCreateDeleteCharset) {
+ int rc;
+ file_handle_t handle;
+ const char *fname = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz_01234.56789";
+
+ // open/create file (expect 0)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+ storage_close_file(handle);
+
+ // open/create an existing file (expect 0)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+ storage_close_file(handle);
+
+ // delete file (expect 0)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // open again
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, WriteReadSequential) {
+ int rc;
+ size_t blk = 2048;
+ file_handle_t handle;
+ const char *fname = "test_write_read_sequential";
+
+ // make sure test file does not exist (expect success or -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ rc = (rc == -ENOENT) ? 0 : rc;
+ ASSERT_EQ(0, rc);
+
+ // create file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write a bunch of blocks (sequentially)
+ WritePattern(handle, 0, 32 * blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadPattern(handle, 0, 32 * blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close file
+ storage_close_file(handle);
+
+ // open the same file again
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // read data back (sequentially) and check pattern again
+ ReadPattern(handle, 0, 32 * blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, OpenTruncate) {
+ int rc;
+ uint32_t val;
+ size_t blk = 2048;
+ file_handle_t handle;
+ const char *fname = "test_open_truncate";
+
+ // make sure test file does not exist (expect success or -ENOENT)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ rc = (rc == -ENOENT) ? 0 : rc;
+ ASSERT_EQ(0, rc);
+
+ // create file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write some data and read it back
+ WritePatternChunk(handle, 0, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadPattern(handle, 0, blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close file
+ storage_close_file(handle);
+
+ // reopen with truncate
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ /* try to read data back (expect no data) */
+ rc = storage_read(handle, 0LL, &val, sizeof(val));
+ ASSERT_EQ(0, rc);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, OpenSame) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ file_handle_t handle3;
+ const char *fname = "test_open_same_file";
+
+ // open/create file (expect 0)
+ rc = storage_open_file(session_, &handle1, fname, STORAGE_FILE_OPEN_CREATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+ storage_close_file(handle1);
+
+ // open an existing file first time (expect 0)
+ rc = storage_open_file(session_, &handle1, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // open the same file second time (expect error)
+ rc = storage_open_file(session_, &handle2, fname, 0, 0);
+ ASSERT_NE(0, rc);
+
+ storage_close_file(handle1);
+
+ // delete file (expect 0)
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // open deleted file (expect -ENOENT)
+ rc = storage_open_file(session_, &handle3, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, OpenMany) {
+ int rc;
+ file_handle_t handles[10];
+ char filename[10];
+ const char *fname_fmt = "mf%d";
+
+ // open or create a bunch of files (expect 0)
+ for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {
+ snprintf(filename, sizeof(filename), fname_fmt, i);
+ rc = storage_open_file(session_, &handles[i], filename,
+ STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+ }
+
+ // check that all handles are different
+ for (uint i = 0; i < ARRAY_SIZE(handles)-1; i++) {
+ for (uint j = i+1; j < ARRAY_SIZE(handles); j++) {
+ ASSERT_NE(handles[i], handles[j]);
+ }
+ }
+
+ // close them all
+ for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {
+ storage_close_file(handles[i]);
+ }
+
+ // open all files without CREATE flags (expect 0)
+ for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {
+ snprintf(filename, sizeof(filename), fname_fmt, i);
+ rc = storage_open_file(session_, &handles[i], filename, 0, 0);
+ ASSERT_EQ(0, rc);
+ }
+
+ // check that all handles are different
+ for (uint i = 0; i < ARRAY_SIZE(handles)-1; i++) {
+ for (uint j = i+1; j < ARRAY_SIZE(handles); j++) {
+ ASSERT_NE(handles[i], handles[j]);
+ }
+ }
+
+ // close and remove all test files
+ for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {
+ storage_close_file(handles[i]);
+ snprintf(filename, sizeof(filename), fname_fmt, i);
+ rc = storage_delete_file(session_, filename, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+ }
+}
+
+
+TEST_P(StorageServiceTest, ReadAtEOF) {
+ int rc;
+ uint32_t val;
+ size_t blk = 2048;
+ file_handle_t handle;
+ const char *fname = "test_read_eof";
+
+ // open/create/truncate file
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write block at offset 0
+ WritePatternChunk(handle, 0, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close file
+ storage_close_file(handle);
+
+ // open same file again
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // read the whole block back and check pattern again
+ ReadPattern(handle, 0, blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read at end of file (expected 0 bytes)
+ rc = storage_read(handle, blk, &val, sizeof(val));
+ ASSERT_EQ(0, rc);
+
+ // partial read at end of the file (expected partial data)
+ ReadPatternEOF(handle, blk/2, blk, blk/2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read past end of file
+ rc = storage_read(handle, blk + 2, &val, sizeof(val));
+ ASSERT_EQ(-EINVAL, rc);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, GetFileSize) {
+ int rc;
+ size_t blk = 2048;
+ storage_off_t size;
+ file_handle_t handle;
+ const char *fname = "test_get_file_size";
+
+ // open/create/truncate file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // check file size (expect success and size == 0)
+ size = 1;
+ rc = storage_get_file_size(handle, &size);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, size);
+
+ // write block
+ WritePatternChunk(handle, 0, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check size
+ rc = storage_get_file_size(handle, &size);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ(blk, size);
+
+ // write another block
+ WritePatternChunk(handle, blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check size again
+ rc = storage_get_file_size(handle, &size);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ(blk*2, size);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, SetFileSize) {
+ int rc;
+ size_t blk = 2048;
+ storage_off_t size;
+ file_handle_t handle;
+ const char *fname = "test_set_file_size";
+
+ // open/create/truncate file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // check file size (expect success and size == 0)
+ size = 1;
+ rc = storage_get_file_size(handle, &size);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, size);
+
+ // write block
+ WritePatternChunk(handle, 0, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check size
+ rc = storage_get_file_size(handle, &size);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ(blk, size);
+
+ storage_close_file(handle);
+
+ // reopen normally
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // check size again
+ rc = storage_get_file_size(handle, &size);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ(blk, size);
+
+ // set file size to half
+ rc = storage_set_file_size(handle, blk/2, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // check size again (should be half of original size)
+ rc = storage_get_file_size(handle, &size);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ(blk/2, size);
+
+ // read data back
+ ReadPatternEOF(handle, 0, blk, blk/2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // set file size to 0
+ rc = storage_set_file_size(handle, 0, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // check size again (should be 0)
+ rc = storage_get_file_size(handle, &size);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0LL, size);
+
+ // try to read again
+ ReadPatternEOF(handle, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+void StorageServiceTest::WriteReadAtOffsetHelper(file_handle_t handle, size_t blk, size_t cnt, bool complete)
+{
+ storage_off_t off1 = blk;
+ storage_off_t off2 = blk * (cnt-1);
+
+ // write known pattern data at non-zero offset1
+ WritePatternChunk(handle, off1, blk, complete);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // write known pattern data at non-zero offset2
+ WritePatternChunk(handle, off2, blk, complete);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read data back at offset1
+ ReadPattern(handle, off1, blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read data back at offset2
+ ReadPattern(handle, off2, blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read partially written data at end of file(expect to get data only, no padding)
+ ReadPatternEOF(handle, off2 + blk/2, blk, blk/2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read data at offset 0 (expect success and zero data)
+ ReadChunk(handle, 0, blk, blk, 0, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read data from gap (expect success and zero data)
+ ReadChunk(handle, off1 + blk, blk, blk, 0, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read partially written data (start pointing within written data)
+ // (expect to get written data back and zeroes at the end)
+ ReadChunk(handle, off1 + blk/2, blk, 0, blk/2, blk/2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read partially written data (start pointing withing unwritten data)
+ // expect to get zeroes at the beginning and proper data at the end
+ ReadChunk(handle, off1 - blk/2, blk, blk/2, blk/2, 0);
+ ASSERT_FALSE(HasFatalFailure());
+}
+
+
+TEST_P(StorageServiceTest, WriteReadAtOffset) {
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ size_t blk_cnt = 32;
+ const char *fname = "test_write_at_offset";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write a bunch of blocks filled with zeroes
+ for (uint i = 0; i < blk_cnt; i++) {
+ WriteZeroChunk(handle, i * blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+ }
+
+ WriteReadAtOffsetHelper(handle, blk, blk_cnt, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, WriteSparse) {
+ int rc;
+ file_handle_t handle;
+ const char *fname = "test_write_sparse";
+
+ // open/create/truncate file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write value past en of file
+ uint32_t val = 0xDEADBEEF;
+ rc = storage_write(handle, 1, &val, sizeof(val), STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+// Persistent 32k
+
+TEST_P(StorageServiceTest, CreatePersistent32K) {
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ size_t file_size = 32768;
+ const char *fname = "test_persistent_32K_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write a bunch of blocks filled with pattern
+ WritePattern(handle, 0, file_size, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, ReadPersistent32k) {
+ int rc;
+ file_handle_t handle;
+ size_t exp_len = 32 * 1024;
+ const char *fname = "test_persistent_32K_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ ReadPatternEOF(handle, 0, 2048, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadPatternEOF(handle, 0, 1024, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadPatternEOF(handle, 0, 332, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, CleanUpPersistent32K) {
+ int rc;
+ const char *fname = "test_persistent_32K_file";
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ rc = (rc == -ENOENT) ? 0 : rc;
+ ASSERT_EQ(0, rc);
+}
+
+// Persistent 1M
+TEST_P(StorageServiceTest, CreatePersistent1M_4040) {
+ int rc;
+ file_handle_t handle;
+ size_t file_size = 1024 * 1024;
+ const char *fname = "test_persistent_1M_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write a bunch of blocks filled with pattern
+ WritePattern(handle, 0, file_size, 4040, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, CreatePersistent1M_2032) {
+ int rc;
+ file_handle_t handle;
+ size_t file_size = 1024 * 1024;
+ const char *fname = "test_persistent_1M_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write a bunch of blocks filled with pattern
+ WritePattern(handle, 0, file_size, 2032, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+
+TEST_P(StorageServiceTest, CreatePersistent1M_496) {
+ int rc;
+ file_handle_t handle;
+ size_t file_size = 1024 * 1024;
+ const char *fname = "test_persistent_1M_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write a bunch of blocks filled with pattern
+ WritePattern(handle, 0, file_size, 496, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, CreatePersistent1M_240) {
+ int rc;
+ file_handle_t handle;
+ size_t file_size = 1024 * 1024;
+ const char *fname = "test_persistent_1M_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write a bunch of blocks filled with pattern
+ WritePattern(handle, 0, file_size, 240, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, ReadPersistent1M_4040) {
+ int rc;
+ file_handle_t handle;
+ size_t exp_len = 1024 * 1024;
+ const char *fname = "test_persistent_1M_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ ReadPatternEOF(handle, 0, 4040, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, ReadPersistent1M_2032) {
+ int rc;
+ file_handle_t handle;
+ size_t exp_len = 1024 * 1024;
+ const char *fname = "test_persistent_1M_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ ReadPatternEOF(handle, 0, 2032, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, ReadPersistent1M_496) {
+ int rc;
+ file_handle_t handle;
+ size_t exp_len = 1024 * 1024;
+ const char *fname = "test_persistent_1M_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ ReadPatternEOF(handle, 0, 496, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, ReadPersistent1M_240) {
+ int rc;
+ file_handle_t handle;
+ size_t exp_len = 1024 * 1024;
+ const char *fname = "test_persistent_1M_file";
+
+ // create/truncate file.
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ ReadPatternEOF(handle, 0, 240, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close but do not delete file
+ storage_close_file(handle);
+}
+
+TEST_P(StorageServiceTest, CleanUpPersistent1M) {
+ int rc;
+ const char *fname = "test_persistent_1M_file";
+ rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+ rc = (rc == -ENOENT) ? 0 : rc;
+ ASSERT_EQ(0, rc);
+}
+
+TEST_P(StorageServiceTest, WriteReadLong) {
+ int rc;
+ file_handle_t handle;
+ size_t wc = 10000;
+ const char *fname = "test_write_read_long";
+
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ test_buf_ = new uint32_t[wc];
+ fill_pattern32(test_buf_, wc * sizeof(uint32_t), 0);
+ rc = storage_write(handle, 0, test_buf_, wc * sizeof(uint32_t), STORAGE_OP_COMPLETE);
+ ASSERT_EQ((int)(wc * sizeof(uint32_t)), rc);
+
+ rc = storage_read(handle, 0, test_buf_, wc * sizeof(uint32_t));
+ ASSERT_EQ((int)(wc * sizeof(uint32_t)), rc);
+ ASSERT_TRUE(check_pattern32(test_buf_, wc * sizeof(uint32_t), 0));
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+// Negative tests
+
+TEST_P(StorageServiceTest, OpenInvalidFileName) {
+ int rc;
+ file_handle_t handle;
+ const char *fname1 = "";
+ const char *fname2 = "ffff$ffff";
+ const char *fname3 = "ffff\\ffff";
+ char max_name[STORAGE_MAX_NAME_LENGTH_BYTES+1];
+
+ rc = storage_open_file(session_, &handle, fname1,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EINVAL, rc);
+
+ rc = storage_open_file(session_, &handle, fname2,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EINVAL, rc);
+
+ rc = storage_open_file(session_, &handle, fname3,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EINVAL, rc);
+
+ /* max name */
+ memset(max_name, 'a', sizeof(max_name));
+ max_name[sizeof(max_name)-1] = 0;
+
+ rc = storage_open_file(session_, &handle, max_name,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EINVAL, rc);
+
+ max_name[sizeof(max_name)-2] = 0;
+ rc = storage_open_file(session_, &handle, max_name,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ storage_close_file(handle);
+ storage_delete_file(session_, max_name, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, BadFileHnadle) {
+ int rc;
+ file_handle_t handle;
+ file_handle_t handle1;
+ const char *fname = "test_invalid_file_handle";
+
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ handle1 = handle + 1;
+
+ // write to invalid file handle
+ uint32_t val = 0xDEDBEEF;
+ rc = storage_write(handle1, 0, &val, sizeof(val), STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // read from invalid handle
+ rc = storage_read(handle1, 0, &val, sizeof(val));
+ ASSERT_EQ(-EINVAL, rc);
+
+ // set size
+ rc = storage_set_file_size(handle1, 0, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // get size
+ storage_off_t fsize = (storage_off_t)(-1);
+ rc = storage_get_file_size(handle1, &fsize);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // close (there is no way to check errors here)
+ storage_close_file(handle1);
+
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, ClosedFileHnadle) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ const char *fname1 = "test_invalid_file_handle1";
+ const char *fname2 = "test_invalid_file_handle2";
+
+ rc = storage_open_file(session_, &handle1, fname1,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_open_file(session_, &handle2, fname2,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // close first file handle
+ storage_close_file(handle1);
+
+ // write to invalid file handle
+ uint32_t val = 0xDEDBEEF;
+ rc = storage_write(handle1, 0, &val, sizeof(val), STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // read from invalid handle
+ rc = storage_read(handle1, 0, &val, sizeof(val));
+ ASSERT_EQ(-EINVAL, rc);
+
+ // set size
+ rc = storage_set_file_size(handle1, 0, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // get size
+ storage_off_t fsize = (storage_off_t)(-1);
+ rc = storage_get_file_size(handle1, &fsize);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // close (there is no way to check errors here)
+ storage_close_file(handle1);
+
+ // clean up
+ storage_close_file(handle2);
+ storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+ storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+// Transactions
+
+TEST_P(StorageServiceTest, TransactDiscardInactive) {
+ int rc;
+
+ // discard current transaction (there should not be any)
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // try it again
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_P(StorageServiceTest, TransactCommitInactive) {
+ int rc;
+
+ // try to commit current transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // try it again
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+}
+
+TEST_P(StorageServiceTest, TransactDiscardWrite) {
+
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_discard_write";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // write (without commit)
+ WritePattern(handle, 0, exp_len, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // abort current transaction
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // cleanup
+ storage_close_file( handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, TransactDiscardWriteAppend) {
+
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_write_append";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write data with commit
+ WritePattern(handle, 0, exp_len/2, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // write data without commit
+ WritePattern(handle, exp_len/2, exp_len/2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size (should be exp_len)
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // discard transaction
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // check file size, it should be exp_len/2
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/2, fsize);
+
+ // check file data
+ ReadPatternEOF(handle, 0, blk, exp_len/2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactDiscardWriteRead) {
+
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_discard_write_read";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // Fill with zeroes (with commit)
+ for (uint i = 0; i < 32; i++) {
+ WriteZeroChunk(handle, i * blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+ }
+
+ // check that test chunk is filled with zeroes
+ ReadChunk(handle, blk, blk, blk, 0, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // write test pattern (without commit)
+ WritePattern(handle, blk, blk, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read it back an check pattern
+ ReadChunk(handle, blk, blk, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // abort current transaction
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // read same chunk back (should be filled with zeros)
+ ReadChunk(handle, blk, blk, blk, 0, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactDiscardWriteMany) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ size_t blk = 2048;
+ size_t exp_len1 = 32 * 1024;
+ size_t exp_len2 = 31 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname1 = "test_transact_discard_write_file1";
+ const char *fname2 = "test_transact_discard_write_file2";
+
+ // open create truncate (with commit)
+ rc = storage_open_file(session_, &handle1, fname1,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // open create truncate (with commit)
+ rc = storage_open_file(session_, &handle2, fname2,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // file1: fill file with pattern (without commit)
+ WritePattern(handle1, 0, exp_len1, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // file2: fill file with pattern (without commit)
+ WritePattern(handle2, 0, exp_len2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size, it should be exp_len1
+ rc = storage_get_file_size(handle1, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len1, fsize);
+
+ // check file size, it should be exp_len2
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len2, fsize);
+
+ // commit transaction
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // check file size, it should be exp_len1
+ rc = storage_get_file_size(handle1, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // check file size, it should be exp_len2
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // check data
+ ReadPatternEOF(handle1, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadPatternEOF(handle2, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle1);
+ storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+ storage_close_file(handle2);
+ storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactDiscardTruncate) {
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_discard_truncate";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, exp_len, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // close file
+ storage_close_file(handle);
+
+ // open truncate file (without commit)
+ rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // abort current transaction
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // check file size (should be an oruginal size)
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactDiscardSetSize) {
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_discard_set_size";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, exp_len, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // set file size to half of original (no commit)
+ rc = storage_set_file_size(handle, (storage_off_t)exp_len/2, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/2, fsize);
+
+ // set file size to 1/3 of original (no commit)
+ rc = storage_set_file_size(handle, (storage_off_t)exp_len/3, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/3, fsize);
+
+ // abort current transaction
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // check file size (should be an original size)
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactDiscardDelete) {
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_discard_delete";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, exp_len, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close it
+ storage_close_file(handle);
+
+ // delete file (without commit)
+ rc = storage_delete_file(session_, fname, 0);
+ ASSERT_EQ(0, rc);
+
+ // try to open it (should fail)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // abort current transaction
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // try to open it
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size (should be an original size)
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactDiscardDelete2) {
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_discard_delete";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, exp_len, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // delete file (without commit)
+ rc = storage_delete_file(session_, fname, 0);
+ ASSERT_EQ(0, rc);
+ storage_close_file(handle);
+
+ // try to open it (should fail)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // abort current transaction
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // try to open it
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size (should be an original size)
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, TransactDiscardCreate) {
+ int rc;
+ file_handle_t handle;
+ const char *fname = "test_transact_discard_create_excl";
+
+ // delete test file just in case
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+
+ // create file (without commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ 0);
+ ASSERT_EQ(0, rc);
+
+ // abort current transaction
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactCommitWrites) {
+
+ int rc;
+ file_handle_t handle;
+ file_handle_t handle_aux;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_commit_writes";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // open the same file in aux session
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size, it should be 0
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // write data in primary session (without commit)
+ WritePattern(handle, 0, exp_len/2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // write more data in primary session (without commit)
+ WritePattern(handle, exp_len/2, exp_len/2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size in aux session, it should still be 0
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // commit current transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // check file size of aux session, should fail
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(-EBUSY, rc);
+
+ // abort transaction in aux session to recover
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // check file size in aux session, it should be exp_len
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // check file size in primary session, it should be exp_len
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // check data in primary session
+ ReadPatternEOF(handle, 0, blk, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check data in aux session
+ ReadPatternEOF(handle_aux, 0, blk, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle);
+ storage_close_file(handle_aux);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, TransactCommitWrites2) {
+
+ int rc;
+ file_handle_t handle;
+ file_handle_t handle_aux;
+ size_t blk = 2048;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_commit_writes2";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // open the same file in separate session
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // discard transaction in aux_session
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // Fill with zeroes (with commit)
+ for (uint i = 0; i < 8; i++) {
+ WriteZeroChunk(handle, i * blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+ }
+
+ // check that test chunks are filled with zeroes
+ ReadChunk(handle, blk, blk, blk, 0, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadChunk(handle, 2 * blk, blk, blk, 0, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // write test pattern (without commit)
+ WritePattern(handle, blk, blk, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // write test pattern (without commit)
+ WritePattern(handle, 2 * blk, blk, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read it back and check pattern
+ ReadChunk(handle, blk, blk, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadChunk(handle, 2 * blk, blk, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // In aux session it still should be empty
+ ReadChunk(handle_aux, blk, blk, blk, 0, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadChunk(handle_aux, 2 * blk, blk, blk, 0, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // commit current transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // read same chunks back in primary session
+ ReadChunk(handle, blk, blk, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadChunk(handle, 2 * blk, blk, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read same chunks back in aux session (should fail)
+ uint32_t val;
+ rc = storage_read(handle_aux, blk, &val, sizeof(val));
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_read(handle_aux, 2 * blk, &val, sizeof(val));
+ ASSERT_EQ(-EBUSY, rc);
+
+ // abort transaction in aux session
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // read same chunk again in aux session
+ ReadChunk(handle_aux, blk, blk, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadChunk(handle_aux, 2 * blk, blk, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+
+ // cleanup
+ storage_close_file(handle);
+ storage_close_file(handle_aux);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactCommitSetSize) {
+ int rc;
+ file_handle_t handle;
+ file_handle_t handle_aux;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_commit_set_size";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // open the same file in separate session
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, exp_len, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // same in aux session
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // set file size to half of original (no commit)
+ rc = storage_set_file_size(handle, (storage_off_t)exp_len/2, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/2, fsize);
+
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // set file size to 1/3 of original (no commit)
+ rc = storage_set_file_size(handle, (storage_off_t)exp_len/3, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/3, fsize);
+
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // commit current transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // check file size (should be 1/3 of an original size)
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/3, fsize);
+
+ // check file size from aux session
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(-EBUSY, rc);
+
+ // abort transaction in aux_session
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // check again
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/3, fsize);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_close_file(handle_aux);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, TransactCommitDelete) {
+ int rc;
+ file_handle_t handle;
+ file_handle_t handle_aux;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ const char *fname = "test_transact_commit_delete";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, exp_len, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close it
+ storage_close_file(handle);
+
+ // open the same file in separate session
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+ storage_close_file(handle_aux);
+
+ // delete file (without commit)
+ rc = storage_delete_file(session_, fname, 0);
+ ASSERT_EQ(0, rc);
+
+ // try to open it (should fail)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // open the same file in separate session (should be fine)
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+ storage_close_file(handle_aux);
+
+ // commit current transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // try to open it in primary session (still fails)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // open the same file in aux session (should also fail)
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+}
+
+
+TEST_P(StorageServiceTest, TransactCommitTruncate) {
+ int rc;
+ file_handle_t handle;
+ file_handle_t handle_aux;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_commit_truncate";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, exp_len, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // close file
+ storage_close_file(handle);
+
+ // check from different session
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // open truncate file (without commit)
+ rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // commit current transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // check file size (should be 0)
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // check file size in aux session (should be -EBUSY)
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(-EBUSY, rc);
+
+ // abort transaction in aux session
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // check again
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_close_file(handle_aux);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactCommitCreate) {
+ int rc;
+ file_handle_t handle;
+ file_handle_t handle_aux;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_commit_create";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // delete test file just in case
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+
+ // check from aux session
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // create file (without commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ 0);
+ ASSERT_EQ(0, rc);
+
+ // check file size
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // close file
+ storage_close_file(handle);
+
+ // check from aux session (should fail)
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // commit current transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // check open from normal session
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // check open from aux session (should succeed)
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // cleanup
+ storage_close_file(handle);
+ storage_close_file(handle_aux);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactCommitCreateMany) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ file_handle_t handle1_aux;
+ file_handle_t handle2_aux;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname1 = "test_transact_commit_create1";
+ const char *fname2 = "test_transact_commit_create2";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // delete test file just in case
+ storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+ storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+
+ // create file (without commit)
+ rc = storage_open_file(session_, &handle1, fname1,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ 0);
+ ASSERT_EQ(0, rc);
+
+ // create file (without commit)
+ rc = storage_open_file(session_, &handle2, fname2,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ 0);
+ ASSERT_EQ(0, rc);
+
+ // check file sizes
+ rc = storage_get_file_size(handle1, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ rc = storage_get_file_size(handle1, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // close files
+ storage_close_file(handle1);
+ storage_close_file(handle2);
+
+ // open files from aux session
+ rc = storage_open_file(aux_session_, &handle1_aux, fname1, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ rc = storage_open_file(aux_session_, &handle2_aux, fname2, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // commit current transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // open from primary session
+ rc = storage_open_file(session_, &handle1, fname1, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_open_file(session_, &handle2, fname2, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // open from aux session
+ rc = storage_open_file(aux_session_, &handle1_aux, fname1, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_open_file(aux_session_, &handle2_aux, fname2, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // cleanup
+ storage_close_file(handle1);
+ storage_close_file(handle1_aux);
+ storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+ storage_close_file(handle2);
+ storage_close_file(handle2_aux);
+ storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, TransactCommitWriteMany) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ file_handle_t handle1_aux;
+ file_handle_t handle2_aux;
+ size_t blk = 2048;
+ size_t exp_len1 = 32 * 1024;
+ size_t exp_len2 = 31 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname1 = "test_transact_commit_write_file1";
+ const char *fname2 = "test_transact_commit_write_file2";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // open create truncate (with commit)
+ rc = storage_open_file(session_, &handle1, fname1,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // open create truncate (with commit)
+ rc = storage_open_file(session_, &handle2, fname2,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // open same files from aux session
+ rc = storage_open_file(aux_session_, &handle1_aux, fname1, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_open_file(aux_session_, &handle2_aux, fname2, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // file1: fill file with pattern (without commit)
+ WritePattern(handle1, 0, exp_len1, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // file2: fill file with pattern (without commit)
+ WritePattern(handle2, 0, exp_len2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size, it should be exp_len1
+ rc = storage_get_file_size(handle1, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len1, fsize);
+
+ // check file size, it should be exp_len2
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len2, fsize);
+
+ // check file sizes from aux session (should be 0)
+ rc = storage_get_file_size(handle1_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ rc = storage_get_file_size(handle2_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // commit transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // check file size, it should be exp_len1
+ rc = storage_get_file_size(handle1, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len1, fsize);
+
+ // check file size, it should be exp_len2
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len2, fsize);
+
+ // check from aux session (should be -EBUSY)
+ rc = storage_get_file_size(handle1_aux, &fsize);
+ ASSERT_EQ(-EBUSY, rc);
+
+ // abort transaction in aux session
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // and check again
+ rc = storage_get_file_size(handle1_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len1, fsize);
+
+ rc = storage_get_file_size(handle2_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len2, fsize);
+
+ // check data
+ ReadPatternEOF(handle1, 0, blk, exp_len1);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadPatternEOF(handle2, 0, blk, exp_len2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadPatternEOF(handle1_aux, 0, blk, exp_len1);
+ ASSERT_FALSE(HasFatalFailure());
+
+ ReadPatternEOF(handle2_aux, 0, blk, exp_len2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle1);
+ storage_close_file(handle1_aux);
+ storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+ storage_close_file(handle2);
+ storage_close_file(handle2_aux);
+ storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, TransactCommitDeleteCreate) {
+ int rc;
+ file_handle_t handle;
+ file_handle_t handle_aux;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_delete_create";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, exp_len, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close it
+ storage_close_file(handle);
+
+ // delete file (without commit)
+ rc = storage_delete_file(session_, fname, 0);
+ ASSERT_EQ(0, rc);
+
+ // try to open it (should fail)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // try to open it in aux session (should succeed)
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // create file with the same name (no commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,
+ 0);
+ ASSERT_EQ(0, rc);
+
+ // write half of data (with commit)
+ WritePattern(handle, 0, exp_len/2, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check file size (should be half)
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/2, fsize);
+
+ // commit transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // check data from primary session
+ ReadPatternEOF(handle, 0, blk, exp_len/2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // check from aux session (should fail)
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // abort trunsaction in aux session
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // and try again (should still fail)
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // close file and reopen it again
+ storage_close_file(handle_aux);
+ rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // try it again (should succeed)
+ rc = storage_get_file_size(handle_aux, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/2, fsize);
+
+ // check data
+ ReadPatternEOF(handle_aux, 0, blk, exp_len/2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle);
+ storage_close_file(handle_aux);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, TransactRewriteExistingTruncate) {
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ const char *fname = "test_transact_rewrite_existing_truncate";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // close it
+ storage_close_file(handle);
+
+ // up
+ for (uint i = 1; i < 32; i++) {
+ // open truncate (no commit)
+ rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, i * blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close
+ storage_close_file(handle);
+ }
+
+ // down
+ for (uint i = 1; i < 32; i++) {
+ // open truncate (no commit)
+ rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, (32 - i) * blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close
+ storage_close_file(handle);
+ }
+
+ // cleanup
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, TransactRewriteExistingSetSize) {
+ int rc;
+ file_handle_t handle;
+ size_t blk = 2048;
+ const char *fname = "test_transact_rewrite_existing_set_size";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // close it
+ storage_close_file(handle);
+
+ // up
+ for (uint i = 1; i < 32; i++) {
+ // open truncate (no commit)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, i * blk, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // update size (with commit)
+ rc = storage_set_file_size(handle, i * blk, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // close
+ storage_close_file(handle);
+ }
+
+ // down
+ for (uint i = 1; i < 32; i++) {
+ // open trancate (no commit)
+ rc = storage_open_file(session_, &handle, fname, 0, 0);
+ ASSERT_EQ(0, rc);
+
+ // write data (with commit)
+ WritePattern(handle, 0, (32 - i) * blk, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // update size (with commit)
+ rc = storage_set_file_size(handle, (32 - i) * blk, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // close
+ storage_close_file(handle);
+ }
+
+ // cleanup
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, TransactResumeAfterNonFatalError) {
+
+ int rc;
+ file_handle_t handle;
+ file_handle_t handle1;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_resume_writes";
+
+ // open create truncate file (with commit)
+ rc = storage_open_file(session_, &handle, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // write (without commit)
+ WritePattern(handle, 0, exp_len/2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // issue some commands that should fail with non-fatal errors
+
+ // write past end of file
+ uint32_t val = 0xDEDBEEF;
+ rc = storage_write(handle, exp_len/2 + 1, &val, sizeof(val), 0);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // read past end of file
+ rc = storage_read(handle, exp_len/2 + 1, &val, sizeof(val));
+ ASSERT_EQ(-EINVAL, rc);
+
+ // try to extend file past end of file
+ rc = storage_set_file_size(handle, exp_len/2 + 1, 0);
+ ASSERT_EQ(-EINVAL, rc);
+
+ // open non existing file
+ rc = storage_open_file(session_, &handle1, "foo",
+ STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // delete non-existing file
+ rc = storage_delete_file(session_, "foo", STORAGE_OP_COMPLETE);
+ ASSERT_EQ(-ENOENT, rc);
+
+ // then resume writinga (without commit)
+ WritePattern(handle, exp_len/2, exp_len/2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // commit current transaction
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // check file size, it should be exp_len
+ rc = storage_get_file_size(handle, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // check data
+ ReadPatternEOF(handle, 0, blk, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle);
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+// Transaction Collisions
+
+TEST_P(StorageServiceTest, Transact2_WriteNC) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ size_t blk = 2048;
+ const char *fname1 = "test_transact_f1";
+ const char *fname2 = "test_transact_f2";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_open_file(session_, &handle1, fname1,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_open_file(aux_session_, &handle2, fname2,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // session 1
+ WritePattern(handle1, 0, blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read it back
+ ReadPatternEOF(handle1, 0, blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // session 2
+ WritePattern(handle2, 0, blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read it back
+ ReadPatternEOF(handle2, 0, blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle1);
+ storage_close_file(handle2);
+
+ storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);
+ storage_delete_file(aux_session_, fname2, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_DeleteNC) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ size_t blk = 2048;
+ const char *fname1 = "test_transact_delete_f1";
+ const char *fname2 = "test_transact_delete_f2";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_open_file(session_, &handle1, fname1,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_open_file(aux_session_, &handle2, fname2,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // session 1
+ WritePattern(handle1, 0, blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read it back
+ ReadPatternEOF(handle1, 0, blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // session 2
+ WritePattern(handle2, 0, blk, blk, true);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // read it back
+ ReadPatternEOF(handle2, 0, blk, blk);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // close files and delete them
+ storage_close_file(handle1);
+ storage_delete_file(session_, fname1, 0);
+
+ storage_close_file(handle2);
+ storage_delete_file(aux_session_, fname2, 0);
+
+ // commit
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ rc = storage_end_transaction(aux_session_, true);
+ ASSERT_EQ(0, rc);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_Write_Read) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_writeRead";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // S1: open create truncate file
+ rc = storage_open_file(session_, &handle1, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S2: open the same file
+ rc = storage_open_file(aux_session_, &handle2, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S1: write (no commit)
+ WritePattern(handle1, 0, exp_len, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S1: read it back
+ ReadPatternEOF(handle1, 0, blk, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S2: check file size, it should be 0
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // S2: read it back (should no data)
+ ReadPatternEOF(handle2, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S1: commit
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // S2: check file size, it should fail
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(-EBUSY, rc);
+
+ // S2: abort transaction
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // S2: check file size again, it should be exp_len
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // S2: read it again (should be exp_len)
+ ReadPatternEOF(handle2, 0, blk, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle1);
+ storage_close_file(handle2);
+
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_Write_Write_Commit_Commit) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ file_handle_t handle3;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_write_write_commit_commit";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // S1: open create truncate file
+ rc = storage_open_file(session_, &handle1, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S2: open the same file
+ rc = storage_open_file(aux_session_, &handle2, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S1: write (no commit)
+ WritePattern(handle1, 0, exp_len, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S2: write (no commit)
+ WritePattern(handle2, 0, exp_len/2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S1: commit
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // S2: read/write/get/set size/delete (all should fail)
+ uint32_t val = 0;
+ rc = storage_read(handle2, 0, &val, sizeof(val));
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_write(handle2, 0, &val, sizeof(val), 0);
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_set_file_size(handle2, fsize, 0);
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_delete_file(aux_session_, fname, 0);
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_open_file(aux_session_, &handle3, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0);
+ ASSERT_EQ(-EBUSY, rc);
+
+ // S2: commit (should fail, and failed state should be cleared)
+ rc = storage_end_transaction(aux_session_, true);
+ ASSERT_EQ(-EBUSY, rc);
+
+ // S2: check file size, it should be exp_len
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // S2: read it again (should be exp_len)
+ ReadPatternEOF(handle2, 0, blk, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle1);
+ storage_close_file(handle2);
+
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_Write_Write_Commit_Discard) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ file_handle_t handle3;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_write_write_commit_discard";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // S1: open create truncate file
+ rc = storage_open_file(session_, &handle1, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S2: open the same file
+ rc = storage_open_file(aux_session_, &handle2, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S1: write (no commit)
+ WritePattern(handle1, 0, exp_len, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S2: write (no commit)
+ WritePattern(handle2, 0, exp_len/2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S1: commit
+ rc = storage_end_transaction(session_, true);
+ ASSERT_EQ(0, rc);
+
+ // S2: read/write/get/set size/delete (all should fail)
+ uint32_t val = 0;
+ rc = storage_read(handle2, 0, &val, sizeof(val));
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_write(handle2, 0, &val, sizeof(val), 0);
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_set_file_size(handle2, fsize, 0);
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_delete_file(aux_session_, fname, 0);
+ ASSERT_EQ(-EBUSY, rc);
+
+ rc = storage_open_file(aux_session_, &handle3, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0);
+ ASSERT_EQ(-EBUSY, rc);
+
+ // S2: discard (should fail, and failed state should be cleared)
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // S2: check file size, it should be exp_len
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len, fsize);
+
+ // S2: read it again (should be exp_len)
+ ReadPatternEOF(handle2, 0, blk, exp_len);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle1);
+ storage_close_file(handle2);
+
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+TEST_P(StorageServiceTest, Transact2_Write_Write_Discard_Commit) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_write_write_discard_commit";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // S1: open create truncate file
+ rc = storage_open_file(session_, &handle1, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S2: open the same file
+ rc = storage_open_file(aux_session_, &handle2, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S1: write (no commit)
+ WritePattern(handle1, 0, exp_len, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S2: write (no commit)
+ WritePattern(handle2, 0, exp_len/2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S1: discard
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // S2: commit (should succeed)
+ rc = storage_end_transaction(aux_session_, true);
+ ASSERT_EQ(0, rc);
+
+ // S2: check file size, it should be exp_len
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)exp_len/2, fsize);
+
+ // S2: read it again (should be exp_len)
+ ReadPatternEOF(handle2, 0, blk, exp_len/2);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle1);
+ storage_close_file(handle2);
+
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
+
+TEST_P(StorageServiceTest, Transact2_Write_Write_Discard_Discard) {
+ int rc;
+ file_handle_t handle1;
+ file_handle_t handle2;
+ size_t blk = 2048;
+ size_t exp_len = 32 * 1024;
+ storage_off_t fsize = (storage_off_t)(-1);
+ const char *fname = "test_transact_write_write_discard_Discard";
+
+ // open second session
+ rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);
+ ASSERT_EQ(0, rc);
+
+ // S1: open create truncate file
+ rc = storage_open_file(session_, &handle1, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S2: open the same file
+ rc = storage_open_file(aux_session_, &handle2, fname,
+ STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,
+ STORAGE_OP_COMPLETE);
+ ASSERT_EQ(0, rc);
+
+ // S1: write (no commit)
+ WritePattern(handle1, 0, exp_len, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S2: write (no commit)
+ WritePattern(handle2, 0, exp_len/2, blk, false);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // S1: discard
+ rc = storage_end_transaction(session_, false);
+ ASSERT_EQ(0, rc);
+
+ // S2: discard
+ rc = storage_end_transaction(aux_session_, false);
+ ASSERT_EQ(0, rc);
+
+ // S2: check file size, it should be 0
+ rc = storage_get_file_size(handle2, &fsize);
+ ASSERT_EQ(0, rc);
+ ASSERT_EQ((storage_off_t)0, fsize);
+
+ // S2: read it again (should be 0)
+ ReadPatternEOF(handle2, 0, blk, 0);
+ ASSERT_FALSE(HasFatalFailure());
+
+ // cleanup
+ storage_close_file(handle1);
+ storage_close_file(handle2);
+
+ storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);
+}
+
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
new file mode 100644
index 0000000..9c3a7df
--- /dev/null
+++ b/trusty/trusty-base.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2016 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# This makefile should be included by devices that use Trusty TEE
+# to pull in the baseline set of Trusty specific modules.
+#
+
+PRODUCT_PACKAGES += \
+ keystore.trusty \
+ gatekeeper.trusty
+
+PRODUCT_PROPERTY_OVERRIDES += \
+ ro.hardware.keystore=trusty \
+ ro.hardware.gatekeeper=trusty
diff --git a/trusty/trusty-storage.mk b/trusty/trusty-storage.mk
new file mode 100644
index 0000000..3f26316
--- /dev/null
+++ b/trusty/trusty-storage.mk
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+PRODUCT_PACKAGES += \
+ storageproxyd \
diff --git a/tzdatacheck/Android.bp b/tzdatacheck/Android.bp
new file mode 100644
index 0000000..00ad141
--- /dev/null
+++ b/tzdatacheck/Android.bp
@@ -0,0 +1,14 @@
+// ========================================================
+// Executable
+// ========================================================
+cc_binary {
+ name: "tzdatacheck",
+ host_supported: true,
+ srcs: ["tzdatacheck.cpp"],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+ cflags: ["-Werror"],
+}
diff --git a/tzdatacheck/Android.mk b/tzdatacheck/Android.mk
deleted file mode 100644
index 0e25f7d..0000000
--- a/tzdatacheck/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# ========================================================
-# Executable
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= tzdatacheck.cpp
-LOCAL_MODULE := tzdatacheck
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= tzdatacheck.cpp
-LOCAL_MODULE := tzdatacheck
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
-LOCAL_CFLAGS := -Werror
-include $(BUILD_HOST_EXECUTABLE)
-
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index 31f7b55..fb5c84b 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <errno.h>
#include <ftw.h>
#include <libgen.h>
@@ -27,49 +28,107 @@
#include <string>
#include <vector>
-#include "base/logging.h"
+#include "android-base/logging.h"
+
+static const char* BUNDLE_VERSION_FILENAME = "/bundle_version";
+// bundle_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
+// AAA.BBB is the major/minor version of the bundle format (e.g. 001.001),
+// CCCCC is the rules version (e.g. 2016g)
+// DDD is the android revision for this rules version to allow for bundle corrections (e.g. 001)
+// We only need the first 13 to determine if it is suitable for the device.
+static const int BUNDLE_VERSION_LENGTH = 13;
+// The major version of the bundle format supported by this code as a null-terminated char[].
+static const char REQUIRED_BUNDLE_VERSION[] = "001";
+static const size_t REQUIRED_BUNDLE_VERSION_LEN = sizeof(REQUIRED_BUNDLE_VERSION) - 1; // exclude \0
+// The length of the IANA rules version bytes. e.g. 2016a
+static const size_t RULES_VERSION_LEN = 5;
+// Bundle version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
+static const size_t BUNDLE_VERSION_RULES_IDX = 8;
static const char* TZDATA_FILENAME = "/tzdata";
// tzdata file header (as much as we need for the version):
// byte[11] tzdata_version -- e.g. "tzdata2012f"
static const int TZ_HEADER_LENGTH = 11;
+// The major version of the bundle format supported by this code as a null-terminated char[].
+static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
+static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
+
static void usage() {
std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
"\n"
- "Compares the headers of two tzdata files. If the one in SYSTEM_TZ_DIR is the\n"
- "same or a higher version than the one in DATA_TZ_DIR the DATA_TZ_DIR is renamed\n"
- "and then deleted.\n";
+ "Checks whether any timezone update bundle in DATA_TZ_DIR is compatible with the\n"
+ "current Android release and better than or the same as base system timezone rules in\n"
+ "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
+ "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
exit(1);
}
/*
- * Opens a file and fills headerBytes with the first byteCount bytes from the file. It is a fatal
- * error if the file is too small or cannot be opened. If the file does not exist false is returned.
+ * Opens a file and fills buffer with the first byteCount bytes from the file.
+ * If the file does not exist or cannot be opened or is too short then false is returned.
* If the bytes were read successfully then true is returned.
*/
-static bool readHeader(const std::string& tzDataFileName, char* headerBytes, size_t byteCount) {
- FILE* tzDataFile = fopen(tzDataFileName.c_str(), "r");
- if (tzDataFile == nullptr) {
- if (errno == ENOENT) {
- return false;
- } else {
- PLOG(FATAL) << "Error opening tzdata file " << tzDataFileName;
+static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
+ FILE* file = fopen(fileName.c_str(), "r");
+ if (file == nullptr) {
+ if (errno != ENOENT) {
+ PLOG(WARNING) << "Error opening file " << fileName;
}
+ return false;
}
- size_t bytesRead = fread(headerBytes, 1, byteCount, tzDataFile);
+ size_t bytesRead = fread(buffer, 1, byteCount, file);
+ fclose(file);
if (bytesRead != byteCount) {
- LOG(FATAL) << tzDataFileName << " is too small. " << byteCount << " bytes required";
+ LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
+ return false;
}
- fclose(tzDataFile);
return true;
}
-/* Checks the contents of headerBytes. It is a fatal error if it not a tzdata header. */
-static void checkValidHeader(const std::string& fileName, char* headerBytes) {
+/*
+ * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
+ * otherwise.
+ */
+static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
if (strncmp("tzdata", headerBytes, 6) != 0) {
- LOG(FATAL) << fileName << " does not start with the expected bytes (tzdata)";
+ LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
+ return false;
}
+ return true;
+}
+
+static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
+ for (size_t j = 0; j < count; j++) {
+ char toCheck = buffer[(*i)++];
+ if (!isdigit(toCheck)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool checkValidBundleVersion(const char* buffer) {
+ // See BUNDLE_VERSION_LENGTH comments above for a description of the format.
+ size_t i = 0;
+ if (!checkDigits(buffer, 3, &i)) {
+ return false;
+ }
+ if (buffer[i++] != '.') {
+ return false;
+ }
+ if (!checkDigits(buffer, 3, &i)) {
+ return false;
+ }
+ if (buffer[i++] != '|') {
+ return false;
+ }
+ if (!checkDigits(buffer, 4, &i)) {
+ return false;
+ }
+ // Ignore the last character. It is assumed to be a letter but we don't check because it's not
+ // obvious what would happen at 'z'.
+ return true;
}
/* Return the parent directory of dirName. */
@@ -103,9 +162,24 @@
return 0;
}
+enum PathStatus { ERR, NONE, IS_DIR, NOT_DIR };
+
+static PathStatus checkPath(const std::string& path) {
+ struct stat buf;
+ if (stat(path.c_str(), &buf) != 0) {
+ if (errno != ENOENT) {
+ PLOG(WARNING) << "Unable to stat " << path;
+ return ERR;
+ }
+ return NONE;
+ }
+ return S_ISDIR(buf.st_mode) ? IS_DIR : NOT_DIR;
+}
+
/*
* Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
- * of the way. If dirToDelete does not exist this function does nothing and returns true.
+ * of the way. If dirToDelete does not exist this function does nothing and returns true. If
+ * dirToDelete is not a directory or cannot be accessed this method returns false.
*
* During deletion, this function first renames the directory to a temporary name. If the temporary
* directory cannot be created, or the directory cannot be renamed, false is returned. After the
@@ -114,23 +188,18 @@
*/
static bool deleteDir(const std::string& dirToDelete) {
// Check whether the dir exists.
- struct stat buf;
- if (stat(dirToDelete.c_str(), &buf) == 0) {
- if (!S_ISDIR(buf.st_mode)) {
- LOG(WARNING) << dirToDelete << " is not a directory";
+ int pathStatus = checkPath(dirToDelete);
+ if (pathStatus == NONE) {
+ LOG(INFO) << "Path " << dirToDelete << " does not exist";
+ return true;
+ }
+ if (pathStatus != IS_DIR) {
+ LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
return false;
- }
- } else {
- if (errno == ENOENT) {
- PLOG(INFO) << "Directory does not exist: " << dirToDelete;
- return true;
- } else {
- PLOG(WARNING) << "Unable to stat " << dirToDelete;
- return false;
- }
}
// First, rename dirToDelete.
+
std::string tempDirNameTemplate = getParentDir(dirToDelete);
tempDirNameTemplate += "/tempXXXXXX";
@@ -142,7 +211,7 @@
return false;
}
- // Rename dirToDelete to tempDirName.
+ // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
if (rc == -1) {
PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
@@ -151,6 +220,7 @@
}
// Recursively delete contents of tempDirName.
+
rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
if (rc == -1) {
@@ -160,9 +230,36 @@
}
/*
+ * Deletes the ConfigInstaller metadata directory.
+ * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
+ */
+static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
+ // Delete the update metadata
+ std::string dataUpdatesDirName(dataZoneInfoDir);
+ dataUpdatesDirName += "/updates";
+ LOG(INFO) << "Removing: " << dataUpdatesDirName;
+ bool deleted = deleteDir(dataUpdatesDirName);
+ if (!deleted) {
+ LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
+ << " was not successful";
+ }
+}
+
+/*
+ * Deletes the timezone update bundle directory.
+ */
+static void deleteUpdateBundleDir(std::string& bundleDirName) {
+ LOG(INFO) << "Removing: " << bundleDirName;
+ bool deleted = deleteDir(bundleDirName);
+ if (!deleted) {
+ LOG(WARNING) << "Deletion of bundle dir " << bundleDirName << " was not successful";
+ }
+}
+
+/*
* After a platform update it is likely that timezone data found on the system partition will be
* newer than the version found in the data partition. This tool detects this case and removes the
- * version in /data along with any update metadata.
+ * version in /data.
*
* Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
* paths for the metadata and current timezone data must match.
@@ -175,62 +272,103 @@
int main(int argc, char* argv[]) {
if (argc != 3) {
usage();
+ return 1;
}
const char* systemZoneInfoDir = argv[1];
const char* dataZoneInfoDir = argv[2];
+ // Check the bundle directory exists. If it does not, exit quickly: nothing to do.
std::string dataCurrentDirName(dataZoneInfoDir);
dataCurrentDirName += "/current";
- std::string dataTzDataFileName(dataCurrentDirName);
- dataTzDataFileName += TZDATA_FILENAME;
-
- std::vector<char> dataTzDataHeader;
- dataTzDataHeader.reserve(TZ_HEADER_LENGTH);
-
- bool dataFileExists = readHeader(dataTzDataFileName, dataTzDataHeader.data(), TZ_HEADER_LENGTH);
- if (!dataFileExists) {
- LOG(INFO) << "tzdata file " << dataTzDataFileName << " does not exist. No action required.";
+ int dataCurrentDirStatus = checkPath(dataCurrentDirName);
+ if (dataCurrentDirStatus == NONE) {
+ LOG(INFO) << "timezone bundle dir " << dataCurrentDirName
+ << " does not exist. No action required.";
return 0;
}
- checkValidHeader(dataTzDataFileName, dataTzDataHeader.data());
+ // If the bundle directory path is not a directory or we can't stat() the path, exit with a
+ // warning: either there's a problem accessing storage or the world is not as it should be;
+ // nothing to do.
+ if (dataCurrentDirStatus != IS_DIR) {
+ LOG(WARNING) << "Current bundle dir " << dataCurrentDirName
+ << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
+ return 2;
+ }
+ // Check the installed bundle version.
+ std::string bundleVersionFileName(dataCurrentDirName);
+ bundleVersionFileName += BUNDLE_VERSION_FILENAME;
+ std::vector<char> bundleVersion;
+ bundleVersion.reserve(BUNDLE_VERSION_LENGTH);
+ bool bundleVersionReadOk =
+ readBytes(bundleVersionFileName, bundleVersion.data(), BUNDLE_VERSION_LENGTH);
+ if (!bundleVersionReadOk) {
+ LOG(WARNING) << "bundle version file " << bundleVersionFileName
+ << " does not exist or is too short. Deleting bundle dir.";
+ // Implies the contents of the data partition is corrupt in some way. Try to clean up.
+ deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+ deleteUpdateBundleDir(dataCurrentDirName);
+ return 3;
+ }
+
+ if (!checkValidBundleVersion(bundleVersion.data())) {
+ LOG(WARNING) << "bundle version file " << bundleVersionFileName
+ << " is not valid. Deleting bundle dir.";
+ // Implies the contents of the data partition is corrupt in some way. Try to clean up.
+ deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+ deleteUpdateBundleDir(dataCurrentDirName);
+ return 4;
+ }
+
+ // Check the first 3 bytes of the bundleVersionHeader: these are the major version (e.g. 001).
+ // It must match exactly to be ok. The minor version is currently ignored.
+ if (strncmp(&bundleVersion[0], REQUIRED_BUNDLE_VERSION, REQUIRED_BUNDLE_VERSION_LEN) != 0) {
+ LOG(INFO) << "bundle version file " << bundleVersionFileName
+ << " is not the required version " << REQUIRED_BUNDLE_VERSION
+ << ". Deleting bundle dir.";
+ // This shouldn't happen with 001, but it in future, this will imply there has been an OTA
+ // and the installed bundle is not compatible with the new version of Android. Remove the
+ // installed bundle.
+ deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+ deleteUpdateBundleDir(dataCurrentDirName);
+ return 5;
+ }
+
+ // Read the system rules version out of the /system tzdata file.
std::string systemTzDataFileName(systemZoneInfoDir);
systemTzDataFileName += TZDATA_FILENAME;
std::vector<char> systemTzDataHeader;
systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
bool systemFileExists =
- readHeader(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
+ readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
if (!systemFileExists) {
- LOG(FATAL) << systemTzDataFileName << " does not exist or could not be opened";
+ // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
+ LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
+ return 6;
}
- checkValidHeader(systemTzDataFileName, systemTzDataHeader.data());
-
- if (strncmp(&systemTzDataHeader[0], &dataTzDataHeader[0], TZ_HEADER_LENGTH) < 0) {
- LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the newer than "
- << systemTzDataFileName << ". No action required.";
- } else {
- // We have detected the case this tool is intended to prevent. Go fix it.
- LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the same as or older than "
- << systemTzDataFileName << "; fixing...";
-
- // Delete the update metadata
- std::string dataUpdatesDirName(dataZoneInfoDir);
- dataUpdatesDirName += "/updates";
- LOG(INFO) << "Removing: " << dataUpdatesDirName;
- bool deleted = deleteDir(dataUpdatesDirName);
- if (!deleted) {
- LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
- << " was not successful";
- }
-
- // Delete the TZ data
- LOG(INFO) << "Removing: " << dataCurrentDirName;
- deleted = deleteDir(dataCurrentDirName);
- if (!deleted) {
- LOG(WARNING) << "Deletion of tzdata " << dataCurrentDirName << " was not successful";
- }
+ if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
+ // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
+ LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
+ return 7;
}
+ // Compare the bundle rules version against the system rules version.
+ if (strncmp(
+ &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
+ &bundleVersion[BUNDLE_VERSION_RULES_IDX],
+ RULES_VERSION_LEN) <= 0) {
+ LOG(INFO) << "Found an installed bundle but it is valid. No action taken.";
+ // Implies there is an installed update, but it is good.
+ return 0;
+ }
+
+ // Implies there has been an OTA and the system version of the timezone rules is now newer
+ // than the version installed in /data. Remove the installed bundle.
+ LOG(INFO) << "timezone bundle in " << dataCurrentDirName << " is older than data in "
+ << systemTzDataFileName << "; fixing...";
+
+ deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+ deleteUpdateBundleDir(dataCurrentDirName);
return 0;
}